Yuki Takei před 1 rokem
rodič
revize
b8761539db

+ 20 - 0
apps/app/src/server/service/config-manager/config-definition.ts

@@ -126,17 +126,22 @@ export const CONFIG_KEYS = [
   'security:passport-ldap:groupSearchFilter',
   'security:passport-ldap:groupDnProperty',
   'security:passport-ldap:isSameUsernameTreatedAsIdenticalUser',
+  'security:passport-saml:isSameUsernameTreatedAsIdenticalUser',
   'security:passport-saml:isSameEmailTreatedAsIdenticalUser',
   'security:passport-google:isEnabled',
   'security:passport-google:clientId',
   'security:passport-google:clientSecret',
   'security:passport-google:isSameUsernameTreatedAsIdenticalUser',
+  'security:passport-google:isSameEmailTreatedAsIdenticalUser',
   'security:passport-github:isEnabled',
   'security:passport-github:clientId',
   'security:passport-github:clientSecret',
   'security:passport-github:isSameUsernameTreatedAsIdenticalUser',
+  'security:passport-github:isSameEmailTreatedAsIdenticalUser',
   'security:passport-oidc:isEnabled',
   'security:passport-oidc:issuerHost',
+  'security:passport-oidc:isSameUsernameTreatedAsIdenticalUser',
+  'security:passport-oidc:isSameEmailTreatedAsIdenticalUser',
 
   // File Upload Settings
   'fileUpload:local:useInternalRedirect',
@@ -681,6 +686,9 @@ export const CONFIG_DEFINITIONS = {
   'security:passport-saml:isSameEmailTreatedAsIdenticalUser': defineConfig<boolean>({
     defaultValue: false,
   }),
+  'security:passport-saml:isSameUsernameTreatedAsIdenticalUser': defineConfig<boolean>({
+    defaultValue: false,
+  }),
   'security:passport-google:isEnabled': defineConfig<boolean>({
     defaultValue: false,
   }),
@@ -693,6 +701,9 @@ export const CONFIG_DEFINITIONS = {
   'security:passport-google:isSameUsernameTreatedAsIdenticalUser': defineConfig<boolean>({
     defaultValue: false,
   }),
+  'security:passport-google:isSameEmailTreatedAsIdenticalUser': defineConfig<boolean>({
+    defaultValue: false,
+  }),
   'security:passport-github:isEnabled': defineConfig<boolean>({
     defaultValue: false,
   }),
@@ -705,12 +716,21 @@ export const CONFIG_DEFINITIONS = {
   'security:passport-github:isSameUsernameTreatedAsIdenticalUser': defineConfig<boolean>({
     defaultValue: false,
   }),
+  'security:passport-github:isSameEmailTreatedAsIdenticalUser': defineConfig<boolean>({
+    defaultValue: false,
+  }),
   'security:passport-oidc:isEnabled': defineConfig<boolean>({
     defaultValue: false,
   }),
   'security:passport-oidc:issuerHost': defineConfig<string | undefined>({
     defaultValue: undefined,
   }),
+  'security:passport-oidc:isSameUsernameTreatedAsIdenticalUser': defineConfig<boolean>({
+    defaultValue: false,
+  }),
+  'security:passport-oidc:isSameEmailTreatedAsIdenticalUser': defineConfig<boolean>({
+    defaultValue: false,
+  }),
 
   // File Upload Settings
   'fileUpload:local:useInternalRedirect': defineConfig<boolean>({

+ 7 - 4
apps/app/src/server/service/external-account.ts

@@ -4,9 +4,10 @@ import { LoginErrorCode } from '~/interfaces/errors/login-error';
 import loggerFactory from '~/utils/logger';
 
 import { NullUsernameToBeRegisteredError } from '../models/errors';
-import ExternalAccount, { ExternalAccountDocument } from '../models/external-account';
+import type { ExternalAccountDocument } from '../models/external-account';
+import ExternalAccount from '../models/external-account';
 
-import PassportService from './passport';
+import type PassportService from './passport';
 
 const logger = loggerFactory('growi:service:external-account-service');
 
@@ -21,11 +22,13 @@ class ExternalAccountService {
 
   async getOrCreateUser(
       userInfo: {id: string, username: string, name?: string, email?: string},
-      providerId: string,
+      providerId: 'ldap' | 'google' | 'github' | 'saml' | 'oidc',
   ): Promise<ExternalAccountDocument | undefined> {
     // get option
     const isSameUsernameTreatedAsIdenticalUser = this.passportService.isSameUsernameTreatedAsIdenticalUser(providerId);
-    const isSameEmailTreatedAsIdenticalUser = this.passportService.isSameEmailTreatedAsIdenticalUser(providerId);
+    const isSameEmailTreatedAsIdenticalUser = providerId === 'ldap'
+      ? false
+      : this.passportService.isSameEmailTreatedAsIdenticalUser(providerId);
 
     try {
       // find or register(create) user

+ 15 - 16
apps/app/src/server/service/passport.ts

@@ -17,6 +17,7 @@ import loggerFactory from '~/utils/logger';
 
 import S2sMessage from '../models/vo/s2s-message';
 
+import { configManager } from './config-manager';
 import type { S2sMessageHandlable } from './s2s-messaging/handlable';
 
 const logger = loggerFactory('growi:service:PassportService');
@@ -315,7 +316,7 @@ class PassportService implements S2sMessageHandlable {
    * @memberof PassportService
    */
   getLdapAttrNameMappedToUsername() {
-    return this.crowi.configManager.getConfig('security:passport-ldap:attrMapUsername') || 'uid';
+    return configManager.getConfig('security:passport-ldap:attrMapUsername') || 'uid';
   }
 
   /**
@@ -325,7 +326,7 @@ class PassportService implements S2sMessageHandlable {
    * @memberof PassportService
    */
   getLdapAttrNameMappedToName() {
-    return this.crowi.configManager.getConfig('security:passport-ldap:attrMapName') || '';
+    return configManager.getConfig('security:passport-ldap:attrMapName') || '';
   }
 
   /**
@@ -335,7 +336,7 @@ class PassportService implements S2sMessageHandlable {
    * @memberof PassportService
    */
   getLdapAttrNameMappedToMail() {
-    return this.crowi.configManager.getConfig('security:passport-ldap:attrMapMail') || 'mail';
+    return configManager.getConfig('security:passport-ldap:attrMapMail') || 'mail';
   }
 
   /**
@@ -554,7 +555,7 @@ class PassportService implements S2sMessageHandlable {
 
     // setup client
     // extend oidc request timeouts
-    const OIDC_ISSUER_TIMEOUT_OPTION = await this.crowi.configManager.getConfig('security:passport-oidc:oidcIssuerTimeoutOption');
+    const OIDC_ISSUER_TIMEOUT_OPTION = await configManager.getConfig('security:passport-oidc:oidcIssuerTimeoutOption');
     // OIDCIssuer.defaultHttpOptions = { timeout: OIDC_ISSUER_TIMEOUT_OPTION };
 
     custom.setHttpOptionsDefaults({
@@ -620,7 +621,7 @@ class PassportService implements S2sMessageHandlable {
       });
       // prevent error AssertionError [ERR_ASSERTION]: id_token issued in the future
       // Doc: https://github.com/panva/node-openid-client/tree/v2.x#allow-for-system-clock-skew
-      const OIDC_CLIENT_CLOCK_TOLERANCE = await this.crowi.configManager.getConfig('security:passport-oidc:oidcClientClockTolerance');
+      const OIDC_CLIENT_CLOCK_TOLERANCE = await configManager.getConfig('security:passport-oidc:oidcClientClockTolerance');
       client[custom.clock_tolerance] = OIDC_CLIENT_CLOCK_TOLERANCE;
       passport.use('oidc', new OidcStrategy(
         {
@@ -713,9 +714,9 @@ class PassportService implements S2sMessageHandlable {
    * @returns instance of OIDCIssuer
    */
   async getOIDCIssuerInstance(issuerHost: string): Promise<void | OIDCIssuer> {
-    const OIDC_TIMEOUT_MULTIPLIER = await this.crowi.configManager.getConfig('security:passport-oidc:timeoutMultiplier');
-    const OIDC_DISCOVERY_RETRIES = await this.crowi.configManager.getConfig('security:passport-oidc:discoveryRetries');
-    const OIDC_ISSUER_TIMEOUT_OPTION = await this.crowi.configManager.getConfig('security:passport-oidc:oidcIssuerTimeoutOption');
+    const OIDC_TIMEOUT_MULTIPLIER = await configManager.getConfig('security:passport-oidc:timeoutMultiplier');
+    const OIDC_DISCOVERY_RETRIES = await configManager.getConfig('security:passport-oidc:discoveryRetries');
+    const OIDC_ISSUER_TIMEOUT_OPTION = await configManager.getConfig('security:passport-oidc:oidcIssuerTimeoutOption');
     const oidcIssuerHostReady = await this.isOidcHostReachable(issuerHost);
     if (!oidcIssuerHostReady) {
       logger.error('OidcStrategy: setup failed');
@@ -799,7 +800,7 @@ class PassportService implements S2sMessageHandlable {
   getSamlMissingMandatoryConfigKeys() {
     const missingRequireds: string[] = [];
     for (const key of this.mandatoryConfigKeysForSaml) {
-      if (this.crowi.configManager.getConfig(key) == null) {
+      if (configManager.getConfig(key) == null) {
         missingRequireds.push(key);
       }
     }
@@ -822,7 +823,7 @@ class PassportService implements S2sMessageHandlable {
    * Verify that a SAML response meets the attribute-base login control rule
    */
   verifySAMLResponseByABLCRule(response) {
-    const rule = this.crowi.configManager.getConfig('security:passport-saml:ABLCRule');
+    const rule = configManager.getConfig('security:passport-saml:ABLCRule');
     if (rule == null) {
       logger.debug('There is no ABLCRule.');
       return true;
@@ -972,14 +973,12 @@ class PassportService implements S2sMessageHandlable {
     this.isSerializerSetup = true;
   }
 
-  isSameUsernameTreatedAsIdenticalUser(providerType) {
-    const key = `security:passport-${providerType}:isSameUsernameTreatedAsIdenticalUser`;
-    return this.crowi.configManager.getConfig(key);
+  isSameUsernameTreatedAsIdenticalUser(providerType: 'ldap' | 'google' | 'github' | 'saml' | 'oidc'): boolean {
+    return configManager.getConfig(`security:passport-${providerType}:isSameUsernameTreatedAsIdenticalUser`);
   }
 
-  isSameEmailTreatedAsIdenticalUser(providerType) {
-    const key = `security:passport-${providerType}:isSameEmailTreatedAsIdenticalUser`;
-    return this.crowi.configManager.getConfig(key);
+  isSameEmailTreatedAsIdenticalUser(providerType: 'google' | 'github' | 'saml' | 'oidc'): boolean {
+    return configManager.getConfig(`security:passport-${providerType}:isSameEmailTreatedAsIdenticalUser`);
   }
 
   literalUnescape(string: string) {