From 217458a9a8bfff1708cb419ec5bd7b5c8de30945 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 4 Oct 2024 16:58:36 +0200 Subject: [PATCH] auth-node: add allowedDomains options for emailLocalPartMatchingUserEntityName + fixes Signed-off-by: Patrik Oldsberg --- .changeset/cuddly-stingrays-smell.md | 18 ++++++++++++++++++ .changeset/flat-seals-type.md | 5 +++++ .../config.d.ts | 5 ++++- .../config.d.ts | 5 ++++- .../config.d.ts | 5 ++++- .../config.d.ts | 5 ++++- .../config.d.ts | 5 ++++- .../config.d.ts | 5 ++++- .../config.d.ts | 5 ++++- .../config.d.ts | 5 ++++- .../config.d.ts | 5 ++++- .../config.d.ts | 5 ++++- .../config.d.ts | 5 ++++- .../config.d.ts | 5 ++++- .../config.d.ts | 5 ++++- .../config.d.ts | 5 ++++- plugins/auth-node/package.json | 3 ++- .../src/oauth/createOAuthProviderFactory.ts | 5 +---- .../proxy/createProxyAuthProviderFactory.ts | 5 +---- .../src/sign-in/commonSignInResolvers.ts | 17 ++++++++++++++++- .../src/sign-in/createSignInResolverFactory.ts | 12 ++++++++++-- yarn.lock | 9 +++++---- 22 files changed, 114 insertions(+), 30 deletions(-) create mode 100644 .changeset/cuddly-stingrays-smell.md create mode 100644 .changeset/flat-seals-type.md diff --git a/.changeset/cuddly-stingrays-smell.md b/.changeset/cuddly-stingrays-smell.md new file mode 100644 index 0000000000..5a8a8ac236 --- /dev/null +++ b/.changeset/cuddly-stingrays-smell.md @@ -0,0 +1,18 @@ +--- +'@backstage/plugin-auth-backend-module-cloudflare-access-provider': patch +'@backstage/plugin-auth-backend-module-vmware-cloud-provider': patch +'@backstage/plugin-auth-backend-module-atlassian-provider': patch +'@backstage/plugin-auth-backend-module-bitbucket-provider': patch +'@backstage/plugin-auth-backend-module-microsoft-provider': patch +'@backstage/plugin-auth-backend-module-onelogin-provider': patch +'@backstage/plugin-auth-backend-module-aws-alb-provider': patch +'@backstage/plugin-auth-backend-module-gcp-iap-provider': patch +'@backstage/plugin-auth-backend-module-github-provider': patch +'@backstage/plugin-auth-backend-module-gitlab-provider': patch +'@backstage/plugin-auth-backend-module-google-provider': patch +'@backstage/plugin-auth-backend-module-oauth2-provider': patch +'@backstage/plugin-auth-backend-module-oidc-provider': patch +'@backstage/plugin-auth-backend-module-okta-provider': patch +--- + +Updated configuration schema to include the new `allowedDomains` option for the `emailLocalPartMatchingUserEntityName` sign-in resolver. diff --git a/.changeset/flat-seals-type.md b/.changeset/flat-seals-type.md new file mode 100644 index 0000000000..9574c74fc9 --- /dev/null +++ b/.changeset/flat-seals-type.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-node': patch +--- + +Added a new `allowedDomains` option for the common `emailLocalPartMatchingUserEntityName` sign-in resolver. diff --git a/plugins/auth-backend-module-atlassian-provider/config.d.ts b/plugins/auth-backend-module-atlassian-provider/config.d.ts index b6ca96d62f..b1edea3d6c 100644 --- a/plugins/auth-backend-module-atlassian-provider/config.d.ts +++ b/plugins/auth-backend-module-atlassian-provider/config.d.ts @@ -31,7 +31,10 @@ export interface Config { signIn?: { resolvers: Array< | { resolver: 'usernameMatchingUserEntityName' } - | { resolver: 'emailLocalPartMatchingUserEntityName' } + | { + resolver: 'emailLocalPartMatchingUserEntityName'; + allowedDomains?: string[]; + } | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; diff --git a/plugins/auth-backend-module-aws-alb-provider/config.d.ts b/plugins/auth-backend-module-aws-alb-provider/config.d.ts index d76ae5bada..2e38490b28 100644 --- a/plugins/auth-backend-module-aws-alb-provider/config.d.ts +++ b/plugins/auth-backend-module-aws-alb-provider/config.d.ts @@ -41,7 +41,10 @@ export interface Config { region: string; signIn?: { resolvers: Array< - | { resolver: 'emailLocalPartMatchingUserEntityName' } + | { + resolver: 'emailLocalPartMatchingUserEntityName'; + allowedDomains?: string[]; + } | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; diff --git a/plugins/auth-backend-module-bitbucket-provider/config.d.ts b/plugins/auth-backend-module-bitbucket-provider/config.d.ts index df68209469..552a66d700 100644 --- a/plugins/auth-backend-module-bitbucket-provider/config.d.ts +++ b/plugins/auth-backend-module-bitbucket-provider/config.d.ts @@ -29,7 +29,10 @@ export interface Config { signIn?: { resolvers: Array< | { resolver: 'userIdMatchingUserEntityAnnotation' } - | { resolver: 'emailLocalPartMatchingUserEntityName' } + | { + resolver: 'emailLocalPartMatchingUserEntityName'; + allowedDomains?: string[]; + } | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; diff --git a/plugins/auth-backend-module-cloudflare-access-provider/config.d.ts b/plugins/auth-backend-module-cloudflare-access-provider/config.d.ts index 21b839b8d7..72dc599cea 100644 --- a/plugins/auth-backend-module-cloudflare-access-provider/config.d.ts +++ b/plugins/auth-backend-module-cloudflare-access-provider/config.d.ts @@ -31,7 +31,10 @@ export interface Config { authorizationCookieName?: string; signIn?: { resolvers: Array< - | { resolver: 'emailLocalPartMatchingUserEntityName' } + | { + resolver: 'emailLocalPartMatchingUserEntityName'; + allowedDomains?: string[]; + } | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; diff --git a/plugins/auth-backend-module-gcp-iap-provider/config.d.ts b/plugins/auth-backend-module-gcp-iap-provider/config.d.ts index 4ca426d10e..d4fe015b7c 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/config.d.ts +++ b/plugins/auth-backend-module-gcp-iap-provider/config.d.ts @@ -36,7 +36,10 @@ export interface Config { resolvers: Array< | { resolver: 'emailMatchingUserEntityAnnotation' } | { resolver: 'idMatchingUserEntityAnnotation' } - | { resolver: 'emailLocalPartMatchingUserEntityName' } + | { + resolver: 'emailLocalPartMatchingUserEntityName'; + allowedDomains?: string[]; + } | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; diff --git a/plugins/auth-backend-module-github-provider/config.d.ts b/plugins/auth-backend-module-github-provider/config.d.ts index 2ca17998f0..b1bc0dc477 100644 --- a/plugins/auth-backend-module-github-provider/config.d.ts +++ b/plugins/auth-backend-module-github-provider/config.d.ts @@ -31,7 +31,10 @@ export interface Config { signIn?: { resolvers: Array< | { resolver: 'usernameMatchingUserEntityName' } - | { resolver: 'emailLocalPartMatchingUserEntityName' } + | { + resolver: 'emailLocalPartMatchingUserEntityName'; + allowedDomains?: string[]; + } | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; diff --git a/plugins/auth-backend-module-gitlab-provider/config.d.ts b/plugins/auth-backend-module-gitlab-provider/config.d.ts index d21c2bd62a..cbb9f01e02 100644 --- a/plugins/auth-backend-module-gitlab-provider/config.d.ts +++ b/plugins/auth-backend-module-gitlab-provider/config.d.ts @@ -31,7 +31,10 @@ export interface Config { signIn?: { resolvers: Array< | { resolver: 'usernameMatchingUserEntityName' } - | { resolver: 'emailLocalPartMatchingUserEntityName' } + | { + resolver: 'emailLocalPartMatchingUserEntityName'; + allowedDomains?: string[]; + } | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; diff --git a/plugins/auth-backend-module-google-provider/config.d.ts b/plugins/auth-backend-module-google-provider/config.d.ts index 3abab05cfe..e788860fa8 100644 --- a/plugins/auth-backend-module-google-provider/config.d.ts +++ b/plugins/auth-backend-module-google-provider/config.d.ts @@ -30,7 +30,10 @@ export interface Config { signIn?: { resolvers: Array< | { resolver: 'emailMatchingUserEntityAnnotation' } - | { resolver: 'emailLocalPartMatchingUserEntityName' } + | { + resolver: 'emailLocalPartMatchingUserEntityName'; + allowedDomains?: string[]; + } | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; diff --git a/plugins/auth-backend-module-microsoft-provider/config.d.ts b/plugins/auth-backend-module-microsoft-provider/config.d.ts index b1ed2d5766..f63f37921d 100644 --- a/plugins/auth-backend-module-microsoft-provider/config.d.ts +++ b/plugins/auth-backend-module-microsoft-provider/config.d.ts @@ -32,7 +32,10 @@ export interface Config { signIn?: { resolvers: Array< | { resolver: 'emailMatchingUserEntityAnnotation' } - | { resolver: 'emailLocalPartMatchingUserEntityName' } + | { + resolver: 'emailLocalPartMatchingUserEntityName'; + allowedDomains?: string[]; + } | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; diff --git a/plugins/auth-backend-module-oauth2-provider/config.d.ts b/plugins/auth-backend-module-oauth2-provider/config.d.ts index fa9dec7ce1..5cee3a5407 100644 --- a/plugins/auth-backend-module-oauth2-provider/config.d.ts +++ b/plugins/auth-backend-module-oauth2-provider/config.d.ts @@ -35,7 +35,10 @@ export interface Config { signIn?: { resolvers: Array< | { resolver: 'usernameMatchingUserEntityName' } - | { resolver: 'emailLocalPartMatchingUserEntityName' } + | { + resolver: 'emailLocalPartMatchingUserEntityName'; + allowedDomains?: string[]; + } | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; diff --git a/plugins/auth-backend-module-oidc-provider/config.d.ts b/plugins/auth-backend-module-oidc-provider/config.d.ts index dbc06e51bb..d59c40697a 100644 --- a/plugins/auth-backend-module-oidc-provider/config.d.ts +++ b/plugins/auth-backend-module-oidc-provider/config.d.ts @@ -33,7 +33,10 @@ export interface Config { prompt?: string; signIn?: { resolvers: Array< - | { resolver: 'emailLocalPartMatchingUserEntityName' } + | { + resolver: 'emailLocalPartMatchingUserEntityName'; + allowedDomains?: string[]; + } | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; diff --git a/plugins/auth-backend-module-okta-provider/config.d.ts b/plugins/auth-backend-module-okta-provider/config.d.ts index 0689171153..d2b0c4aa02 100644 --- a/plugins/auth-backend-module-okta-provider/config.d.ts +++ b/plugins/auth-backend-module-okta-provider/config.d.ts @@ -33,7 +33,10 @@ export interface Config { signIn?: { resolvers: Array< | { resolver: 'emailMatchingUserEntityAnnotation' } - | { resolver: 'emailLocalPartMatchingUserEntityName' } + | { + resolver: 'emailLocalPartMatchingUserEntityName'; + allowedDomains?: string[]; + } | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; diff --git a/plugins/auth-backend-module-onelogin-provider/config.d.ts b/plugins/auth-backend-module-onelogin-provider/config.d.ts index 8257e2ac71..da7c312dd3 100644 --- a/plugins/auth-backend-module-onelogin-provider/config.d.ts +++ b/plugins/auth-backend-module-onelogin-provider/config.d.ts @@ -30,7 +30,10 @@ export interface Config { signIn?: { resolvers: Array< | { resolver: 'usernameMatchingUserEntityName' } - | { resolver: 'emailLocalPartMatchingUserEntityName' } + | { + resolver: 'emailLocalPartMatchingUserEntityName'; + allowedDomains?: string[]; + } | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; diff --git a/plugins/auth-backend-module-vmware-cloud-provider/config.d.ts b/plugins/auth-backend-module-vmware-cloud-provider/config.d.ts index 8bb8320c0c..67db735713 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/config.d.ts +++ b/plugins/auth-backend-module-vmware-cloud-provider/config.d.ts @@ -27,7 +27,10 @@ export interface Config { additionalScopes?: string | string[]; signIn?: { resolvers: Array< - | { resolver: 'emailLocalPartMatchingUserEntityName' } + | { + resolver: 'emailLocalPartMatchingUserEntityName'; + allowedDomains?: string[]; + } | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; diff --git a/plugins/auth-node/package.json b/plugins/auth-node/package.json index 6925fc82b6..b27a5fd942 100644 --- a/plugins/auth-node/package.json +++ b/plugins/auth-node/package.json @@ -53,7 +53,8 @@ "passport": "^0.7.0", "winston": "^3.2.1", "zod": "^3.22.4", - "zod-to-json-schema": "^3.21.4" + "zod-to-json-schema": "^3.21.4", + "zod-validation-error": "^3.4.0" }, "devDependencies": { "@backstage/backend-test-utils": "workspace:^", diff --git a/plugins/auth-node/src/oauth/createOAuthProviderFactory.ts b/plugins/auth-node/src/oauth/createOAuthProviderFactory.ts index fccfcb94de..f7f56371a3 100644 --- a/plugins/auth-node/src/oauth/createOAuthProviderFactory.ts +++ b/plugins/auth-node/src/oauth/createOAuthProviderFactory.ts @@ -34,10 +34,7 @@ export function createOAuthProviderFactory(options: { profileTransform?: ProfileTransform>; signInResolver?: SignInResolver>; signInResolverFactories?: { - [name in string]: SignInResolverFactory< - OAuthAuthenticatorResult, - unknown - >; + [name in string]: SignInResolverFactory; }; }): AuthProviderFactory { return ctx => { diff --git a/plugins/auth-node/src/proxy/createProxyAuthProviderFactory.ts b/plugins/auth-node/src/proxy/createProxyAuthProviderFactory.ts index 36e7b04e1e..0ce0102444 100644 --- a/plugins/auth-node/src/proxy/createProxyAuthProviderFactory.ts +++ b/plugins/auth-node/src/proxy/createProxyAuthProviderFactory.ts @@ -31,10 +31,7 @@ export function createProxyAuthProviderFactory(options: { authenticator: ProxyAuthenticator; profileTransform?: ProfileTransform; signInResolver?: SignInResolver; - signInResolverFactories?: Record< - string, - SignInResolverFactory - >; + signInResolverFactories?: Record; }): AuthProviderFactory { return ctx => { const signInResolver = diff --git a/plugins/auth-node/src/sign-in/commonSignInResolvers.ts b/plugins/auth-node/src/sign-in/commonSignInResolvers.ts index f664e7e33b..6f0fc7fdb2 100644 --- a/plugins/auth-node/src/sign-in/commonSignInResolvers.ts +++ b/plugins/auth-node/src/sign-in/commonSignInResolvers.ts @@ -14,7 +14,9 @@ * limitations under the License. */ +import { z } from 'zod'; import { createSignInResolverFactory } from './createSignInResolverFactory'; +import { NotAllowedError } from '@backstage/errors'; // This splits an email "joe+work@acme.com" into ["joe", "+work", "@acme.com"] // so that we can remove the plus addressing. May output a shorter array: @@ -77,7 +79,13 @@ export namespace commonSignInResolvers { */ export const emailLocalPartMatchingUserEntityName = createSignInResolverFactory({ - create() { + optionsSchema: z + .object({ + allowedDomains: z.array(z.string()).optional(), + }) + .optional(), + create(options = {}) { + const { allowedDomains } = options; return async (info, ctx) => { const { profile } = info; @@ -87,6 +95,13 @@ export namespace commonSignInResolvers { ); } const [localPart] = profile.email.split('@'); + const domain = profile.email.slice(localPart.length + 1); + + if (allowedDomains && !allowedDomains.includes(domain)) { + throw new NotAllowedError( + 'Sign-in user email is not from an allowed domain', + ); + } return ctx.signInWithCatalogUser({ entityRef: { name: localPart }, diff --git a/plugins/auth-node/src/sign-in/createSignInResolverFactory.ts b/plugins/auth-node/src/sign-in/createSignInResolverFactory.ts index 0632b704c3..e857a0fe3f 100644 --- a/plugins/auth-node/src/sign-in/createSignInResolverFactory.ts +++ b/plugins/auth-node/src/sign-in/createSignInResolverFactory.ts @@ -18,10 +18,11 @@ import { ZodSchema, ZodTypeDef } from 'zod'; import { SignInResolver } from '../types'; import zodToJsonSchema from 'zod-to-json-schema'; import { JsonObject } from '@backstage/types'; +import { fromError } from 'zod-validation-error'; import { InputError } from '@backstage/errors'; /** @public */ -export interface SignInResolverFactory { +export interface SignInResolverFactory { ( ...options: undefined extends TOptions ? [options?: TOptions] @@ -66,7 +67,14 @@ export function createSignInResolverFactory< ? [options?: TOptionsInput] : [options: TOptionsInput] ) => { - const parsedOptions = optionsSchema.parse(resolverOptions); + let parsedOptions; + try { + parsedOptions = optionsSchema.parse(resolverOptions); + } catch (error) { + throw new InputError( + `Invalid sign-in resolver options, ${fromError(error)}`, + ); + } return options.create(parsedOptions); }; diff --git a/yarn.lock b/yarn.lock index 93d7efa51d..790454ed4b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5461,6 +5461,7 @@ __metadata: winston: ^3.2.1 zod: ^3.22.4 zod-to-json-schema: ^3.21.4 + zod-validation-error: ^3.4.0 languageName: unknown linkType: soft @@ -45286,12 +45287,12 @@ __metadata: languageName: node linkType: hard -"zod-validation-error@npm:^3.0.3": - version: 3.1.0 - resolution: "zod-validation-error@npm:3.1.0" +"zod-validation-error@npm:^3.0.3, zod-validation-error@npm:^3.4.0": + version: 3.4.0 + resolution: "zod-validation-error@npm:3.4.0" peerDependencies: zod: ^3.18.0 - checksum: 84df01c91d594701eaf7f5f007be881e47f7adef2e3f3765f7be031cb78033f9be0924273106cb81b586d8020da9885dbb81b3da363f00a51df00f26274f2b23 + checksum: b07fbfc39582dbdf6972f5f5f0c3bac9e6b5e6d2e55ef3dd891fd08f1966ebf1023a4bc270e9b569eaa48ed1684ac2252c9f260b0bd07b167671596e6e4d0fa8 languageName: node linkType: hard