Difference between revisions of "Globalyzer Server SSO Installation"
(→Generate sp.xml) |
(→Configure GzserverConfig.groovy) |
||
(29 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
== Overview == |
== Overview == |
||
− | Many |
+ | Many companies use SAML SSO with an Identity Provider to manage users and access to applications. |
− | + | To integrate Globalyzer with SAML SSO, first, the Identity Provider must be configured to allow access to Globalyzer. |
|
− | Then, Globalyzer must be configured for SSO. |
+ | Then, Globalyzer must be configured for SSO. The result is three key files referenced from GzserverConfig.groovy |
+ | # a keystore that contains the identity provider certificate and a key |
||
+ | # the idp.xml file that describes the identity provider (Okta in our example) |
||
+ | # the sp.xml file that describes the service provider (our Globalyzer application) |
||
== Configure the Identity Provider == |
== Configure the Identity Provider == |
||
Line 24: | Line 27: | ||
* Choose <b>SAML 2.0</b> and then Next |
* Choose <b>SAML 2.0</b> and then Next |
||
* Give your app a name and click Next |
* Give your app a name and click Next |
||
− | * Single sign on URL: <your server machine>/gzserver/ |
+ | * Single sign on URL: <your server machine>/gzserver/login/saml2/sso/<your-saml-key>, for example https://saml.lingoport.net/gzserver/login/saml2/sso/gzkey |
+ | * Audience URI: <your server machine>/gzserver/saml2/service-provider-metadata/<your-saml-key>, for example https://saml.lingoport.net/gzserver/saml2/service-provider-metadata/gzkey |
||
− | * Audience URI: whatever you put here must match the entity id you put in the sp.xml file, for example, GlobalyzerServer |
||
* Attributes Section: enter in the following: |
* Attributes Section: enter in the following: |
||
First Name, Unspecified, user.firstName |
First Name, Unspecified, user.firstName |
||
Line 43: | Line 46: | ||
* Copy IDP Metadata to a file named idp.xml |
* Copy IDP Metadata to a file named idp.xml |
||
− | == Generate Keystore == |
+ | == Generate Keys and Keystore == |
* Generate key and keystore: |
* Generate key and keystore: |
||
− | keytool -genkey -alias |
+ | keytool -genkey -alias <your-saml-key> -keyalg RSA -keystore <your-key-store.jks> |
* Accept Identity Provider Certficate |
* Accept Identity Provider Certficate |
||
− | keytool -import -alias okta -keystore |
+ | keytool -import -alias okta -keystore <your-key-store.jks> -file <certificate you downloaded from okta> |
== Generate sp.xml == |
== Generate sp.xml == |
||
* Create a file named sp.xml with the following contents |
* Create a file named sp.xml with the following contents |
||
<?xml version="1.0" encoding="UTF-8"?> |
<?xml version="1.0" encoding="UTF-8"?> |
||
− | <md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" <b>entityID</b>=" |
+ | <md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" <b>entityID</b>="https://saml.lingoport.net/gzserver/saml2/service-provider-metadata/<your-saml-key>"> |
<md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> |
<md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> |
||
<md:Extensions><idpdisco:DiscoveryResponse xmlns:idpdisco="urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol" Binding="urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol" <b>Location</b>="https://saml.lingoport.net/gzserver/login/auth?disco=true"/> |
<md:Extensions><idpdisco:DiscoveryResponse xmlns:idpdisco="urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol" Binding="urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol" <b>Location</b>="https://saml.lingoport.net/gzserver/login/auth?disco=true"/> |
||
Line 71: | Line 74: | ||
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat> |
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat> |
||
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName</md:NameIDFormat> |
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName</md:NameIDFormat> |
||
− | <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" <b>Location</b>="https://saml.lingoport.net/gzserver/ |
+ | <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" <b>Location</b>="https://saml.lingoport.net/gzserver/login/saml2/sso/<your-saml-key>" index="0" isDefault="true"/> |
− | <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" <b>Location</b>="https://saml.lingoport.net/gzserver/ |
+ | <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" <b>Location</b>="https://saml.lingoport.net/gzserver/login/saml2/sso/<your-saml-key>" index="1" isDefault="false"/> |
− | <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:PAOS" <b>Location</b>="https://saml.lingoport.net/gzserver/ |
+ | <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:PAOS" <b>Location</b>="https://saml.lingoport.net/gzserver/login/saml2/sso/<your-saml-key>" index="2" isDefault="false"/> |
</md:SPSSODescriptor> |
</md:SPSSODescriptor> |
||
</md:EntityDescriptor> |
</md:EntityDescriptor> |
||
− | * Modify <b>entityId</b> to match what you specified as <b>Audience</b> in your Okta app |
+ | * Modify <b>entityId</b> to match what you specified as <b>Audience</b> in your Okta app. |
* Replace the two <b>CERTIFICATEs</b> with the certificate you downloaded from Okta. Open the file and grab the lines between BEGIN CERTIFICATE and END CERTIFICATE in the downloaded file. |
* Replace the two <b>CERTIFICATEs</b> with the certificate you downloaded from Okta. Open the file and grab the lines between BEGIN CERTIFICATE and END CERTIFICATE in the downloaded file. |
||
− | * Update the various <b>Locations</b> to be the machine your Globalyzer Server is running on ... keeping the /gzserver/ |
+ | * Update the various <b>Locations</b> to be the machine your Globalyzer Server is running on ... keeping the /gzserver/login/saml2/sso/<your-saml-key> or /gzserver/saml/SingleLogout endings |
− | == |
+ | == Configure GzserverConfig.groovy == |
− | SSO SAML configuration involves setting the environment variable GZSERVER_SAML_MODE to true, and configuring GzserverConfig.groovy. |
||
+ | * Copy <your-key-store.jks>, sp.xml, and idp.xml files to a specific location on the machine running the Globalyzer Server. |
||
− | === Setting GZSERVER_SAML_MODE === |
||
+ | * Add and configure the following lines to GzserverConfig.groovy: |
||
− | If your server runs on Linux, in the enterprise.sh file, set GZSERVER_SAML_MODE to true. |
||
+ | // tell Globalyzer and the Plugin to use saml |
||
− | #!/bin/sh |
||
+ | gzserver.saml.mode = true |
||
− | |||
+ | grails.plugin.springsecurity.saml.active = true |
||
− | export GZSERVER_ENTERPRISE_MODE=true |
||
+ | grails.plugin.springsecurity.providerNames = ['samlAuthenticationProvider','anonymousAuthenticationProvider'] |
||
− | <b>export GZSERVER_SAML_MODE=true</b> |
||
+ | grails.plugin.springsecurity.saml.loginFormUrl = '/saml2/authenticate/<your-saml-key>' |
||
− | export CATALINA_HOME=/usr/local/tomcat |
||
− | ... |
||
− | If your server runs on Windows, and you are using the enterprise.bat file to start/stop your server, set GZSERVER_SAML_MODE to true: |
||
+ | // keystore configuration |
||
− | @echo off |
||
+ | // assuming you created a keystore named saml-keystore.jks and a key named samlkey ... |
||
− | |||
+ | grails.plugin.springsecurity.saml.keyManager.storeFile = "file:/path/to/<your-key-store.jks>" |
||
− | if "%OS%" == "Windows_NT" setlocal |
||
+ | grails.plugin.springsecurity.saml.keyManager.storePass = '<your-keystore-pw>' |
||
− | |||
+ | grails.plugin.springsecurity.saml.keyManager.passwords = [<your-saml-key>:'<your-keystore-pw>'] |
||
− | set "GZSERVER_ENTERPRISE_MODE=true" |
||
+ | grails.plugin.springsecurity.saml.keyManager.defaultKey = '<your-saml-key>' |
||
− | <b>set "GZSERVER_SAML_MODE=true"</b> |
||
+ | grails.plugin.springsecurity.saml.metadata.sp.defaults.signingKey = '<your-saml-key>' |
||
− | set "CATALINA_HOME=C:\apache-tomcat-8.5.78" |
||
+ | grails.plugin.springsecurity.saml.metadata.sp.defaults.encryptionKey = '<your-saml-key>' |
||
− | ... |
||
+ | grails.plugin.springsecurity.saml.metadata.sp.defaults.tlsKey = '<your-saml-key>' |
||
+ | // leave as is if created okta app as specified above |
||
− | If your server runs on Windows and you are running Tomcat as a service, create a new GZSERVER_SAML_MODE environment variable for the machine, and set it to true. |
||
+ | grails.plugin.springsecurity.saml.userGroupAttribute = 'memberOf' |
||
+ | grails.plugin.springsecurity.saml.userAttributeMappings = ['username' : 'Email', 'firstName': 'First Name', 'lastName' : 'Last Name'] |
||
+ | grails.plugin.springsecurity.saml.userGroupToRoleMapping = ['ROLE_ADMIN': 'Globalyzer Admin', 'ROLE_MANAGER': 'Globalyzer Manager', 'ROLE_USER': 'Globalyzer Member'] |
||
+ | // idp configuration |
||
− | === Configuring GzserverConfig.groovy === |
||
+ | grails.plugin.springsecurity.saml.metadata.defaultIdp = '<entity id found in idp.xml>' |
||
− | The bulk of the SAML configuration takes place in the GzserverConfig.groovy file. There is a section in the file dedicated to LDAP configuration. |
||
+ | grails.plugin.springsecurity.saml.metadata.idp.file = 'file:/path/to/idp.xml' |
||
+ | grails.plugin.springsecurity.saml.metadata.providers = ['<your-saml-key>':'file:/path/to/idp.xml'] |
||
+ | // sp configuration |
||
− | Your company's LDAP stores information for each LDAP user. Map the field names defined in LDAP so that Globalyzer knows how to access the user's first name, last name, etc. |
||
+ | grails.plugin.springsecurity.saml.metadata.sp.file = "/path/to/sp.xml" |
||
+ | grails.plugin.springsecurity.saml.metadata.sp.alias = "<entity id found in sp.xml file>" |
||
+ | grails.plugin.springsecurity.saml.metadata.sp.defaults.alias = '<entity id found in sp.xml file>' |
||
+ | grails.plugin.springsecurity.saml.metadata.sp.defaults.entityId = '<entity id found in sp.xml file>' |
||
+ | // true if want token to auto renew when user logs into server |
||
− | // ************************ START OF LDAP CONFIGURATION ********************* |
||
+ | grails.plugin.springsecurity.saml.autoRenewToken = true |
||
− | // ** |
||
− | // ** Uncomment the following 7 gzserver lines and map them to fields in your LDAP |
||
− | // ** If your LDAP does not have the information per user, leave it as empty string |
||
− | // ** NOTE: It is required that LDAP contains an email field for each LDAP user |
||
− | // ** |
||
− | //gzserver.ldap.ctx.firstName = "" |
||
− | //gzserver.ldap.ctx.lastName = "" |
||
− | //gzserver.ldap.ctx.email = "mail" |
||
− | //gzserver.ldap.ctx.phone = "" |
||
− | //gzserver.ldap.ctx.title = "" |
||
− | //gzserver.ldap.ctx.country = "" |
||
− | //gzserver.ldap.ctx.timeZone = "" |
||
+ | // specify number of days until token expires |
||
+ | grails.plugin.springsecurity.saml.renewTokenDays = 90 |
||
+ | === Encrypting SSO Passwords === |
||
− | For example, if the field name for phone is "telephone" in your LDAP, then change the line in GzserverConfig.groovy to this: |
||
+ | To support SSO logins, there are some passwords required in the GzserverConfig.groovy file: |
||
+ | * grails.plugin.springsecurity.saml.keyManager.storePass = 'my plain password' |
||
+ | * grails.plugin.springsecurity.saml.passwords = [samlkey:'my plain password'] |
||
+ | You may encrypt these passwords, rather than having them appear in the config file as plain text. |
||
− | gzserver.ldap.ctx.phone = "telephone" |
||
+ | To encrypt the passwords, you must use the <b>globalyzer-encrypt-password.jar</b> that is available in the Globalyzer-Server.zip file. |
||
− | If your LDAP does not store a telephone number for LDAP users, then leave the line like this: |
||
− | |||
− | gzserver.ldap.ctx.phone = "" |
||
− | |||
− | The only required field is email. Your company's LDAP must store email information for each user, since the email field is required to create a Globalyzer account. |
||
− | |||
− | In addition to user information, your company's LDAP defines groups and group membership. Group membership is used to determine the applications users have access to, as well as the level of access. For Globalyzer, three new groups should be added to your company's LDAP by your company's LDAP administrator: |
||
− | * a Globalyzer admin group |
||
− | * a Globalyzer manager group |
||
− | * a Globalyzer member group |
||
− | |||
− | Users who have access to Globalyzer will be members of one of these three groups. Your LDAP administrator can choose the group names; below is where you map the groups names defined in LDAP. |
||
− | |||
− | // ** |
||
− | // ** Uncomment the following 3 gzserver lines and map them to groups defined in your LDAP |
||
− | // ** The LDAP groups represent globalyzer admin, manager, and member access: |
||
− | // ** |
||
− | //gzserver.ldap.admin.groupName = "GlobalyzerAdmin" |
||
− | //gzserver.ldap.manager.groupName = "GlobalyzerManager" |
||
− | //gzserver.ldap.member.groupName = "GlobalyzerMember" |
||
− | |||
− | Configure the message that displays if the user logging in is NOT a member of one of the Globalyzer groups defined above. |
||
− | |||
− | // ** |
||
− | // ** Uncomment the following gzserver line and configure |
||
− | // ** This text will be displayed to users who are not members of a |
||
− | // ** Globalyzer group |
||
− | // ** |
||
− | //gzserver.ldap.noaccess = "You are not authorized to access Globalyzer." |
||
− | |||
− | Next, configure the LDAP server address and then uncomment the providerNames line, as it is already configured correctly. |
||
− | |||
− | // ** |
||
− | // ** Uncomment the following grails line and set to the address of the LDAP server |
||
− | // ** |
||
− | //grails.plugin.springsecurity.ldap.context.server = 'ldap://localhost:389' |
||
− | |||
− | // ** |
||
− | // ** Uncomment the following grails line; it is already configured properly for LDAP |
||
− | // ** |
||
− | //grails.plugin.springsecurity.providerNames = ['ldapAuthProvider','anonymousAuthenticationProvider'] |
||
− | |||
− | To authenticate LDAP users, Globalyzer connects to your company's LDAP server, logging in via the information provided below. This can be a read only LDAP account and password. |
||
− | |||
− | // ** |
||
− | // ** Uncomment the following two grails lines and set to the DN to authenticate with |
||
− | // ** Globalyzer; will connect the LDAP server and log in via this account |
||
− | // ** <b>See the next section for information on how to encrypt the managerPassword</b> |
||
− | // ** |
||
− | //grails.plugin.springsecurity.ldap.context.managerDn = 'cn=read-only-admin,dc=example,dc=com' |
||
− | //grails.plugin.springsecurity.ldap.context.managerPassword = 'password' |
||
− | |||
− | The rest of the LDAP section of the GzserverConfig.groovy file needs to be configured to perform searches for users and groups. |
||
− | |||
− | // ** |
||
− | // ** Uncomment the following grails line and configure - |
||
− | // ** The base DN from which the search for group membership should be performed |
||
− | // ** |
||
− | //grails.plugin.springsecurity.ldap.authorities.groupSearchBase = 'ou=Groups,dc=example,dc=com' |
||
− | |||
− | // ** |
||
− | // ** Uncomment the following grails line and configure - |
||
− | // ** The ID of the attribute which contains the role name for a group |
||
− | // ** |
||
− | //grails.plugin.springsecurity.ldap.authorities.groupRoleAttribute = 'cn' |
||
− | |||
− | // ** |
||
− | // ** Uncomment the following grails line and configure - |
||
− | // ** The pattern to be used for the user search. {0} is the user's DN |
||
− | // ** |
||
− | //grails.plugin.springsecurity.ldap.authorities.groupSearchFilter = 'member={0}' |
||
− | |||
− | // ** |
||
− | // ** Uncomment the following grails line and configure - |
||
− | // ** Context name to search in |
||
− | // ** |
||
− | //grails.plugin.springsecurity.ldap.search.base = 'dc=example,dc=com' |
||
− | |||
− | // ** |
||
− | // ** Uncomment the following grails line and configure - |
||
− | // ** The filter expression used in the user search |
||
− | // ** |
||
− | //grails.plugin.springsecurity.ldap.search.filter = '(uid={0})' |
||
− | // ************************ END OF LDAP CONFIGURATION ************************* |
||
− | |||
− | === Encrypting the LDAP Password === |
||
− | To support LDAP logins, the Globalyzer Server requires an LDAP account that can connect to your LDAP server and perform reads. |
||
− | As of 6.3, this password may be encrypted, rather that appear in plain text. |
||
− | To encrypt the password, you must use the <b>globalyzer-encrypt-password.jar</b> that is available in the Globalyzer-Server.zip file (starting with the 6.3 release). |
||
Run the jar to generate an encrypted password: |
Run the jar to generate an encrypted password: |
||
Line 225: | Line 141: | ||
Then place the generated password in the GzserverConfig.groovy file within ENC(): |
Then place the generated password in the GzserverConfig.groovy file within ENC(): |
||
− | grails.plugin.springsecurity. |
+ | grails.plugin.springsecurity.saml.keyManager.storePass = 'ENC(CLCjzYV02uZaWDTDkcvK65BndTfUlH5leL00vsgWkmY=)' |
+ | grails.plugin.springsecurity.saml.passwords = [<your-saml-key>:'ENC(CLCjzYV02uZaWDTDkcvK65BndTfUlH5leL00vsgWkmY=)'] |
||
+ | == Extra Configuration for Https == |
||
− | Plain passwords are still supported and are configured like this: |
||
+ | If your server is running under https, in the tomcat server.xml file, you must set the scheme for the Connector to https. |
||
− | grails.plugin.springsecurity.ldap.context.managerPassword = 'my plain password' |
||
+ | For example: |
||
+ | <Connector port="8080" |
||
+ | protocol="HTTP/1.1" |
||
+ | ... |
||
+ | scheme="https" |
||
+ | /> |
||
+ | Or you can configure your reverse proxy to preserve https in the request header. In apache, it would look like this: |
||
− | == Example LDAP Configuration == |
||
+ | RequestHeader add X-Forwarded-Proto https |
||
− | As an example, let's assume that your LDAP has the following directory structure: |
||
+ | == Trouble-Shooting your SSO Configuration == |
||
− | dc=example, dc=com |
||
+ | If you are having difficulty logging in to your SSO-configured Globalyzer Server (login is failing, for example), configure the Globalyzer Server to write more information to the tomcat/temp/gzserver.log file during the login process. This will help in fixing your configuration. |
||
− | ou=Groups |
||
− | cn=GlobalyzerAdmin |
||
− | cn=GlobalyzerManager |
||
− | cn=GlobalyzerMember |
||
− | cn=SomeOtherGroup |
||
− | ou=Users |
||
− | uid=mheilner |
||
− | uid=lcameron |
||
− | uid=olibouban |
||
− | |||
− | The configuration for this is shown below: |
||
− | |||
− | grails.plugin.springsecurity.ldap.authorities.groupSearchBase = 'ou=Groups,dc=example,dc=com' |
||
− | grails.plugin.springsecurity.ldap.authorities.groupRoleAttribute = 'cn' |
||
− | grails.plugin.springsecurity.ldap.authorities.groupSearchFilter = 'member={0}' |
||
− | grails.plugin.springsecurity.ldap.search.base = 'dc=example,dc=com' |
||
− | grails.plugin.springsecurity.ldap.search.filter = '(uid={0})' |
||
− | |||
− | == Trouble-Shooting your LDAP Configuration == |
||
− | If you are having difficulty logging in to your LDAP-configured Globalyzer Server (login is failing, for example), configure the Globalyzer Server to write more information to the tomcat/temp/gzserver.log file during the login process. This will help in fixing your configuration. |
||
To do this, place the logback-debug.groovy file (delivered in the Globalyzer Server zip file) to a location on your server. Then add <b>-Dlogging.config</b> to your JAVA_OPTS in your enterprise.sh/bat script to use this file. |
To do this, place the logback-debug.groovy file (delivered in the Globalyzer Server zip file) to a location on your server. Then add <b>-Dlogging.config</b> to your JAVA_OPTS in your enterprise.sh/bat script to use this file. |
||
Line 265: | Line 169: | ||
Note, if you are running Tomcat as a service rather than starting/stopping using the enterprise script, then update your JAVA_OPTS environment variable on the Server machine and then restart the Tomcat service. |
Note, if you are running Tomcat as a service rather than starting/stopping using the enterprise script, then update your JAVA_OPTS environment variable on the Server machine and then restart the Tomcat service. |
||
− | == |
+ | == What Differences Will I see Using SSO? == |
+ | When an SSO server has been successfully configured and launched, you will see these changes. |
||
− | When logging in to the Globalyzer Server or Client, the user will enter in their LDAP username and password. Globalyzer logs in to the LDAP server using the configured <code>managerDN</code> and <code>managerPassword</code>. A search is performed to authenticate the LDAP user as entered on the login screen. The search for this user will begin at the configured <code>ldap.search.base</code> and use the configured <code>ldap.search.filter</code>. If the user is not found, or the password is incorrect, the login will fail. |
||
− | |||
− | If the user is a valid LDAP user, then a search for the groups the user belongs to is performed. This group search begins at the configured <code>groupSearchBase</code>. Groups are identified in LDAP by the configured <code>groupRoleAttribute</code>. It uses the configured <code>groupSearchFilter</code> to determine if the found user is a member of the group. Users may be members of several groups across LDAP, but only one Globalyzer group. |
||
− | |||
− | If logging into the server, and the user is authenticated (user is a valid LDAP user) but the user does not belong to one of the three Globalyzer groups (admin, manager, or member), the user will be logged in, but won't be able to perform any actions, since not authorized to do so. |
||
− | |||
− | If logging into the client, and the user is authenticated (user is a valid LDAP user) but the user does not belong to one of the three Globalyzer groups, login will fail. |
||
− | |||
− | On initial login, if the user is authenticated (user is valid LDAP user) and authorized (user belongs to one of the three Globalyzer groups), a Globalyzer account is created at the appropriate access level. |
||
− | |||
− | On subsequent logins, if the user is authenticated (user is a valid LDAP user) and authorized (user belongs to one of the three Globalyzer groups), the existing server account is then updated with the latest information in LDAP, except for the level of access. If the user was authorized to be a Globalyzer Manager when the account was created, the user will always be a Globalyzer Manager. Switching the user to a different access level requires that the Globalyzer account be deleted (by a Globalyzer Manager or Member), and then on login, the Globalyzer account will be recreated at the current access level as configured in LDAP. |
||
− | |||
− | == What Differences Will I see Using LDAP? == |
||
− | When an LDAP server has been successfully configured and launched, you will see these changes. |
||
Server changes: |
Server changes: |
||
− | * On server login screen, |
+ | * On server login screen, an SSO login button displays, rather than Email and Password |
* On server login screen, <b>Forgot Password</b> link is removed |
* On server login screen, <b>Forgot Password</b> link is removed |
||
* Admin users can no longer create other Admins, Managers, or Members |
* Admin users can no longer create other Admins, Managers, or Members |
||
* Manager users can no longer create other Managers or Members |
* Manager users can no longer create other Managers or Members |
||
* No users can edit their profile |
* No users can edit their profile |
||
− | * When an |
+ | * When an SSO user initially logs in to the server, a server account will be created if they were authenticated by the Identity Provider and authorized (by belonging to one of the three Globalyzer groups) |
− | * If user is NOT authenticated |
+ | * If user is NOT authenticated or authorized, login will fail |
+ | * On subsequent logins, the user's server account is updated with the latest information from the Identity Provider, EXCEPT for their level of access. If they were authorized to be a Globalyzer Manager when their account was created, they will always be a Globalyzer Manager. An existing Manager account will not be switched to a Member account or an Admin account, for example. The Manager account can be deleted from the Globalyzer server (by another Manager or Admin), and then on login, the account will be recreated at the current access level as configured in the Identity Provider. |
||
− | * If user is authenticated by LDAP, but not authorized (via group membership), a screen appears saying they are not authorized to access Globalyzer |
||
− | * On subsequent logins, the user is first authenticated (account is validated against LDAP) and authorized (if Globalyzer group member). Their existing server account is then updated with the latest information in LDAP, EXCEPT for their level of access. If they were authorized to be a Globalyzer Manager when their account was created, they will always be a Globalyzer Manager. An existing Manager account will not be switched to a Member account or an Admin account, for example. The Manager account can be deleted from the Globalyzer server (by another Manager or Admin), and then on login, the account will be recreated at the current access level as configured in LDAP. |
||
Client Workbench changes: |
Client Workbench changes: |
||
− | * Forgot Password link still displays (since clients can connect to various servers) but if they are connected to an |
+ | * Forgot Password link still displays (since clients can connect to various servers) but if they are connected to an SSO-configured server, a message displays saying that the password cannot be retrieved from an SSO-configured server |
+ | |||
− | * When LDAP users initially log in to the client (haven't logged in to server yet), a server account will be created for them if they are authenticated by LDAP and authorized (by belonging to one of the three Globalyzer groups). |
||
+ | Client access to an SSO-configured Server: |
||
− | * If user is NOT authenticated by LDAP, login will fail. |
||
+ | * To run a Globalyzer Client (Workbench, Lite) against an SSO-configured Server, you need to generate a token from the Server (click on the <b>download Globalyzer Client here</b> link at the bottom of the home screen) and use your email address as the username and the token as the password when connecting to the server. |
||
− | * If user is authenticated by LDAP, but not authorized (via group membership), login will fail. |
||
+ | * Tokens expire in 90 days, but the number of days is configurable in the GzserverConfig.groovy file. |
||
+ | * Tokens may be auto renewed by logging into the server. This feature is configurable in GzserverConfig.groovy. |
||
+ | * Attempting to log in with expired tokens will fail. |
Latest revision as of 22:19, 31 August 2023
Contents
Overview
Many companies use SAML SSO with an Identity Provider to manage users and access to applications. To integrate Globalyzer with SAML SSO, first, the Identity Provider must be configured to allow access to Globalyzer. Then, Globalyzer must be configured for SSO. The result is three key files referenced from GzserverConfig.groovy
- a keystore that contains the identity provider certificate and a key
- the idp.xml file that describes the identity provider (Okta in our example)
- the sp.xml file that describes the service provider (our Globalyzer application)
Configure the Identity Provider
We will be using Okta as the Identity Provider in order to illustrate how to configure Globalyzer.
Set up Okta Developer Account
- https://developer.okta.com/signup/
- Enter Admin mode
Create Globalyzer Groups/People
- Click Directory->Groups on left
- Create Globalyzer Admin group
- Create Globalyzer Manager group
- Create Globalyzer Member group
- Choose Directory->People on left
- Add accounts and assign to appropriate Globalyzer Groups
Create Okta Application
- Click Applications->Applications on the left.
- Click Create App Integration
- Choose SAML 2.0 and then Next
- Give your app a name and click Next
- Single sign on URL: <your server machine>/gzserver/login/saml2/sso/<your-saml-key>, for example https://saml.lingoport.net/gzserver/login/saml2/sso/gzkey
- Audience URI: <your server machine>/gzserver/saml2/service-provider-metadata/<your-saml-key>, for example https://saml.lingoport.net/gzserver/saml2/service-provider-metadata/gzkey
- Attributes Section: enter in the following:
First Name, Unspecified, user.firstName Last Name, Unspecified, user.lastName Email, Unspecified, user.email
- Groups Section: enter in the following:
memberOf, Unspecified, Contains, Globalyzer
- Select I'm an Okta customer adding an internal app
- Check This is an internal app that we have created
- Go to Assignments tab
- Assign the three Globalyzer groups to your app
Download Artifacts
- Go to Sign On tab of your app
- Click View SAML setup instructions
- Download certificate
- Copy IDP Metadata to a file named idp.xml
Generate Keys and Keystore
- Generate key and keystore:
keytool -genkey -alias <your-saml-key> -keyalg RSA -keystore <your-key-store.jks>
- Accept Identity Provider Certficate
keytool -import -alias okta -keystore <your-key-store.jks> -file <certificate you downloaded from okta>
Generate sp.xml
- Create a file named sp.xml with the following contents
<?xml version="1.0" encoding="UTF-8"?> <md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://saml.lingoport.net/gzserver/saml2/service-provider-metadata/<your-saml-key>"> <md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> <md:Extensions><idpdisco:DiscoveryResponse xmlns:idpdisco="urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol" Binding="urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol" Location="https://saml.lingoport.net/gzserver/login/auth?disco=true"/> </md:Extensions><md:KeyDescriptor use="signing"> <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:X509Data> <ds:X509Certificate>CERTIFICATE</ds:X509Certificate> </ds:X509Data></ds:KeyInfo></md:KeyDescriptor> <md:KeyDescriptor use="encryption"> <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:X509Data> <ds:X509Certificate>CERTIFICATE</ds:X509Certificate> </ds:X509Data></ds:KeyInfo> </md:KeyDescriptor> <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://saml.lingoport.net/gzserver/saml/SingleLogout"/> <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://saml.lingoport.net/gzserver/saml/SingleLogout"/> <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat> <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat> <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat> <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat> <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName</md:NameIDFormat> <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://saml.lingoport.net/gzserver/login/saml2/sso/<your-saml-key>" index="0" isDefault="true"/> <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="https://saml.lingoport.net/gzserver/login/saml2/sso/<your-saml-key>" index="1" isDefault="false"/> <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:PAOS" Location="https://saml.lingoport.net/gzserver/login/saml2/sso/<your-saml-key>" index="2" isDefault="false"/> </md:SPSSODescriptor> </md:EntityDescriptor>
- Modify entityId to match what you specified as Audience in your Okta app.
- Replace the two CERTIFICATEs with the certificate you downloaded from Okta. Open the file and grab the lines between BEGIN CERTIFICATE and END CERTIFICATE in the downloaded file.
- Update the various Locations to be the machine your Globalyzer Server is running on ... keeping the /gzserver/login/saml2/sso/<your-saml-key> or /gzserver/saml/SingleLogout endings
Configure GzserverConfig.groovy
- Copy <your-key-store.jks>, sp.xml, and idp.xml files to a specific location on the machine running the Globalyzer Server.
- Add and configure the following lines to GzserverConfig.groovy:
// tell Globalyzer and the Plugin to use saml gzserver.saml.mode = true grails.plugin.springsecurity.saml.active = true grails.plugin.springsecurity.providerNames = ['samlAuthenticationProvider','anonymousAuthenticationProvider'] grails.plugin.springsecurity.saml.loginFormUrl = '/saml2/authenticate/<your-saml-key>'
// keystore configuration // assuming you created a keystore named saml-keystore.jks and a key named samlkey ... grails.plugin.springsecurity.saml.keyManager.storeFile = "file:/path/to/<your-key-store.jks>" grails.plugin.springsecurity.saml.keyManager.storePass = '<your-keystore-pw>' grails.plugin.springsecurity.saml.keyManager.passwords = [<your-saml-key>:'<your-keystore-pw>'] grails.plugin.springsecurity.saml.keyManager.defaultKey = '<your-saml-key>' grails.plugin.springsecurity.saml.metadata.sp.defaults.signingKey = '<your-saml-key>' grails.plugin.springsecurity.saml.metadata.sp.defaults.encryptionKey = '<your-saml-key>' grails.plugin.springsecurity.saml.metadata.sp.defaults.tlsKey = '<your-saml-key>'
// leave as is if created okta app as specified above grails.plugin.springsecurity.saml.userGroupAttribute = 'memberOf' grails.plugin.springsecurity.saml.userAttributeMappings = ['username' : 'Email', 'firstName': 'First Name', 'lastName' : 'Last Name'] grails.plugin.springsecurity.saml.userGroupToRoleMapping = ['ROLE_ADMIN': 'Globalyzer Admin', 'ROLE_MANAGER': 'Globalyzer Manager', 'ROLE_USER': 'Globalyzer Member']
// idp configuration grails.plugin.springsecurity.saml.metadata.defaultIdp = '<entity id found in idp.xml>' grails.plugin.springsecurity.saml.metadata.idp.file = 'file:/path/to/idp.xml' grails.plugin.springsecurity.saml.metadata.providers = ['<your-saml-key>':'file:/path/to/idp.xml']
// sp configuration grails.plugin.springsecurity.saml.metadata.sp.file = "/path/to/sp.xml" grails.plugin.springsecurity.saml.metadata.sp.alias = "<entity id found in sp.xml file>" grails.plugin.springsecurity.saml.metadata.sp.defaults.alias = '<entity id found in sp.xml file>' grails.plugin.springsecurity.saml.metadata.sp.defaults.entityId = '<entity id found in sp.xml file>'
// true if want token to auto renew when user logs into server grails.plugin.springsecurity.saml.autoRenewToken = true
// specify number of days until token expires grails.plugin.springsecurity.saml.renewTokenDays = 90
Encrypting SSO Passwords
To support SSO logins, there are some passwords required in the GzserverConfig.groovy file:
- grails.plugin.springsecurity.saml.keyManager.storePass = 'my plain password'
- grails.plugin.springsecurity.saml.passwords = [samlkey:'my plain password']
You may encrypt these passwords, rather than having them appear in the config file as plain text.
To encrypt the passwords, you must use the globalyzer-encrypt-password.jar that is available in the Globalyzer-Server.zip file.
Run the jar to generate an encrypted password:
$ java -jar globalyzer-encrypt-password.jar -in "my plain password" Encrypted Password: CLCjzYV02uZaWDTDkcvK65BndTfUlH5leL00vsgWkmY=
Then place the generated password in the GzserverConfig.groovy file within ENC():
grails.plugin.springsecurity.saml.keyManager.storePass = 'ENC(CLCjzYV02uZaWDTDkcvK65BndTfUlH5leL00vsgWkmY=)' grails.plugin.springsecurity.saml.passwords = [<your-saml-key>:'ENC(CLCjzYV02uZaWDTDkcvK65BndTfUlH5leL00vsgWkmY=)']
Extra Configuration for Https
If your server is running under https, in the tomcat server.xml file, you must set the scheme for the Connector to https. For example:
<Connector port="8080" protocol="HTTP/1.1" ... scheme="https" />
Or you can configure your reverse proxy to preserve https in the request header. In apache, it would look like this:
RequestHeader add X-Forwarded-Proto https
Trouble-Shooting your SSO Configuration
If you are having difficulty logging in to your SSO-configured Globalyzer Server (login is failing, for example), configure the Globalyzer Server to write more information to the tomcat/temp/gzserver.log file during the login process. This will help in fixing your configuration.
To do this, place the logback-debug.groovy file (delivered in the Globalyzer Server zip file) to a location on your server. Then add -Dlogging.config to your JAVA_OPTS in your enterprise.sh/bat script to use this file.
For example, the modified enterprise.bat would look like this:
set "JAVA_OPTS=-Xms256m -Xmx1600m -Dstringchararrayaccessor.disabled=true -Dlogging.config=C:\path\to\logback-debug.groovy"
Then stop and start your Globalyzer Server to incorporate the changes. You should now see more information written to the gzserver.log file.
Note, if you are running Tomcat as a service rather than starting/stopping using the enterprise script, then update your JAVA_OPTS environment variable on the Server machine and then restart the Tomcat service.
What Differences Will I see Using SSO?
When an SSO server has been successfully configured and launched, you will see these changes.
Server changes:
- On server login screen, an SSO login button displays, rather than Email and Password
- On server login screen, Forgot Password link is removed
- Admin users can no longer create other Admins, Managers, or Members
- Manager users can no longer create other Managers or Members
- No users can edit their profile
- When an SSO user initially logs in to the server, a server account will be created if they were authenticated by the Identity Provider and authorized (by belonging to one of the three Globalyzer groups)
- If user is NOT authenticated or authorized, login will fail
- On subsequent logins, the user's server account is updated with the latest information from the Identity Provider, EXCEPT for their level of access. If they were authorized to be a Globalyzer Manager when their account was created, they will always be a Globalyzer Manager. An existing Manager account will not be switched to a Member account or an Admin account, for example. The Manager account can be deleted from the Globalyzer server (by another Manager or Admin), and then on login, the account will be recreated at the current access level as configured in the Identity Provider.
Client Workbench changes:
- Forgot Password link still displays (since clients can connect to various servers) but if they are connected to an SSO-configured server, a message displays saying that the password cannot be retrieved from an SSO-configured server
Client access to an SSO-configured Server:
- To run a Globalyzer Client (Workbench, Lite) against an SSO-configured Server, you need to generate a token from the Server (click on the download Globalyzer Client here link at the bottom of the home screen) and use your email address as the username and the token as the password when connecting to the server.
- Tokens expire in 90 days, but the number of days is configurable in the GzserverConfig.groovy file.
- Tokens may be auto renewed by logging into the server. This feature is configurable in GzserverConfig.groovy.
- Attempting to log in with expired tokens will fail.