Skip to content
This repository has been archived by the owner on Apr 2, 2024. It is now read-only.

Commit

Permalink
Make it easier to look up principals (#4)
Browse files Browse the repository at this point in the history
* Check permissions for `/principal/find` against the user to be
returned.
* Always allow a user to look up their own UUID.
* Default `/principal/find` to the authenticated user.

Other minor updates: 
* Local build infrastructure.
* Updated version of `@amrc-factoryplus/utilities`, including better
Cache-Control behaviour.
* Include git revision in `/ping`.
* Display object classes and deleted objects in the editor.
  • Loading branch information
AlexGodbehere authored Jul 5, 2023
2 parents 860d134 + 770793c commit 6e0453b
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 25 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
# Environment
.env

# Local config
config.mk

# Secrets
/keytabs/
/volumes/
Expand Down
17 changes: 14 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
# syntax=docker/dockerfile:1

ARG utility_prefix=ghcr.io/amrc-factoryplus/utilities
ARG utility_ver=v1.0.6
ARG utility_ver=v1.0.8

FROM ${utility_prefix}-build:${utility_ver} AS build

# Install the node application on the build container where we can
# compile the native modules.
RUN install -d -o node -g node /home/node/app
USER root
RUN <<'SHELL'
install -d -o node -g node /home/node/app
apk add git
SHELL
WORKDIR /home/node/app
USER node
COPY package*.json ./
RUN npm install --save=false
COPY . .
COPY --chown=node . .
RUN <<'SHELL'
git describe --tags --dirty \
| sed -re's/-[0-9]+-/-/;s/(.*)/export const GIT_VERSION="\1";/' \
> lib/git-version.js
SHELL

FROM ${utility_prefix}-run:${utility_ver}

Expand Down
48 changes: 48 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# On Windows try https://frippery.org/busybox/

-include config.mk

pkgver!=node -e 'console.log(JSON.parse(fs.readFileSync("package.json")).version)'

version?=${pkgver}
suffix?=
registry?=ghcr.io/amrc-factoryplus
repo?=acs-auth

tag=${registry}/${repo}:${version}${suffix}

all: build push

.PHONY: all build push check-committed

check-committed:
[ -z "$$(git status --porcelain)" ] || (git status; exit 1)

build: check-committed
docker build -t "${tag}" .

push:
docker push "${tag}"

ifdef deployment

.PHONY: deploy restart logs

kdeploy= deploy/"${deployment}"

deploy: all restart logs

restart:
kubectl rollout restart ${kdeploy}
kubectl rollout status ${kdeploy}
sleep 2

logs:
kubectl logs -f ${kdeploy}

else

deploy:
: Set $${deployment} for automatic k8s deployment

endif
13 changes: 11 additions & 2 deletions bin/authn.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
* Copyright 2022 AMRC
*/

import { WebAPI, pkgVersion } from "@amrc-factoryplus/utilities";
import { WebAPI, UUIDs } from "@amrc-factoryplus/utilities";

import AuthN from "../lib/authn.js";
import AuthZ from "../lib/authz.js";
import Editor from "../lib/editor.js";

const Version = pkgVersion(import.meta);
import { GIT_VERSION } from "../lib/git-version.js";

/* This is the F+ service spec version */
const Version = "1.0.0";

const authn = await new AuthN({ }).init();
const authz = await new AuthZ({
Expand All @@ -31,6 +34,12 @@ const editor = await new Editor({
const api = await new WebAPI({
ping: {
version: Version,
service: UUIDs.Service.Authentication,
software: {
vendor: "AMRC",
application: "acs-auth",
revision: GIT_VERSION,
},
},
realm: process.env.REALM,
hostname: process.env.HOSTNAME,
Expand Down
16 changes: 14 additions & 2 deletions editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const html = htm.bind(h);

const Uuid = {
General_Info: "64a8bfa9-7772-45c4-9d1a-9e6290690957",
Registration: "cb40bed5-49ad-4443-a7f5-08c75009da8f",
};

let Services;
Expand Down Expand Up @@ -83,9 +84,20 @@ async function fetch_json (service, path, method="GET", body=null) {
return json;
}

async function get_name (obj) {
async function _get_name (obj) {
const gi = await fetch_json("configdb", `v1/app/${Uuid.General_Info}/object/${obj}`);
return gi ? gi.name : html`<i>NO NAME</i>`;
return gi
? gi.deleted
? html`<s>${gi.name}</s>`
: gi.name
: html`<i>NO NAME</i>`;
}

async function get_name (obj) {
const reg = await fetch_json("configdb", `v1/app/${Uuid.Registration}/object/${obj}`);
const name = await _get_name(obj);
const klass = reg ? await _get_name(reg.class) : html`<i>NO CLASS</i>`;
return html`${name} <small>(${klass})</small>`;
}

function sort_acl (list) {
Expand Down
33 changes: 18 additions & 15 deletions lib/authz.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,17 @@ export default class AuthZ {
if (!valid_uuid(uuid))
return res.status(400).end();

const ok = await this.model.check_acl(
req.auth, Perm.Manage_Krb, uuid, true);
const ids = await this.model.principal_get(uuid);

/* We can return 403 here as long as we don't return 404 until
* we've checked the permissions. */
const ok = req.auth == ids.kerberos
|| await this.model.check_acl(req.auth, Perm.Read_Krb, uuid, true);
if (!ok) return res.status(403).end();

const krb = await this.model.principal_get(uuid);
if (krb == null)
if (ids == null)
return res.status(404).end();
return res.status(200).json(krb);
return res.status(200).json(ids);
}

async principal_delete(req, res) {
Expand All @@ -181,22 +184,22 @@ export default class AuthZ {
/* This endpoint may change in future to allow searching for
* principals by other criteria, e.g. Node address. */
async principal_find(req, res) {
const {kerberos} = req.query;
const kerberos = req.query?.kerberos ?? req.auth;
if (!valid_krb(kerberos))
return res.status(400).end();

/* XXX I'm not sure this is right: this means any client that
* wants to resolve Kerberos principals needs to be able to read
* them all. I would like to check ACLs against the resolved
* UUID, but then we have the problem of when to return 404,
* when 403, etc. */
const ok = await this.model.check_acl(
req.auth, Perm.Read_Krb, UUIDs.Null, false);
if (!ok) return res.status(403).end();

const uuid = await this.model.principal_find_by_krb(kerberos);
if (uuid == null)
return res.status(404).end();

/* We have to check permissions against the returned UUID. This
* means that we must return 404 instead of 403 if the check
* fails. Principals are always allowed to look up their own
* information. */
const ok = req.auth == kerberos
|| await this.model.check_acl(req.auth, Perm.Read_Krb, uuid, true)
if (!ok) return res.status(404).end();

return res.status(200).json(uuid);
}

Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "acs-auth",
"version": "1.0.0",
"version": "1.0.1",
"description": "The Authorisation component of the AMRC Connectivity Stack",
"type": "module",
"main": "index.js",
Expand All @@ -21,10 +21,10 @@
"author": "AMRC",
"license": "MIT",
"dependencies": {
"@amrc-factoryplus/utilities": "1.0.6",
"@amrc-factoryplus/utilities": "^1.0.8",
"express": "^4.18.1",
"express-basic-auth": "^1.2.1",
"http-errors": "^2.0.0",
"timers-promises": "^1.0.1"
}
}
}

0 comments on commit 6e0453b

Please sign in to comment.