Skip to content

Commit

Permalink
preprocess group ACLs
Browse files Browse the repository at this point in the history
Close #40, eliminates one potential interstitial in #34
  • Loading branch information
battis committed Aug 29, 2024
1 parent 6345a90 commit b780f2f
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 52 deletions.
39 changes: 30 additions & 9 deletions apps/indexer/bin/groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ const options = {

const spinner = cli.spinner();

let groups: Var.Groups = [];
const pattern = new RegExp(
permissionsRegex || process.env.PERMISSIONS_REGEX || '.*'
);
let groups: Var.Groups = {};
let nextPageToken: string | undefined = undefined;
do {
const page = JSON.parse(
Expand All @@ -41,16 +44,34 @@ const options = {
}`
).stdout
);
groups.push(...page.groups);
for (const group of page.groups) {
if (pattern.test(group.groupKey.id)) {
groups[group.groupKey.id] = group;
}
}
nextPageToken = page.nextPageToken;
} while (nextPageToken);
const pattern = new RegExp(
permissionsRegex || process.env.PERMISSIONS_REGEX || '.*'
);

fs.writeFileSync(
filePath,
JSON.stringify(groups.filter((group) => pattern.test(group.groupKey.id)))
);
for (const group in groups) {
groups[group].members = [];
nextPageToken = undefined;
do {
const page = JSON.parse(
cli.shell.exec(
`gcloud identity groups memberships list --group-email=${group} --project=${
process.env.PROJECT
} --format=json --quiet${
nextPageToken ? ` --page-token="${nextPageToken}"` : ''
}`
).stdout
);
groups[group].members.push(
...page.map((member) => member.preferredMemberKey.id)
);
nextPageToken = page.nextPageToken;
} while (nextPageToken);
}

fs.writeFileSync(filePath, JSON.stringify(groups));
spinner.succeed(`List saved to ${cli.colors.url(filePath)}`);
})();
63 changes: 22 additions & 41 deletions apps/router/src/Services/ACL.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import Auth from './Auth';
import Logger from './Logger';
import { drive_v3 } from '@googleapis/drive';
import Var from '@groton/knowledgebase.config';
import { Request, Response } from 'express';
Expand All @@ -13,52 +11,35 @@ export default class ACL {

// FIXME this needs to have an expiration date and get refreshed regularly
public async prepare() {
if (!this.req.session.groups) {
const userGroups: string[] = [];
for (const group of this.groups) {
try {
if (
(
await (
await fetch(
`https://cloudidentity.googleapis.com/v1/${group.name}/memberships:checkTransitiveMembership?query=member_key_id == '${this.req.session.userInfo?.email}'`,
{
headers: {
Authorization: `Bearer ${
(
await Auth.authClient.getAccessToken()
).token
}`
}
}
)
).json()
).hasMembership
) {
userGroups.push(group.groupKey.id);
}
} catch (error) {
Logger.error(this.req.originalUrl, {
function: 'ACL.prepare()',
error
});
this.res.status((error as any).code || 418);
return this;
}

private groupContains(group: string, email: string): boolean {
if (group in this.groups) {
if (this.groups[group].members?.includes(email)) {
return true;
}
for (const member of this.groups[group].members || []) {
if (member in this.groups && this.groupContains(member, email)) {
return true;
}
}
this.req.session.groups = userGroups;
}
return this;
return false;
}

public hasAccess(permissions: drive_v3.Schema$Permission[] = []) {
if (!this.req.session.groups) {
throw new Error('ACL improperly prepared');
}
return permissions?.reduce((access: boolean, permission) => {
if (this.req.session.groups!.includes(permission.emailAddress!)) {
for (const permission of permissions) {
if (
permission.emailAddress &&
this.groupContains(
permission.emailAddress,
this.req.session.userInfo.email
)
) {
return true;
}
return access;
}, false);
}
return false;
}
}
1 change: 0 additions & 1 deletion apps/router/src/Services/Auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ export default class Auth {
scope: [
// TODO examine these scopes more carefully
'https://www.googleapis.com/auth/devstorage.read_only',
'https://www.googleapis.com/auth/cloud-identity.groups.readonly',
'openid',
'profile',
'email'
Expand Down
3 changes: 2 additions & 1 deletion packages/config/src/Groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ export type Group = {
displayName: string;
groupKey: { id: EmailString };
name: string; // groups/*
members?: EmailString[];
};

type Groups = Group[];
type Groups = Record<EmailString, Group>;

export default Groups;

0 comments on commit b780f2f

Please sign in to comment.