瀏覽代碼

Login with OIDC OAuth2 Oracle on premise identity manager OIM, with setting ORACLE_OIM_ENABLED=true.

Thanks to xet7 !
Lauri Ojansivu 4 年之前
父節點
當前提交
ec8a78537f
共有 9 個文件被更改,包括 222 次插入106 次删除
  1. 2 1
      Dockerfile
  2. 3 0
      docker-compose.yml
  3. 113 40
      packages/wekan-oidc/oidc_server.js
  4. 86 64
      server/authentication.js
  5. 0 0
      snap-src/bin/config
  6. 6 0
      snap-src/bin/wekan-help
  7. 5 0
      start-wekan.bat
  8. 3 0
      start-wekan.sh
  9. 4 1
      torodb-postgresql/docker-compose.yml

+ 2 - 1
Dockerfile

@@ -130,7 +130,8 @@ ENV BUILD_DEPS="apt-utils libarchive-tools gnupg gosu wget curl bzip2 g++ build-
     SAML_PUBLIC_CERTFILE="" \
     SAML_IDENTIFIER_FORMAT="" \
     SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE="" \
-    SAML_ATTRIBUTES=""
+    SAML_ATTRIBUTES="" \
+    ORACLE_OIM_ENABLED=false
 
 # Copy the app to the image
 COPY ${SRC_PATH} /home/wekan/app

+ 3 - 0
docker-compose.yml

@@ -323,6 +323,9 @@ services:
       # ==== Debug OIDC OAuth2 etc ====
       #- DEBUG=true
       #-----------------------------------------------------------------
+      # ==== OAUTH2 ORACLE on premise identity manager OIM ====
+      #- ORACLE_OIM_ENABLED=true
+      #-----------------------------------------------------------------
       # ==== OAUTH2 AZURE ====
       # https://github.com/wekan/wekan/wiki/Azure
       # 1) Register the application with Azure. Make sure you capture

+ 113 - 40
packages/wekan-oidc/oidc_server.js

@@ -33,7 +33,19 @@ OAuth.registerService('oidc', 2, null, function (query) {
   serviceData.fullname = userinfo[process.env.OAUTH2_FULLNAME_MAP]; // || userinfo["displayName"];
   serviceData.accessToken = accessToken;
   serviceData.expiresAt = expiresAt;
-  serviceData.email = userinfo[process.env.OAUTH2_EMAIL_MAP]; // || userinfo["email"];
+
+  // If on Oracle OIM email is empty or null, get info from username
+  if (process.env.ORACLE_OIM_ENABLED === 'true' || process.env.ORACLE_OIM_ENABLED === true) {
+    if (userinfo[process.env.OAUTH2_EMAIL_MAP]) {
+      serviceData.email = userinfo[process.env.OAUTH2_EMAIL_MAP];
+    } else {
+      serviceData.email = userinfo[process.env.OAUTH2_USERNAME_MAP];
+    }
+  }
+
+  if (process.env.ORACLE_OIM_ENABLED !== 'true' && process.env.ORACLE_OIM_ENABLED !== true) {
+    serviceData.email = userinfo[process.env.OAUTH2_EMAIL_MAP]; // || userinfo["email"];
+  }
 
   if (accessToken) {
     var tokenContent = getTokenContent(accessToken);
@@ -61,47 +73,108 @@ if (Meteor.release) {
   userAgent += "/" + Meteor.release;
 }
 
-var getToken = function (query) {
-  var debug = process.env.DEBUG || false;
-  var config = getConfiguration();
-  if(config.tokenEndpoint.includes('https://')){
-    var serverTokenEndpoint = config.tokenEndpoint;
-  }else{
-    var serverTokenEndpoint = config.serverUrl + config.tokenEndpoint;
-  }
-  var requestPermissions = config.requestPermissions;
-  var response;
+if (process.env.ORACLE_OIM_ENABLED !== 'true' && process.env.ORACLE_OIM_ENABLED !== true) {
+  var getToken = function (query) {
+    var debug = process.env.DEBUG || false;
+    var config = getConfiguration();
+    if(config.tokenEndpoint.includes('https://')){
+      var serverTokenEndpoint = config.tokenEndpoint;
+    }else{
+      var serverTokenEndpoint = config.serverUrl + config.tokenEndpoint;
+    }
+    var requestPermissions = config.requestPermissions;
+    var response;
 
-  try {
-    response = HTTP.post(
-      serverTokenEndpoint,
-      {
-        headers: {
-          Accept: 'application/json',
-          "User-Agent": userAgent
-        },
-        params: {
-          code: query.code,
-          client_id: config.clientId,
-          client_secret: OAuth.openSecret(config.secret),
-          redirect_uri: OAuth._redirectUri('oidc', config),
-          grant_type: 'authorization_code',
-          state: query.state
+    try {
+      response = HTTP.post(
+        serverTokenEndpoint,
+        {
+          headers: {
+            Accept: 'application/json',
+            "User-Agent": userAgent
+          },
+          params: {
+            code: query.code,
+            client_id: config.clientId,
+            client_secret: OAuth.openSecret(config.secret),
+            redirect_uri: OAuth._redirectUri('oidc', config),
+            grant_type: 'authorization_code',
+            state: query.state
+          }
         }
-      }
-    );
-  } catch (err) {
-    throw _.extend(new Error("Failed to get token from OIDC " + serverTokenEndpoint + ": " + err.message),
-      { response: err.response });
-  }
-  if (response.data.error) {
-    // if the http response was a json object with an error attribute
-    throw new Error("Failed to complete handshake with OIDC " + serverTokenEndpoint + ": " + response.data.error);
-  } else {
-    if (debug) console.log('XXX: getToken response: ', response.data);
-    return response.data;
-  }
-};
+      );
+    } catch (err) {
+      throw _.extend(new Error("Failed to get token from OIDC " + serverTokenEndpoint + ": " + err.message),
+        { response: err.response });
+    }
+    if (response.data.error) {
+      // if the http response was a json object with an error attribute
+      throw new Error("Failed to complete handshake with OIDC " + serverTokenEndpoint + ": " + response.data.error);
+    } else {
+      if (debug) console.log('XXX: getToken response: ', response.data);
+      return response.data;
+    }
+  };
+}
+
+if (process.env.ORACLE_OIM_ENABLED === 'true' || process.env.ORACLE_OIM_ENABLED === true) {
+
+  var getToken = function (query) {
+    var debug = (process.env.DEBUG === 'true' || process.env.DEBUG === true) || false;
+    var config = getConfiguration();
+    if(config.tokenEndpoint.includes('https://')){
+      var serverTokenEndpoint = config.tokenEndpoint;
+    }else{
+      var serverTokenEndpoint = config.serverUrl + config.tokenEndpoint;
+    }
+    var requestPermissions = config.requestPermissions;
+    var response;
+
+    // OIM needs basic Authentication token in the header - ClientID + SECRET in base64
+    var dataToken=null;
+    var strBasicToken=null;
+    var strBasicToken64=null;
+
+    dataToken = process.env.OAUTH2_CLIENT_ID + ':' + process.env.OAUTH2_SECRET;
+    strBasicToken = new Buffer(dataToken);
+    strBasicToken64 = strBasicToken.toString('base64');
+
+    // eslint-disable-next-line no-console
+    if (debug) console.log('Basic Token: ', strBasicToken64);
+
+    try {
+      response = HTTP.post(
+        serverTokenEndpoint,
+        {
+          headers: {
+            Accept: 'application/json',
+            "User-Agent": userAgent,
+            "Authorization": "Basic " + strBasicToken64
+          },
+          params: {
+            code: query.code,
+            client_id: config.clientId,
+            client_secret: OAuth.openSecret(config.secret),
+            redirect_uri: OAuth._redirectUri('oidc', config),
+            grant_type: 'authorization_code',
+            state: query.state
+          }
+        }
+      );
+    } catch (err) {
+      throw _.extend(new Error("Failed to get token from OIDC " + serverTokenEndpoint + ": " + err.message),
+        { response: err.response });
+    }
+    if (response.data.error) {
+      // if the http response was a json object with an error attribute
+      throw new Error("Failed to complete handshake with OIDC " + serverTokenEndpoint + ": " + response.data.error);
+    } else {
+      // eslint-disable-next-line no-console
+      if (debug) console.log('XXX: getToken response: ', response.data);
+      return response.data;
+    }
+  };
+}
 
 var getUserInfo = function (accessToken) {
   var debug = process.env.DEBUG || false;

+ 86 - 64
server/authentication.js

@@ -64,6 +64,28 @@ Meteor.startup(() => {
 
   if (Meteor.isServer) {
     if (
+      process.env.ORACLE_OIM_ENABLED === 'true' ||
+      process.env.ORACLE_OIM_ENABLED === true
+    ) {
+      ServiceConfiguration.configurations.upsert(
+        // eslint-disable-line no-undef
+        { service: 'oidc' },
+        {
+          $set: {
+            loginStyle: process.env.OAUTH2_LOGIN_STYLE,
+            clientId: process.env.OAUTH2_CLIENT_ID,
+            secret: process.env.OAUTH2_SECRET,
+            serverUrl: process.env.OAUTH2_SERVER_URL,
+            authorizationEndpoint: process.env.OAUTH2_AUTH_ENDPOINT,
+            userinfoEndpoint: process.env.OAUTH2_USERINFO_ENDPOINT,
+            tokenEndpoint: process.env.OAUTH2_TOKEN_ENDPOINT,
+            idTokenWhitelistFields:
+              process.env.OAUTH2_ID_TOKEN_WHITELIST_FIELDS || [],
+            requestPermissions: 'BDFUserProfile.me',
+          },
+        },
+      );
+    } else if (
       process.env.OAUTH2_ENABLED === 'true' ||
       process.env.OAUTH2_ENABLED === true
     ) {
@@ -87,73 +109,73 @@ Meteor.startup(() => {
           // OAUTH2_REQUEST_PERMISSIONS || 'openid profile email',
         },
       );
-    }
-  } else if (
-    process.env.CAS_ENABLED === 'true' ||
-    process.env.CAS_ENABLED === true
-  ) {
-    ServiceConfiguration.configurations.upsert(
-      // eslint-disable-line no-undef
-      { service: 'cas' },
-      {
-        $set: {
-          baseUrl: process.env.CAS_BASE_URL,
-          loginUrl: process.env.CAS_LOGIN_URL,
-          serviceParam: 'service',
-          popupWidth: 810,
-          popupHeight: 610,
-          popup: true,
-          autoClose: true,
-          validateUrl: process.env.CASE_VALIDATE_URL,
-          casVersion: 3.0,
-          attributes: {
-            debug: process.env.DEBUG,
+    } else if (
+      process.env.CAS_ENABLED === 'true' ||
+      process.env.CAS_ENABLED === true
+    ) {
+      ServiceConfiguration.configurations.upsert(
+        // eslint-disable-line no-undef
+        { service: 'cas' },
+        {
+          $set: {
+            baseUrl: process.env.CAS_BASE_URL,
+            loginUrl: process.env.CAS_LOGIN_URL,
+            serviceParam: 'service',
+            popupWidth: 810,
+            popupHeight: 610,
+            popup: true,
+            autoClose: true,
+            validateUrl: process.env.CASE_VALIDATE_URL,
+            casVersion: 3.0,
+            attributes: {
+              debug: process.env.DEBUG,
+            },
           },
         },
-      },
-    );
-  } else if (
-    process.env.SAML_ENABLED === 'true' ||
-    process.env.SAML_ENABLED === true
-  ) {
-    ServiceConfiguration.configurations.upsert(
-      // eslint-disable-line no-undef
-      { service: 'saml' },
-      {
-        $set: {
-          provider: process.env.SAML_PROVIDER,
-          entryPoint: process.env.SAML_ENTRYPOINT,
-          issuer: process.env.SAML_ISSUER,
-          cert: process.env.SAML_CERT,
-          idpSLORedirectURL: process.env.SAML_IDPSLO_REDIRECTURL,
-          privateKeyFile: process.env.SAML_PRIVATE_KEYFILE,
-          publicCertFile: process.env.SAML_PUBLIC_CERTFILE,
-          identifierFormat: process.env.SAML_IDENTIFIER_FORMAT,
-          localProfileMatchAttribute:
-            process.env.SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE,
-          attributesSAML: process.env.SAML_ATTRIBUTES || [
-            'sn',
-            'givenName',
-            'mail',
-          ],
+      );
+    } else if (
+      process.env.SAML_ENABLED === 'true' ||
+      process.env.SAML_ENABLED === true
+    ) {
+      ServiceConfiguration.configurations.upsert(
+        // eslint-disable-line no-undef
+        { service: 'saml' },
+        {
+          $set: {
+            provider: process.env.SAML_PROVIDER,
+            entryPoint: process.env.SAML_ENTRYPOINT,
+            issuer: process.env.SAML_ISSUER,
+            cert: process.env.SAML_CERT,
+            idpSLORedirectURL: process.env.SAML_IDPSLO_REDIRECTURL,
+            privateKeyFile: process.env.SAML_PRIVATE_KEYFILE,
+            publicCertFile: process.env.SAML_PUBLIC_CERTFILE,
+            identifierFormat: process.env.SAML_IDENTIFIER_FORMAT,
+            localProfileMatchAttribute:
+              process.env.SAML_LOCAL_PROFILE_MATCH_ATTRIBUTE,
+            attributesSAML: process.env.SAML_ATTRIBUTES || [
+              'sn',
+              'givenName',
+              'mail',
+            ],
 
-          /*
-          settings = {"saml":[{
-            "provider":"openam",
-            "entryPoint":"https://openam.idp.io/openam/SSORedirect/metaAlias/zimt/idp",
-            "issuer": "https://sp.zimt.io/", //replace with url of your app
-            "cert":"MIICizCCAfQCCQCY8tKaMc0 LOTS OF FUNNY CHARS ==",
-            "idpSLORedirectURL": "http://openam.idp.io/openam/IDPSloRedirect/metaAlias/zimt/idp",
-             "privateKeyFile": "certs/mykey.pem",  // path is relative to $METEOR-PROJECT/private
-             "publicCertFile": "certs/mycert.pem",  // eg $METEOR-PROJECT/private/certs/mycert.pem
-             "dynamicProfile": true // set to true if we want to create a user in Meteor.users dynamically if SAML assertion is valid
-             "identifierFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", // Defaults to urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
-             "localProfileMatchAttribute": "telephoneNumber" // CAUTION: this will be mapped to profile.<localProfileMatchAttribute> attribute in Mongo if identifierFormat (see above) differs from urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress,
-             "attributesSAML": [telephoneNumber, sn, givenName, mail], // attrs from SAML attr statement, which will be used for local Meteor profile creation. Currently no real attribute mapping. If required use mapping on IdP side.
-          }]}
-          */
+            /*
+            settings = {"saml":[{
+              "provider":"openam",
+              "entryPoint":"https://openam.idp.io/openam/SSORedirect/metaAlias/zimt/idp",
+              "issuer": "https://sp.zimt.io/", //replace with url of your app
+              "cert":"MIICizCCAfQCCQCY8tKaMc0 LOTS OF FUNNY CHARS ==",
+              "idpSLORedirectURL": "http://openam.idp.io/openam/IDPSloRedirect/metaAlias/zimt/idp",
+              "privateKeyFile": "certs/mykey.pem",  // path is relative to $METEOR-PROJECT/private
+              "publicCertFile": "certs/mycert.pem",  // eg $METEOR-PROJECT/private/certs/mycert.pem
+              "dynamicProfile": true // set to true if we want to create a user in Meteor.users dynamically if SAML assertion is valid
+              "identifierFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", // Defaults to urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
+              "localProfileMatchAttribute": "telephoneNumber" // CAUTION: this will be mapped to profile.<localProfileMatchAttribute> attribute in Mongo if identifierFormat (see above) differs from urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress,
+              "attributesSAML": [telephoneNumber, sn, givenName, mail], // attrs from SAML attr statement, which will be used for local Meteor profile creation. Currently no real attribute mapping. If required use mapping on IdP side.
+            }]}
+            */
+          },
         },
-      },
-    );
+      );
+    }
   }
 });

文件差異過大導致無法顯示
+ 0 - 0
snap-src/bin/config


+ 6 - 0
snap-src/bin/wekan-help

@@ -182,6 +182,12 @@ echo -e "\t$ snap set $SNAP_NAME oauth2-enabled='true'"
 echo -e "\t-Disable the OAuth2 of Wekan:"
 echo -e "\t$ snap unset $SNAP_NAME oauth2-enabled"
 echo -e "\n"
+echo -e "Enable OAuth2 Oracle on premise identity manager OIM. Default: false"
+echo -e "To enable the OAuth2 Oracle OIM of Wekan:"
+echo -e "\t$ snap set $SNAP_NAME oracle-oim-enabled='true'"
+echo -e "\t-Disable the OAuth2 Oracle OIM of Wekan:"
+echo -e "\t$ snap unset $SNAP_NAME oracle-oim-enabled"
+echo -e "\n"
 echo -e "OAuth2 ADFS Enabled. Also requires oauth2-enabled='true'"
 echo -e "To enable the OAuth2 ADFS of Wekan:"
 echo -e "\t$ snap set $SNAP_NAME oauth2-adfs-enabled='true'"

+ 5 - 0
start-wekan.bat

@@ -114,6 +114,11 @@ REM SET WEBHOOKS_ATTRIBUTES=
 
 REM ------------------------------------------------------------
 
+REM # OAUTH2 ORACLE on premise identity manager OIM
+REM SET ORACLE_OIM_ENABLED=true
+
+REM ------------------------------------------------------------
+
 REM # Enable the OAuth2 connection
 REM # OAuth2 docs: https://github.com/wekan/wekan/wiki/OAuth2
 REM # example: OAUTH2_ENABLED=true

+ 3 - 0
start-wekan.sh

@@ -120,6 +120,9 @@
       # Example: export WEBHOOKS_ATTRIBUTES=cardId,listId,oldListId,boardId,comment,user,card,commentId
       export WEBHOOKS_ATTRIBUTES=''
       #---------------------------------------------
+      # OAUTH2 ORACLE on premise identity manager OIM
+      #export ORACLE_OIM_ENABLED=true
+      #---------------------------------------------
       # ==== OAUTH2 AZURE ====
       # https://github.com/wekan/wekan/wiki/Azure
       # 1) Register the application with Azure. Make sure you capture

+ 4 - 1
torodb-postgresql/docker-compose.yml

@@ -132,7 +132,7 @@ services:
         ' 1>/dev/null 2>&1 &
         mongod --replSet rs1
   wekan:
-    image: quay.io/wekan/wekan
+    image: wekanteam/wekan
     container_name: wekan-app
     restart: always
     networks:
@@ -309,6 +309,9 @@ services:
       # example: WEBHOOKS_ATTRIBUTES=cardId,listId,oldListId,boardId,comment,user,card,commentId
       #- WEBHOOKS_ATTRIBUTES=
       #-----------------------------------------------------------------
+      # ==== OAUTH2 ORACLE on premise identity manager OIM ====
+      #- ORACLE_OIM_ENABLED=true
+      #-----------------------------------------------------------------
       # ==== OAUTH2 ONLY WITH OIDC AND DOORKEEPER AS INDENTITY PROVIDER
       # https://github.com/wekan/wekan/issues/1874
       # https://github.com/wekan/wekan/wiki/OAuth2

部分文件因文件數量過多而無法顯示