diff --git a/docs/ENTITY_RELATIONSHIP_DIAGRAM.md b/docs/ENTITY_RELATIONSHIP_DIAGRAM.md index 0e25a99a..ae4932eb 100644 --- a/docs/ENTITY_RELATIONSHIP_DIAGRAM.md +++ b/docs/ENTITY_RELATIONSHIP_DIAGRAM.md @@ -132,6 +132,9 @@ erDiagram Proposal }o--|| User : "is created by" ProposalVersion }o--|| User : "is created by" BulkUpload }o--|| User : "is created by" + User }o--o{ Changemaker : "is granted permissions for" + User }o--o{ Funder : "is granted permissions for" + User }o--o{ DataProvider : "is granted permissions for" ``` ## Narrative diff --git a/src/__tests__/users.int.test.ts b/src/__tests__/users.int.test.ts index 624d2401..f296eebe 100644 --- a/src/__tests__/users.int.test.ts +++ b/src/__tests__/users.int.test.ts @@ -1,13 +1,27 @@ import request from 'supertest'; import { v4 as uuidv4 } from 'uuid'; import { app } from '../app'; -import { createUser, loadSystemUser, loadTableMetrics } from '../database'; +import { + createChangemaker, + createOrUpdateDataProvider, + createOrUpdateFunder, + createOrUpdateUserChangemakerPermission, + createOrUpdateUserDataProviderPermission, + createOrUpdateUserFunderPermission, + createUser, + loadSystemUser, + loadTableMetrics, +} from '../database'; import { expectTimestamp, loadTestUser } from '../test/utils'; import { mockJwt as authHeader, mockJwtWithAdminRole as authHeaderWithAdminRole, } from '../test/mockJwt'; -import { keycloakUserIdToString, stringToKeycloakUserId } from '../types'; +import { + keycloakUserIdToString, + stringToKeycloakUserId, + Permission, +} from '../types'; const createAdditionalTestUser = async () => createUser({ @@ -33,7 +47,78 @@ describe('/users', () => { .expect(200); expect(response.body).toEqual({ total: userCount, - entries: [testUser], + entries: [ + { + keycloakUserId: testUser.keycloakUserId, + permissions: { + changemaker: {}, + dataProvider: {}, + funder: {}, + }, + createdAt: expectTimestamp, + }, + ], + }); + }); + + it('returns the permissions associated with a user', async () => { + const systemUser = await loadSystemUser(); + const testUser = await loadTestUser(); + const dataProvider = await createOrUpdateDataProvider({ + name: 'Test Provider', + shortCode: 'testProvider', + }); + const funder = await createOrUpdateFunder({ + name: 'Test Funder', + shortCode: 'testFunder', + }); + const changemaker = await createChangemaker({ + name: 'Test Changemaker', + taxId: '12-3456789', + }); + await createOrUpdateUserDataProviderPermission({ + userKeycloakUserId: testUser.keycloakUserId, + permission: Permission.MANAGE, + dataProviderShortCode: dataProvider.shortCode, + createdBy: systemUser.keycloakUserId, + }); + await createOrUpdateUserFunderPermission({ + userKeycloakUserId: testUser.keycloakUserId, + permission: Permission.EDIT, + funderShortCode: funder.shortCode, + createdBy: systemUser.keycloakUserId, + }); + await createOrUpdateUserChangemakerPermission({ + userKeycloakUserId: testUser.keycloakUserId, + permission: Permission.VIEW, + changemakerId: changemaker.id, + createdBy: systemUser.keycloakUserId, + }); + const { count: userCount } = await loadTableMetrics('users'); + + const response = await request(app) + .get('/users') + .set(authHeader) + .expect(200); + expect(response.body).toEqual({ + total: userCount, + entries: [ + { + keycloakUserId: testUser.keycloakUserId, + permissions: { + changemaker: { + [changemaker.id]: [Permission.VIEW], + }, + dataProvider: { + testProvider: [Permission.MANAGE], + }, + funder: { + testFunder: [Permission.EDIT], + }, + }, + createdAt: expectTimestamp, + }, + ], }); }); @@ -99,22 +184,47 @@ describe('/users', () => { entries: [ { keycloakUserId: uuids[14], + permissions: { + changemaker: {}, + dataProvider: {}, + funder: {}, + }, createdAt: expectTimestamp, }, { keycloakUserId: uuids[13], + permissions: { + changemaker: {}, + dataProvider: {}, + funder: {}, + }, createdAt: expectTimestamp, }, { keycloakUserId: uuids[12], + permissions: { + changemaker: {}, + dataProvider: {}, + funder: {}, + }, createdAt: expectTimestamp, }, { keycloakUserId: uuids[11], + permissions: { + changemaker: {}, + dataProvider: {}, + funder: {}, + }, createdAt: expectTimestamp, }, { keycloakUserId: uuids[10], + permissions: { + changemaker: {}, + dataProvider: {}, + funder: {}, + }, createdAt: expectTimestamp, }, ], diff --git a/src/database/initialization/user_changemaker_permission_to_json.sql b/src/database/initialization/user_changemaker_permission_to_json.sql new file mode 100644 index 00000000..1dc66f7d --- /dev/null +++ b/src/database/initialization/user_changemaker_permission_to_json.sql @@ -0,0 +1,14 @@ +SELECT drop_function('user_changemaker_permission_to_json'); + +CREATE FUNCTION user_changemaker_permission_to_json(user_changemaker_permission user_changemaker_permissions) +RETURNS JSONB AS $$ +BEGIN + RETURN jsonb_build_object( + 'userKeycloakUserId', user_changemaker_permission.user_keycloak_user_id, + 'permission', user_changemaker_permission.permission, + 'changemakerId', user_changemaker_permission.changemaker_id, + 'createdBy', user_changemaker_permission.created_by, + 'createdAt', user_changemaker_permission.created_at + ); +END; +$$ LANGUAGE plpgsql; diff --git a/src/database/initialization/user_data_provider_permission_to_json.sql b/src/database/initialization/user_data_provider_permission_to_json.sql new file mode 100644 index 00000000..f64b5807 --- /dev/null +++ b/src/database/initialization/user_data_provider_permission_to_json.sql @@ -0,0 +1,14 @@ +SELECT drop_function('user_data_provider_permission_to_json'); + +CREATE FUNCTION user_data_provider_permission_to_json(user_data_provider_permission user_data_provider_permissions) +RETURNS JSONB AS $$ +BEGIN + RETURN jsonb_build_object( + 'userKeycloakUserId', user_data_provider_permission.user_keycloak_user_id, + 'permission', user_data_provider_permission.permission, + 'dataProviderShortCode', user_data_provider_permission.data_provider_short_code, + 'createdBy', user_data_provider_permission.created_by, + 'createdAt', user_data_provider_permission.created_at + ); +END; +$$ LANGUAGE plpgsql; diff --git a/src/database/initialization/user_funder_permission_to_json.sql b/src/database/initialization/user_funder_permission_to_json.sql new file mode 100644 index 00000000..bca0dad0 --- /dev/null +++ b/src/database/initialization/user_funder_permission_to_json.sql @@ -0,0 +1,14 @@ +SELECT drop_function('user_funder_permission_to_json'); + +CREATE FUNCTION user_funder_permission_to_json(user_funder_permission user_funder_permissions) +RETURNS JSONB AS $$ +BEGIN + RETURN jsonb_build_object( + 'userKeycloakUserId', user_funder_permission.user_keycloak_user_id, + 'permission', user_funder_permission.permission, + 'funderShortCode', user_funder_permission.funder_short_code, + 'createdBy', user_funder_permission.created_by, + 'createdAt', user_funder_permission.created_at + ); +END; +$$ LANGUAGE plpgsql; diff --git a/src/database/initialization/user_to_json.sql b/src/database/initialization/user_to_json.sql index 23948c44..59fc0e2a 100644 --- a/src/database/initialization/user_to_json.sql +++ b/src/database/initialization/user_to_json.sql @@ -2,9 +2,56 @@ SELECT drop_function('user_to_json'); CREATE FUNCTION user_to_json("user" users) RETURNS JSONB AS $$ +DECLARE + permissions_json JSONB := NULL::JSONB; + user_changemaker_permissions_json JSONB := NULL::JSONB; + user_funder_permissions_json JSONB := NULL::JSONB; + user_data_provider_permissions_json JSONB := NULL::JSONB; BEGIN + user_changemaker_permissions_json := ( + SELECT jsonb_object_agg( + aggregated_user_changemaker_permissions.changemaker_id, aggregated_user_changemaker_permissions.permissions + ) + FROM ( + SELECT user_changemaker_permissions.changemaker_id, jsonb_agg(user_changemaker_permissions.permission) AS permissions + FROM user_changemaker_permissions + WHERE user_changemaker_permissions.user_keycloak_user_id = "user".keycloak_user_id + GROUP BY user_changemaker_permissions.changemaker_id + ) AS aggregated_user_changemaker_permissions + ); + + user_data_provider_permissions_json := ( + SELECT jsonb_object_agg( + aggregated_user_data_provider_permissions.data_provider_short_code, aggregated_user_data_provider_permissions.permissions + ) + FROM ( + SELECT user_data_provider_permissions.data_provider_short_code, jsonb_agg(user_data_provider_permissions.permission) AS permissions + FROM user_data_provider_permissions + WHERE user_data_provider_permissions.user_keycloak_user_id = "user".keycloak_user_id + GROUP BY user_data_provider_permissions.data_provider_short_code + ) AS aggregated_user_data_provider_permissions ); + + user_funder_permissions_json := ( + SELECT jsonb_object_agg( + aggregated_user_funder_permissions.funder_short_code, aggregated_user_funder_permissions.permissions + ) + FROM ( + SELECT user_funder_permissions.funder_short_code, jsonb_agg(user_funder_permissions.permission) AS permissions + FROM user_funder_permissions + WHERE user_funder_permissions.user_keycloak_user_id = "user".keycloak_user_id + GROUP BY user_funder_permissions.funder_short_code + ) AS aggregated_user_funder_permissions + ); + + permissions_json := jsonb_build_object( + 'changemaker', COALESCE(user_changemaker_permissions_json, '{}'), + 'dataProvider', COALESCE(user_data_provider_permissions_json, '{}'), + 'funder', COALESCE(user_funder_permissions_json, '{}') + ); + RETURN jsonb_build_object( 'keycloakUserId', "user".keycloak_user_id, + 'permissions', permissions_json, 'createdAt', "user".created_at ); END; diff --git a/src/database/migrations/0040-create-permission-tables.sql b/src/database/migrations/0040-create-permission-tables.sql new file mode 100644 index 00000000..26f2172c --- /dev/null +++ b/src/database/migrations/0040-create-permission-tables.sql @@ -0,0 +1,32 @@ +CREATE TYPE permission_t AS ENUM ( + 'manage', + 'edit', + 'view' +); + +CREATE TABLE user_changemaker_permissions ( + user_keycloak_user_id UUID NOT NULL REFERENCES users(keycloak_user_id) ON DELETE CASCADE, + permission permission_t NOT NULL, + changemaker_id INT NOT NULL REFERENCES changemakers(id) ON DELETE CASCADE, + created_by UUID NOT NULL REFERENCES users(keycloak_user_id) ON DELETE CASCADE, + created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), + PRIMARY KEY (user_keycloak_user_id, permission, changemaker_id) +); + +CREATE TABLE user_funder_permissions ( + user_keycloak_user_id UUID NOT NULL REFERENCES users(keycloak_user_id) ON DELETE CASCADE, + permission permission_t NOT NULL, + funder_short_code short_code_t NOT NULL REFERENCES funders(short_code) ON DELETE CASCADE, + created_by UUID NOT NULL REFERENCES users(keycloak_user_id) ON DELETE CASCADE, + created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), + PRIMARY KEY (user_keycloak_user_id, permission, funder_short_code) +); + +CREATE TABLE user_data_provider_permissions ( + user_keycloak_user_id UUID NOT NULL REFERENCES users(keycloak_user_id) ON DELETE CASCADE, + permission permission_t NOT NULL, + data_provider_short_code short_code_t NOT NULL REFERENCES data_providers(short_code) ON DELETE CASCADE, + created_by UUID NOT NULL REFERENCES users(keycloak_user_id) ON DELETE CASCADE, + created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), + PRIMARY KEY (user_keycloak_user_id, permission, data_provider_short_code) +); diff --git a/src/database/operations/index.ts b/src/database/operations/index.ts index f7d0c1cf..4dd103dc 100644 --- a/src/database/operations/index.ts +++ b/src/database/operations/index.ts @@ -13,4 +13,7 @@ export * from './proposalFieldValues'; export * from './proposals'; export * from './proposalVersions'; export * from './sources'; +export * from './userChangemakerPermissions'; +export * from './userDataProviderPermissions'; +export * from './userFunderPermissions'; export * from './users'; diff --git a/src/database/operations/userChangemakerPermissions/createOrUpdateUserChangemakerPermission.ts b/src/database/operations/userChangemakerPermissions/createOrUpdateUserChangemakerPermission.ts new file mode 100644 index 00000000..d3ac53f7 --- /dev/null +++ b/src/database/operations/userChangemakerPermissions/createOrUpdateUserChangemakerPermission.ts @@ -0,0 +1,32 @@ +import { db } from '../../db'; +import type { + InternallyWritableUserChangemakerPermission, + JsonResultSet, + UserChangemakerPermission, +} from '../../../types'; + +const createOrUpdateUserChangemakerPermission = async ( + createValues: InternallyWritableUserChangemakerPermission, +): Promise => { + const { userKeycloakUserId, changemakerId, permission, createdBy } = + createValues; + const result = await db.sql>( + 'userChangemakerPermissions.insertOrUpdateOne', + { + userKeycloakUserId, + permission, + changemakerId, + createdBy, + }, + ); + + const { object } = result.rows[0] ?? {}; + if (object === undefined) { + throw new Error( + 'The entity creation did not appear to fail, but no data was returned by the operation.', + ); + } + return object; +}; + +export { createOrUpdateUserChangemakerPermission }; diff --git a/src/database/operations/userChangemakerPermissions/index.ts b/src/database/operations/userChangemakerPermissions/index.ts new file mode 100644 index 00000000..5bce400c --- /dev/null +++ b/src/database/operations/userChangemakerPermissions/index.ts @@ -0,0 +1 @@ +export * from './createOrUpdateUserChangemakerPermission'; diff --git a/src/database/operations/userDataProviderPermissions/createOrUpdateUserDataProviderPermission.ts b/src/database/operations/userDataProviderPermissions/createOrUpdateUserDataProviderPermission.ts new file mode 100644 index 00000000..10e00d12 --- /dev/null +++ b/src/database/operations/userDataProviderPermissions/createOrUpdateUserDataProviderPermission.ts @@ -0,0 +1,32 @@ +import { db } from '../../db'; +import type { + UserDataProviderPermission, + InternallyWritableUserDataProviderPermission, + JsonResultSet, +} from '../../../types'; + +const createOrUpdateUserDataProviderPermission = async ( + createValues: InternallyWritableUserDataProviderPermission, +): Promise => { + const { userKeycloakUserId, dataProviderShortCode, permission, createdBy } = + createValues; + const result = await db.sql>( + 'userDataProviderPermissions.insertOrUpdateOne', + { + userKeycloakUserId, + permission, + dataProviderShortCode, + createdBy, + }, + ); + + const { object } = result.rows[0] ?? {}; + if (object === undefined) { + throw new Error( + 'The entity creation did not appear to fail, but no data was returned by the operation.', + ); + } + return object; +}; + +export { createOrUpdateUserDataProviderPermission }; diff --git a/src/database/operations/userDataProviderPermissions/index.ts b/src/database/operations/userDataProviderPermissions/index.ts new file mode 100644 index 00000000..dd63d340 --- /dev/null +++ b/src/database/operations/userDataProviderPermissions/index.ts @@ -0,0 +1 @@ +export * from './createOrUpdateUserDataProviderPermission'; diff --git a/src/database/operations/userFunderPermissions/createOrUpdateUserFunderPermission.ts b/src/database/operations/userFunderPermissions/createOrUpdateUserFunderPermission.ts new file mode 100644 index 00000000..065e80ff --- /dev/null +++ b/src/database/operations/userFunderPermissions/createOrUpdateUserFunderPermission.ts @@ -0,0 +1,32 @@ +import { db } from '../../db'; +import type { + UserFunderPermission, + InternallyWritableUserFunderPermission, + JsonResultSet, +} from '../../../types'; + +const createOrUpdateUserFunderPermission = async ( + createValues: InternallyWritableUserFunderPermission, +): Promise => { + const { userKeycloakUserId, funderShortCode, permission, createdBy } = + createValues; + const result = await db.sql>( + 'userFunderPermissions.insertOrUpdateOne', + { + userKeycloakUserId, + permission, + funderShortCode, + createdBy, + }, + ); + + const { object } = result.rows[0] ?? {}; + if (object === undefined) { + throw new Error( + 'The entity creation did not appear to fail, but no data was returned by the operation.', + ); + } + return object; +}; + +export { createOrUpdateUserFunderPermission }; diff --git a/src/database/operations/userFunderPermissions/index.ts b/src/database/operations/userFunderPermissions/index.ts new file mode 100644 index 00000000..f0a3a374 --- /dev/null +++ b/src/database/operations/userFunderPermissions/index.ts @@ -0,0 +1 @@ +export * from './createOrUpdateUserFunderPermission'; diff --git a/src/database/queries/userChangemakerPermissions/insertOrUpdateOne.sql b/src/database/queries/userChangemakerPermissions/insertOrUpdateOne.sql new file mode 100644 index 00000000..f97e31a1 --- /dev/null +++ b/src/database/queries/userChangemakerPermissions/insertOrUpdateOne.sql @@ -0,0 +1,14 @@ +INSERT INTO user_changemaker_permissions ( + user_keycloak_user_id, + permission, + changemaker_id, + created_by +) VALUES ( + :userKeycloakUserId, + :permission::permission_t, + :changemakerId, + :createdBy +) +ON CONFLICT (user_keycloak_user_id, permission, changemaker_id) +DO NOTHING +RETURNING user_changemaker_permission_to_json(user_changemaker_permissions) AS "object"; diff --git a/src/database/queries/userDataProviderPermissions/insertOrUpdateOne.sql b/src/database/queries/userDataProviderPermissions/insertOrUpdateOne.sql new file mode 100644 index 00000000..85486fc4 --- /dev/null +++ b/src/database/queries/userDataProviderPermissions/insertOrUpdateOne.sql @@ -0,0 +1,14 @@ +INSERT INTO user_data_provider_permissions ( + user_keycloak_user_id, + permission, + data_provider_short_code, + created_by +) VALUES ( + :userKeycloakUserId, + :permission::permission_t, + :dataProviderShortCode, + :createdBy +) +ON CONFLICT (user_keycloak_user_id, permission, data_provider_short_code) +DO NOTHING +RETURNING user_data_provider_permission_to_json(user_data_provider_permissions) AS "object"; diff --git a/src/database/queries/userFunderPermissions/insertOrUpdateOne.sql b/src/database/queries/userFunderPermissions/insertOrUpdateOne.sql new file mode 100644 index 00000000..984939b6 --- /dev/null +++ b/src/database/queries/userFunderPermissions/insertOrUpdateOne.sql @@ -0,0 +1,14 @@ +INSERT INTO user_funder_permissions ( + user_keycloak_user_id, + permission, + funder_short_code, + created_by +) VALUES ( + :userKeycloakUserId, + :permission::permission_t, + :funderShortCode, + :createdBy +) +ON CONFLICT (user_keycloak_user_id, permission, funder_short_code) +DO NOTHING +RETURNING user_funder_permission_to_json(user_funder_permissions) AS "object"; diff --git a/src/openapi.json b/src/openapi.json index d5761457..d32cac96 100644 --- a/src/openapi.json +++ b/src/openapi.json @@ -947,6 +947,110 @@ } ] }, + "AccessType": { + "type": "string", + "enum": ["manage", "edit", "view"] + }, + "ChangemakerRole": { + "type": "object", + "properties": { + "changemakerId": { + "type": "integer", + "readOnly": true + }, + "userKeycloakUserId": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "accessType": { + "$ref": "#/components/schemas/AccessType", + "readOnly": true + }, + "createdBy": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "createdAt": { + "type": "string", + "format": "date-time", + "readOnly": true + } + }, + "required": ["changemakerId", "userKeycloakUserId", "assignedRole"] + }, + "DataProviderRole": { + "type": "object", + "properties": { + "dataProviderShortCode": { + "type": "string", + "readOnly": true + }, + "userKeycloakUserId": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "accessType": { + "$ref": "#/components/schemas/AccessType", + "readOnly": true + }, + "createdBy": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "createdAt": { + "type": "string", + "format": "date-time", + "readOnly": true + } + }, + "required": [ + "dataProviderShortCode", + "userKeycloakUserId", + "assignedRole" + ] + }, + "FunderRole": { + "type": "object", + "properties": { + "funderShortCode": { + "type": "string", + "readOnly": true + }, + "userKeycloakUserId": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "accessType": { + "$ref": "#/components/schemas/AccessType", + "readOnly": true + }, + "createdBy": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "createdAt": { + "type": "string", + "format": "date-time", + "readOnly": true + } + }, + "required": ["funderShortCode", "userKeycloakUserId", "assignedRole"] + }, + "RoleMap": { + "type": "object", + "propertyNames": { + "$ref": "#/components/schemas/AccessType" + }, + "additionalProperties": { + "type": "boolean" + } + }, "User": { "type": "object", "properties": { @@ -954,6 +1058,31 @@ "type": "string", "example": "550e8400-e29b-41d4-a716-446655440000" }, + "roles": { + "type": "object", + "readOnly": true, + "properties": { + "changemaker": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/RoleMap" + } + }, + "dataProvider": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/RoleMap" + } + }, + "funder": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/RoleMap" + } + } + }, + "required": ["changemaker", "dataProvider", "funder"] + }, "createdAt": { "type": "string", "format": "date-time", diff --git a/src/types/Permission.ts b/src/types/Permission.ts new file mode 100644 index 00000000..ebab4002 --- /dev/null +++ b/src/types/Permission.ts @@ -0,0 +1,7 @@ +enum Permission { + MANAGE = 'manage', + EDIT = 'edit', + VIEW = 'view', +} + +export { Permission }; diff --git a/src/types/UserChangemakerPermission.ts b/src/types/UserChangemakerPermission.ts new file mode 100644 index 00000000..0020968c --- /dev/null +++ b/src/types/UserChangemakerPermission.ts @@ -0,0 +1,40 @@ +import { ajv } from '../ajv'; +import { Permission } from './Permission'; +import type { JSONSchemaType } from 'ajv'; +import type { Writable } from './Writable'; +import type { KeycloakUserId } from './KeycloakUserId'; + +interface UserChangemakerPermission { + readonly userKeycloakUserId: KeycloakUserId; + readonly permission: Permission; + readonly changemakerId: number; + readonly createdBy: KeycloakUserId; + readonly createdAt: string; +} + +type WritableUserChangemakerPermission = Writable; + +type InternallyWritableUserChangemakerPermission = + WritableUserChangemakerPermission & + Pick< + UserChangemakerPermission, + 'userKeycloakUserId' | 'permission' | 'changemakerId' | 'createdBy' + >; + +const writableUserChangemakerPermissionSchema: JSONSchemaType = + { + type: 'object', + properties: {}, + required: [], + }; + +const isWritableUserChangemakerPermission = ajv.compile( + writableUserChangemakerPermissionSchema, +); + +export { + InternallyWritableUserChangemakerPermission, + UserChangemakerPermission, + WritableUserChangemakerPermission, + isWritableUserChangemakerPermission, +}; diff --git a/src/types/UserDataProviderPermission.ts b/src/types/UserDataProviderPermission.ts new file mode 100644 index 00000000..df706478 --- /dev/null +++ b/src/types/UserDataProviderPermission.ts @@ -0,0 +1,44 @@ +import { ajv } from '../ajv'; +import { Permission } from './Permission'; +import type { JSONSchemaType } from 'ajv'; +import type { Writable } from './Writable'; +import type { ShortCode } from './ShortCode'; +import type { KeycloakUserId } from './KeycloakUserId'; + +interface UserDataProviderPermission { + readonly userKeycloakUserId: KeycloakUserId; + readonly permission: Permission; + readonly dataProviderShortCode: ShortCode; + readonly createdBy: KeycloakUserId; + readonly createdAt: string; +} + +type WritableUserDataProviderPermission = Writable; + +type InternallyWritableUserDataProviderPermission = + WritableUserDataProviderPermission & + Pick< + UserDataProviderPermission, + | 'userKeycloakUserId' + | 'permission' + | 'dataProviderShortCode' + | 'createdBy' + >; + +const writableUserDataProviderSchema: JSONSchemaType = + { + type: 'object', + properties: {}, + required: [], + }; + +const isWritableUserDataProviderPermission = ajv.compile( + writableUserDataProviderSchema, +); + +export { + InternallyWritableUserDataProviderPermission, + UserDataProviderPermission, + WritableUserDataProviderPermission, + isWritableUserDataProviderPermission, +}; diff --git a/src/types/UserFunderPermission.ts b/src/types/UserFunderPermission.ts new file mode 100644 index 00000000..7be9a268 --- /dev/null +++ b/src/types/UserFunderPermission.ts @@ -0,0 +1,40 @@ +import { ajv } from '../ajv'; +import { Permission } from './Permission'; +import type { JSONSchemaType } from 'ajv'; +import type { Writable } from './Writable'; +import type { ShortCode } from './ShortCode'; +import type { KeycloakUserId } from './KeycloakUserId'; + +interface UserFunderPermission { + readonly userKeycloakUserId: KeycloakUserId; + readonly permission: Permission; + readonly funderShortCode: ShortCode; + readonly createdBy: KeycloakUserId; + readonly createdAt: string; +} + +type WritableUserFunderPermission = Writable; + +type InternallyWritableUserFunderPermission = WritableUserFunderPermission & + Pick< + UserFunderPermission, + 'userKeycloakUserId' | 'permission' | 'funderShortCode' | 'createdBy' + >; + +const writableUserFunderPermissionSchema: JSONSchemaType = + { + type: 'object', + properties: {}, + required: [], + }; + +const isWritableUserFunderPermission = ajv.compile( + writableUserFunderPermissionSchema, +); + +export { + InternallyWritableUserFunderPermission, + UserFunderPermission, + WritableUserFunderPermission, + isWritableUserFunderPermission, +}; diff --git a/src/types/index.ts b/src/types/index.ts index 5ca7bc4b..4787af3e 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -19,6 +19,7 @@ export * from './Language'; export * from './Opportunity'; export * from './PaginationParameters'; export * from './PaginationParametersQuery'; +export * from './Permission'; export * from './PlatformProviderResponse'; export * from './PostgresErrorCode'; export * from './PresignedPostRequest'; @@ -31,4 +32,7 @@ export * from './Source'; export * from './TableMetrics'; export * from './TinyPgErrorWithQueryContext'; export * from './User'; +export * from './UserChangemakerPermission'; +export * from './UserDataProviderPermission'; +export * from './UserFunderPermission'; export * from './Uuid';