Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: enable dynamic profile field mapping via env variables for oidc response #1041

Merged
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
590e66c
feat: Enable dynamic profile field mapping via env variables
Junjiequan Jan 30, 2024
05fda06
Merge branch 'master' into SWAP-3745-scicat-be-make-oidc-user-validat…
Junjiequan Jan 30, 2024
a8c7150
add oidc groups to access groups
Junjiequan Jan 30, 2024
03fcc15
fix: minor fix for typo
Junjiequan Jan 30, 2024
f174ff3
fix: set default oidc displayName to userInfo.name
Junjiequan Jan 30, 2024
13739cf
Added removal of duplicates post-array concatenation
Junjiequan Jan 30, 2024
7458961
fixed typo and wrong user profile schema
Junjiequan Jan 30, 2024
854cf3f
Merge branch 'master' into SWAP-3745-scicat-be-make-oidc-user-validat…
Junjiequan Jan 30, 2024
4864519
Allow for override of configuration
bpedersen2 Sep 11, 2023
b07606e
Make the acccess groups service configurable
bpedersen2 Sep 11, 2023
c17335c
Add example graphql handler
bpedersen2 Nov 23, 2023
ca4f17c
removed redundant code
Junjiequan Jan 31, 2024
b96d585
OIDC userinfo and user query settings have been made configurable
Junjiequan Feb 5, 2024
927525a
improved if condition for parseQueryFilter
Junjiequan Feb 6, 2024
6b9cf7f
fixed wrong default logger method integration
Junjiequan Feb 6, 2024
2736ea3
improved logger message format
Junjiequan Feb 7, 2024
2f3c50d
get accessGroupsProperty from userPayload for AccesGroupFromPayloadSe…
Junjiequan Feb 7, 2024
75cf22a
fix access-group-from-payload unit test fail
Junjiequan Feb 7, 2024
13c4f69
refactor: improved readability of parseQueryFilter in oidc.strategy file
Junjiequan Feb 7, 2024
29e3e3d
fixed defaultLogger to log message without undefined even second para…
Junjiequan Feb 7, 2024
952621a
Merge branch 'master' into SWAP-3745-scicat-be-make-oidc-user-validat…
nitrosx Feb 9, 2024
f26522f
moved externalId and provider of create-user-identity dto to update-u…
Junjiequan Feb 9, 2024
f3b7268
Merge branch 'master' into SWAP-3745-scicat-be-make-oidc-user-validat…
Junjiequan Feb 13, 2024
0b701c1
Merge branch 'master' into SWAP-3745-scicat-be-make-oidc-user-validat…
Junjiequan Feb 20, 2024
c300a77
minor refactoring
Junjiequan Feb 22, 2024
fd7e9c1
README updates for new environment variables
Junjiequan Feb 22, 2024
5d51633
Merge branch 'master' into SWAP-3745-scicat-be-make-oidc-user-validat…
Junjiequan Feb 22, 2024
571cfbd
Merge branch 'master' into SWAP-3745-scicat-be-make-oidc-user-validat…
Junjiequan Feb 26, 2024
2f52cf7
Update README.md
Junjiequan Feb 27, 2024
24cf7a3
Merge branch 'master' into SWAP-3745-scicat-be-make-oidc-user-validat…
Junjiequan Feb 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { Injectable, Logger } from "@nestjs/common";
import { UserPayload } from "../interfaces/userPayload.interface";
import { HttpService } from "@nestjs/axios";
import { firstValueFrom } from "rxjs";

/**
* This service is used to fetch access groups from a GraphQL API.
*/
Expand All @@ -27,14 +26,19 @@ export class AccessGroupFromGraphQLApiService extends AccessGroupService {
): Promise<string[]> {
const userId = userPayload.userId as string;
const query = this.graphqlTemplateQuery.replace("{{userId}}", userId);
const response = await this.callGraphQLApi(query);
const accessGroups = this.responseProcessor(response);
try {
const response = await this.callGraphQLApi(query);
const accessGroups = this.responseProcessor(response);

Logger.log(
"Access groups from graphql api call getAccessGroups: " +
accessGroups.join(","),
);
return accessGroups;
Logger.log(
nitrosx marked this conversation as resolved.
Show resolved Hide resolved
accessGroups,
"AccessGroupFromGraphQLApiService getAccessGroups:",
);
return accessGroups;
} catch (error) {
Logger.error(error, "AccessGroupFromGraphQLApiService");
return [];
}
}

async callGraphQLApi(query: string): Promise<Record<string, unknown>> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Injectable } from "@nestjs/common";
import { Injectable, Logger } from "@nestjs/common";

Check warning on line 1 in src/auth/access-group-provider/access-group-from-multiple-providers.service.ts

View workflow job for this annotation

GitHub Actions / eslint

'Logger' is defined but never used
import { UserPayload } from "../interfaces/userPayload.interface";
import { AccessGroupService } from "./access-group.service";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ describe("AccessGroupFromPayloadService", () => {
let service: AccessGroupFromPayloadService;

const mockConfigService = {
get: () => ({
accessGroups: "accessGroups",
}),
get: () => "access_group_property",
};

beforeEach(async () => {
Expand All @@ -30,14 +28,15 @@ describe("AccessGroupFromPayloadService", () => {
});

it("Should resolve access groups", async () => {
const expected = ["AAA", "BBB"];
const actual = await service.getAccessGroups({
const userPayload = {
userId: "test_user",
accessGroupProperty: "access_group_property",
accessGroupProperty: "testGroups",
payload: {
access_group_property: expected,
testGroups: ["AAA", "BBB"],
},
} as UserPayload);
};
const expected = userPayload.payload.testGroups;
const actual = await service.getAccessGroups(userPayload as UserPayload);
expect(actual).toEqual(expected);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ export class AccessGroupFromPayloadService extends AccessGroupService {
//const defaultAccessGroups: string[] = [];
let accessGroups: string[] = [];

const accessGroupsProperty = userPayload?.accessGroupProperty;

const accessGroupsProperty = userPayload.accessGroupProperty;
if (accessGroupsProperty) {
const payload: Record<string, unknown> | undefined = userPayload.payload;
if (
Expand All @@ -30,11 +29,9 @@ export class AccessGroupFromPayloadService extends AccessGroupService {
? (payload[accessGroupsProperty] as string[])
: [];
}
Logger.log(accessGroups, "AccessGroupFromPayloadService");
}

Logger.log(
"Access groups AccessGroupFromPayloadService : " + accessGroups.join(","),
);
return accessGroups;
}
}
70 changes: 66 additions & 4 deletions src/auth/access-group-provider/access-group-service-factory.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,79 @@
import { ConfigService } from "@nestjs/config";
import { AccessGroupFromStaticValuesService } from "./access-group-from-static-values.service";
import { AccessGroupService } from "./access-group.service";

import { AccessGroupFromGraphQLApiService } from "./access-group-from-graphql-api-call.service";
import { AccessGroupFromPayloadService } from "./access-group-from-payload.service";
import { HttpService } from "@nestjs/axios";
import { AccessGroupFromMultipleProvidersService } from "./access-group-from-multiple-providers.service";
import { Logger } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
/*
* this is the default function which provides an empty array as groups
*/
export const accessGroupServiceFactory = {
imports: [ConfigModule],
provide: AccessGroupService,
useFactory: (configService: ConfigService) => {
const accessGroupsStaticValues = configService.get(
"accessGroupsStaticValues",
Logger.debug("Service factory starting", "accessGroupServiceFactory");
const accessGroupsStaticConfig = configService.get(
"accessGroupsStaticConfig",
);
const accessGroupsGraphQlConfig = configService.get(
"accessGroupsGraphQlConfig",
);
return new AccessGroupFromStaticValuesService(accessGroupsStaticValues);
const accessGroupsOIDCPayloadConfig = configService.get(
"accessGroupsOIDCPayloadConfig",
);

const accessGroupServices: AccessGroupService[] = [];
if (accessGroupsStaticConfig?.enabled == true) {
Logger.log(
nitrosx marked this conversation as resolved.
Show resolved Hide resolved
JSON.stringify(accessGroupsStaticConfig),
"loading static processor",
);
accessGroupServices.push(
new AccessGroupFromStaticValuesService(accessGroupsStaticConfig.value),
);
}
if (accessGroupsOIDCPayloadConfig?.enabled == true) {
Logger.log(
JSON.stringify(accessGroupsOIDCPayloadConfig),
"loading oidc processor",
);
accessGroupServices.push(
new AccessGroupFromPayloadService(configService),
);
}

if (accessGroupsGraphQlConfig?.enabled == true) {
Logger.log(
JSON.stringify(accessGroupsGraphQlConfig),
"loading graphql processor",
);

import(accessGroupsGraphQlConfig.responseProcessorSrc).then(
(rpModule) => {
const gh = rpModule.graphHandler;
const responseProcessor: (
response: Record<string, unknown>,
) => string[] = gh.responseProcessor;
const graphqlTemplateQuery: string = gh.graphqlTemplateQuery;
accessGroupServices.push(
new AccessGroupFromGraphQLApiService(
graphqlTemplateQuery,
accessGroupsGraphQlConfig.apiUrl,
{
Authorization: `Bearer ${accessGroupsGraphQlConfig.token}`,
},
responseProcessor,
new HttpService(),
),
);
},
);
}

return new AccessGroupFromMultipleProvidersService(accessGroupServices);
},
inject: [ConfigService],
};
16 changes: 16 additions & 0 deletions src/auth/interfaces/oidc-user.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export interface IOidcUserInfoMapping {
id: string;
username: string;
displayName: string;
familyName: string;
email: string;
thumbnailPhoto: string;
groups?: string[];
provider?: string;
[key: string]: string | string[] | undefined;
}

export interface IOidcUserQueryMapping {
operator: string;
filter: string[];
}
Loading
Loading