11import axios from 'axios' ;
22import config from 'config' ;
33
4- import { Action } from '../utils/enums/application.ts' ;
5- import { Problem , uuidValidateV4 } from '../utils/index.ts' ;
4+ import { Action , GroupName } from '../utils/enums/application.ts' ;
5+ import { getCurrentSubject , Problem , uuidValidateV4 } from '../utils/index.ts' ;
6+ import { getSubjectGroups } from './yars.ts' ;
67
78import type { AxiosInstance , AxiosRequestConfig } from 'axios' ;
9+ import type { PrismaTransactionClient } from '../db/dataConnection.ts' ;
10+ import type { CurrentContext } from '../types/stuff' ;
811
912/**
1013 * Returns an Axios instance for the COMS API
14+ * Injects pseudo service account basic auth if no other auth provided
1115 * @param options Axios request config options
1216 * @returns An axios instance
1317 */
1418function comsAxios ( options : AxiosRequestConfig = { } ) : AxiosInstance {
19+ // Inject pseudo service account if no other auth provided
20+ if ( ! options . headers ?. Authorization ) {
21+ const ak = config . get < string > ( 'server.objectStorage.accessKeyId' ) ;
22+ const sak = config . get < string > ( 'server.objectStorage.secretAccessKey' ) ;
23+ const b64 = Buffer . from ( ak + ':' + sak ) . toString ( 'base64' ) ;
24+
25+ if ( ! options . headers ) options . headers = { } ;
26+ options . headers . Authorization = `basic ${ b64 } ` ;
27+ options . headers [ 'x-amz-bucket' ] = config . get ( 'server.objectStorage.bucket' ) ;
28+ options . headers [ 'x-amz-endpoint' ] = config . get ( 'server.objectStorage.endpoint' ) ;
29+ }
30+
1531 // Create axios instance
1632 const instance = axios . create ( {
1733 baseURL : config . get ( 'frontend.coms.apiPath' ) ,
@@ -23,27 +39,20 @@ function comsAxios(options: AxiosRequestConfig = {}): AxiosInstance {
2339}
2440
2541/**
26- * Creates a bucket record. Bucket should exist in S3. If the set of bucket, endpoint and key match
27- * an existing record, the user will be added to that existing bucket with the provided permissions
28- * instead of generating a new bucket record.
29- * This endpoint can be used to grant the current user permission to upload to a new or existing bucket.
30- * @param bearerToken The bearer token of the authorized user
31- * @param permissions An array of permissions to grant the user
42+ * Obtain a bucket record. This actually utilizes the COMS create bucket endpoint as its the only way to
43+ * obtain full bucket information using their pseudo service account access.
3244 * @returns The created bucket data
3345 */
34- export const createBucket = async ( bearerToken : string , permissions : Action [ ] ) => {
35- const { data } = await comsAxios ( {
36- headers : { Authorization : `Bearer ${ bearerToken } ` }
37- } ) . put ( '/bucket' , {
46+ export const getBucket = async ( ) => {
47+ const { status, headers, data } = await comsAxios ( ) . put ( '/bucket' , {
3848 accessKeyId : config . get ( 'server.objectStorage.accessKeyId' ) ,
3949 bucket : config . get ( 'server.objectStorage.bucket' ) ,
4050 bucketName : 'PCNS' ,
4151 endpoint : config . get ( 'server.objectStorage.endpoint' ) ,
4252 secretAccessKey : config . get ( 'server.objectStorage.secretAccessKey' ) ,
43- key : config . get ( 'server.objectStorage.key' ) ,
44- permCodes : permissions
53+ key : config . get ( 'server.objectStorage.key' )
4554 } ) ;
46- return data ;
55+ return { status , headers , data } ;
4756} ;
4857
4958/**
@@ -62,3 +71,71 @@ export const getObject = async (bearerToken: string, objectId: string) => {
6271 } ) . get ( `/object/${ objectId } ` ) ;
6372 return { status, headers, data } ;
6473} ;
74+
75+ /**
76+ * Obtain the current user information in COMS
77+ * @param currentContext The current context of the Express request
78+ * @returns The COMS response
79+ */
80+ export const searchUser = async ( currentContext : CurrentContext ) => {
81+ const { status, headers, data } = await comsAxios ( {
82+ headers : { Authorization : `Bearer ${ currentContext . bearerToken } ` }
83+ // Update to use username (sub) if COMS ever opens that up
84+ } ) . get ( '/user' , { params : { identityId : getCurrentSubject ( currentContext ) . split ( '@' ) [ 0 ] . toUpperCase ( ) } } ) ;
85+ return { status, headers, data } ;
86+ } ;
87+
88+ /**
89+ * Assigns COMS permissions to the current user based on their current groups
90+ * @param tx Prisma transaction client
91+ * @param currentContext The current context of the Express request
92+ */
93+ export const assignPermissions = async ( tx : PrismaTransactionClient , currentContext : CurrentContext ) => {
94+ const sub = currentContext . tokenPayload ?. sub ;
95+
96+ if ( ! sub ) {
97+ throw new Problem ( 403 , {
98+ detail : 'Unable to obtain token sub'
99+ } ) ;
100+ }
101+
102+ const groups = await getSubjectGroups ( tx , sub ) ;
103+ const groupNames = new Set ( groups . map ( ( x ) => x . name ) ) ;
104+
105+ const comsPermsMap = new Map < GroupName , Action [ ] > ( [
106+ [ GroupName . PROPONENT , [ Action . CREATE ] ] ,
107+ [ GroupName . NAVIGATOR_READ_ONLY , [ Action . READ ] ] ,
108+ [ GroupName . NAVIGATOR , [ Action . CREATE , Action . READ , Action . UPDATE , Action . DELETE ] ] ,
109+ [ GroupName . SUPERVISOR , [ Action . CREATE , Action . READ , Action . UPDATE , Action . DELETE ] ] ,
110+ [ GroupName . ADMIN , [ Action . CREATE , Action . READ , Action . UPDATE , Action . DELETE ] ] ,
111+ [ GroupName . DEVELOPER , [ Action . CREATE , Action . READ , Action . UPDATE , Action . DELETE ] ]
112+ ] ) ;
113+
114+ const actions = new Set < Action > ( [ ...groupNames ] . flatMap ( ( groupName ) => comsPermsMap . get ( groupName ) ?? [ ] ) ) ;
115+
116+ if ( actions && currentContext ) {
117+ try {
118+ const user = await searchUser ( currentContext ) ;
119+ const bucket = await getBucket ( ) ;
120+
121+ const { userId } = user . data [ 0 ] ;
122+ const { bucketId } = bucket . data ;
123+
124+ if ( ! userId || ! bucketId ) {
125+ throw new Error ( 'Unable to obtain userId or bucketId' ) ;
126+ }
127+
128+ const permissions = [ ...actions ] . flatMap ( ( action ) => ( { permCode : action , userId } ) ) ;
129+
130+ await comsAxios ( ) . put ( `/permission/bucket/${ bucketId } ` , permissions ) ;
131+ } catch ( e ) {
132+ if ( axios . isAxiosError ( e ) ) {
133+ const detail = e . response ?. data . detail ;
134+ const status = e . response ? e . response . status : 500 ;
135+ throw new Problem ( status , { detail } , { extra : { comsError : e . response ?. data } } ) ;
136+ } else {
137+ throw new Problem ( 500 , { detail : 'Server Error' } ) ;
138+ }
139+ }
140+ }
141+ } ;
0 commit comments