morincer / teamcity-plugin-saml

The plug-in adds ability to authenticate users by SAML-based SSO providers (like Okta, Onelogin etc.)
MIT License
25 stars 16 forks source link

TeamCity SAML Authentication Plug-In

Java CI with Maven

Overview

The plug-in adds ability to authenticate users with SAML-based SSO providers (like Okta, Onelogin etc.).

The plug-in offers the following functionality:

Things the plug-in does NOT currently support (but they're on the roadmap):

Login Screen

Configurations

Installation

Grab the latest release follow the TeamCity installation instructions (google).

Configuration

The plugin has been tested with Okta and Onelogin but should work fine with other SAML v2 Identity Providers.

Add Module

Edit Settings

Please refer the example of set up for Okta if you need some details.

The SAML authentication sequence is a following:

  1. When you click the "Login with SSO" button you are redirected to your IdP login page
  2. You enter credentials on the login screen, your IdP validates them and, if all is fine, posts a signed SAML-assertion XML to the SSO login callback (/app/saml/callback/)
  3. The assertion contains name ID of the user (usually - e-mail). Plugin searches for users having the same username (not e-mail!) and, if the user is found, authenticates the request.

Automatic Users Creation and Custom Attributes Mapping

You have an option to create users automatically upon first successful login and provision their data basing on SAML assertion attributes.

In this case you must explicitly specify the source for user full name, VCS username, e-mail and groups. Valid options are:

To reference an attribute in the expression you can either use it's key directly (if the attribute name conforms to standard Java variable naming conventions) or by calling get('attribute name') function, which effectively does the same.

Note

If the expression evaluation fails due to any reason (wrong syntax, mentioned attribute is missing from request) the user is stil being created but the respective element is considered to be empty.

However, to ease the troubleshooting of such cases, the plugin puts into the server log:

  1. Error message describing exception that lead to such behavior
  2. Warning listing all available attributes.

For instance you will see something like lines below:

[2022-01-11 17:18:16,702]  ERROR -     jetbrains.buildServer.AUTH - EL1008E: Property or field 'fullName' cannot be found on object of type 'java.util.HashMap' - maybe not public or not valid?
org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'fullName' cannot be found on object of type 'java.util.HashMap' - maybe not public or not valid?
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:229)
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:97)
    ... stack trace...

[2022-01-11 17:18:16,703]   WARN -     jetbrains.buildServer.AUTH - Available properties are: lastassertionid, FullName, nameid

Expression Examples:

Concatenate firstName and lastName

Assuming we've got custom attributes named firstName and lastName in SAML assertion.

firstName + ' ' + lastName

Concatenate firstName and lastName using get

Assuming we've got custom attributes named First Name and Last Name in SAML assertion.

get('First Name') + ' ' + get('Last Name')

Group mapping

The plugin can optionally support adding users to TeamCity groups based on IdP group membership.

The mapping between IdP group and TeamCity group is perfomed on the TeamCity groups Key value, with the matching being case insensitive.

See the Okta Group Mapping doc for an example of how to set this up.

Secondary Node Authentication Support

The plugin supports execution on secondary server instances (nodes) (read official documentation for details). However, few limitations apply:

  1. You are not allowed to change the plugin settings from the secondary node admin UI (as per JetBrains recommendation)
  2. You should manually configure IdP to send SAML assertion to the secondary node URL (or deal with redirection on the load balancer/proxy level)

Examples

Troubleshooting

Additional Logging

General troubleshooting step is to enable debug auth logging in Teamcity and check the log output for possible clues.

You may use pre-crafted logging preset: just import it into your TC (Server Administration -> Diagnostics -> Logging Presets tab) and switch the logging profile (Server Administration -> Diagnostics -> Troubleshooting tab). This preset is naturally just a copy of standard debud-auth.xml preset + com.onelogin.saml2 debug logging.

The log file would be named "teamcity-auth.log" and in most situations it will give you some ideas on what is wrong.

Invalid_response error on login

This is the most common issue and its root cause varies from wrong setup on IdP side to wrong networking configuration.

Possible solutions:

...Deploy it

mvn tc-sdk:reload

Admin UI Development with Vue

The plugin's admin UI is built using Vue.js framework with Typescript and therefore supports standard Vue-cli-based toolchain. The Vue part is located in the vue/admin-ui sub-folder of the plugin and contains it's own package.json file with all the needed dependencies and basic build commands.

To start the built-in Vue server just use

npm run serve 

And navigate to http://localhost:8080 (to see the admin UI as a standalone controls) or http://localhost:8080/#/demo - to see it in the Teamcity-like design.