Thomas Draebing | 16bd87e | 2021-03-18 10:11:10 +0100 | [diff] [blame] | 1 | --- |
| 2 | title: "Design Doc - Case Insensitive Username Matching - Use Cases" |
| 3 | permalink: design-docs/case-insensitive-username-matching-use-cases.html |
| 4 | hide_sidebar: true |
| 5 | hide_navtoggle: true |
| 6 | toc: false |
| 7 | --- |
| 8 | |
| 9 | # Use Cases |
| 10 | |
| 11 | ## <a id="definitions"> Definitions |
| 12 | |
| 13 | * duplicate usernames/accounts: Usernames or accounts having usernames that are |
| 14 | only different in their capitalization. |
| 15 | E.g.: `johndoe` and `JohnDoe`. While no true duplicates, they will be referred |
| 16 | to as duplicates for readability. |
| 17 | * external IDs: Gerrit uses (generic) user names in different externalId schemes |
| 18 | which are used for different authentication systems. |
| 19 | |
| 20 | Currently the following externalId schemes exist (defined in |
| 21 | [ExternalId.java](https://gerrit.googlesource.com/gerrit/+/refs/heads/master/java/com/google/gerrit/server/account/externalids/ExternalId.java#114)): |
| 22 | |
| 23 | * `gerrit` (LDAP) |
| 24 | * `username` (authentication REST and git endpoints) |
| 25 | * `external` (external authentication e.g. oauth, saml) |
| 26 | * `gpgkey` (gpg keys) |
| 27 | * `mailto` (email addresses) |
| 28 | * `uuid` (randomly created identities constructed by a UUID) |
| 29 | * `http` (used by OpenID) |
| 30 | * `https` (used by OpenID) |
| 31 | * `xri` (used by OpenID) |
| 32 | |
| 33 | The schemes relevant for authentication are: `gerrit`, `username`. |
| 34 | |
| 35 | ## <a id="primary"> Primary Use Cases |
| 36 | |
| 37 | * Gerrit administrators can migrate between LDAP- and SAML identity providers |
| 38 | and from LDAP-based accounts to accounts managed by Gerrit, regardless of |
| 39 | handling of capitalization of usernames in the different systems. |
| 40 | |
| 41 | ## <a id="secondary"> Secondary Use Cases |
| 42 | |
| 43 | * Prohibit usernames only different in capitalization to make it easier for |
| 44 | humans to unequivocally identify users by their username. |
| 45 | |
| 46 | ## <a id="acceptance-criteria"> Acceptance Criteria |
| 47 | |
| 48 | * An administrator should be able to prohibit the creation of accounts with |
| 49 | usernames that are only different in capitalization. |
| 50 | * A tool that allows Gerrit administrators to identify existing duplicate |
| 51 | accounts should be available. Already implemented |
| 52 | in [change 300308](https://gerrit-review.googlesource.com/c/gerrit/+/300308). |
| 53 | * The process of dealing with duplicates by either deletion of the external IDs |
| 54 | or changing of the username by the administrator should be properly documented |
| 55 | and facilitated by Gerrit tools. |
| 56 | * Gerrit should provide an option to allow clients to be able to login with a |
| 57 | username using any capitalization they prefer, i.e. username matching during |
| 58 | authentication should be case insensitive. It might be considered to be the |
| 59 | default in the future. |
| 60 | * Display names of accounts using the username as a default display name should |
| 61 | use the username as presented during account creation, i.e. with the original |
| 62 | capitalization. |
| 63 | E.g. if `JohnDoe` is used during account registration this form should appear |
| 64 | if mentioned in the UI, regardless of how the username is represented internally. |
| 65 | * Existing accounts, except for duplicates, should not be disrupted by introducing |
| 66 | this change. |
| 67 | * Gerrit administrators should have a straightforward way of migrating to other |
| 68 | identity providers without disrupting users, e.g. from LDAP to SAML or LDAP |
| 69 | to Gerrit-serviceuser. |
| 70 | |
| 71 | ## <a id="background"> Background |
| 72 | |
| 73 | Gerrit by default treats usernames case sensitive, i.e. there can be the usernames |
| 74 | `johndoe` and `JohnDoe` identifying different accounts. There are currently options in |
| 75 | Gerrit's configuration influencing that aim to achieve case insensitivity |
| 76 | by storing and matching usernames in all lowercase, regardless of the capitalization |
| 77 | used by the client. However, these options don't allow to handle accounts with |
| 78 | mixed- or uppercase usernames created before being set and currently do not |
| 79 | handle all ways to create accounts in Gerrit. Gerrit provides a tool to convert |
| 80 | usernames stored in the `gerrit` external ID to all lowercase to solve part of |
| 81 | the issue. However, this misses accounts created in Gerrit itself, which use the |
| 82 | `username` external ID. The `username` external ID cannot be converted to all |
| 83 | lowercase, since this would break the sandbox branches feature, which uses the |
| 84 | username in the branch name (e.g. `refs/heads/sandbox/${username}/*`). |
| 85 | |
| 86 | The existing behavior can lead to issues, when trying to migrate to a different |
| 87 | identity provider. The scenario encountered by SAP can be taken as an example: |
| 88 | The identity provider is supposed to be switched from LDAP to SAML. This has to |
| 89 | happen with minimal disruption for the users, i.e. the username should not change. |
| 90 | The Gerrit instance has three types of users: |
| 91 | |
| 92 | * Internal technical users (created via REST API or the `create-account` SSH command |
| 93 | or via the serviceuser-plugin) |
| 94 | * Technical users managed by LDAP |
| 95 | * Human users managed by LDAP |
| 96 | |
| 97 | Usernames of accounts managed by LDAP are stored in all lowercase |
| 98 | (`ldap.localUsernameToLowerCase = true`). Other account types are allowed to |
| 99 | use uppercase letters in the username stored in NoteDB. Some usernames are |
| 100 | duplicates. The new identity provider does not allow technical users, thus |
| 101 | technical users managed by LDAP were registered as service users, so that they |
| 102 | can be used as internal accounts managed by the serviceuser plugin. |
| 103 | |
| 104 | The saml-plugin uses the `auth.userNameToLowerCase`-option to convert the username |
| 105 | used to login to lowercase. This setting blocks all internal technical users |
| 106 | and service users with uppercase letters in the username, e.g. the service user |
| 107 | `JenkinsBuild` will not be able to authenticate, since during authentication the |
| 108 | username is converted to `jenkinsbuild`, which is not present in NoteDB, where |
| 109 | the external ID `username:JenkinsBuild` is present. This can be fixed by |
| 110 | adding an option complimentary to `ldap.localUsernameToLowerCase` to the saml- |
| 111 | plugin. However, with such an option the newly registered service users (former |
| 112 | LDAP-based technical users) would not able to log in, if the usernames have |
| 113 | uppercase letters, since this does not match the username in NoteDB that is all |
| 114 | lowercase. |
| 115 | |
| 116 | To illustrate, a list of example scenarios follows: |
| 117 | |
| 118 | * LDAP scenario (current state) |
| 119 | * `ldap.localUsernameToLowerCase = true` |
| 120 | * `auth.userNameToLowerCase = false` |
| 121 | |
| 122 | | user type | username used by user | username in NoteDB | username used in DB lookup | login successful | |
| 123 | |----------------------------|-----------------------|--------------------|----------------------------|------------------| |
| 124 | | LDAP (human) | johndoe | johndoe | johndoe | true | |
| 125 | | LDAP (human) | JohnDoe | johndoe | johndoe | true | |
| 126 | | LDAP (technical) | johndoe | johndoe | johndoe | true | |
| 127 | | LDAP (technical) | JohnDoe | johndoe | johndoe | true | |
| 128 | | internal user/service user | johndoe | johndoe | johndoe | true | |
| 129 | | internal user/service user | JohnDoe | JohnDoe | JohnDoe | true | |
| 130 | |
| 131 | * SAML scenario 1 |
| 132 | * technical users (internal users and LDAP-managed technical users) are registered as service users |
| 133 | * `auth.userNameToLowerCase = false` |
| 134 | |
| 135 | | user type | username used by user | username in NoteDB | username used in DB lookup | login successful | |
| 136 | |----------------------------|-----------------------|--------------------|----------------------------|------------------| |
| 137 | | SAML (human) | johndoe | johndoe | johndoe | true | |
| 138 | | SAML (human) | JohnDoe | johndoe | JohnDoe | false | |
| 139 | | formerly LDAP (technical) | johndoe | johndoe | johndoe | true | |
| 140 | | formerly LDAP (technical) | JohnDoe | johndoe | JohnDoe | false | |
| 141 | | internal user/service user | johndoe | johndoe | johndoe | true | |
| 142 | | internal user/service user | JohnDoe | JohnDoe | JohnDoe | true | |
| 143 | |
| 144 | * SAML scenario 2 |
| 145 | * technical users (internal users and LDAP-managed technical users) are registered as service users |
| 146 | * `auth.userNameToLowerCase = true` |
| 147 | |
| 148 | | user type | username used by user | username in NoteDB | username used in DB lookup | login successful | |
| 149 | |----------------------------|-----------------------|--------------------|----------------------------|------------------| |
| 150 | | SAML (human) | johndoe | johndoe | johndoe | true | |
| 151 | | SAML (human) | JohnDoe | johndoe | johndoe | true | |
| 152 | | formerly LDAP (technical) | johndoe | johndoe | johndoe | true | |
| 153 | | formerly LDAP (technical) | JohnDoe | johndoe | johndoe | true | |
| 154 | | internal user/service user | johndoe | johndoe | johndoe | true | |
| 155 | | internal user/service user | JohnDoe | JohnDoe | johndoe | false | |
| 156 | |
| 157 | * SAML scenario 3 |
| 158 | * technical users (internal users and LDAP-managed technical users) are registered as service users |
| 159 | * `auth.userNameToLowerCase = true` |
| 160 | * `saml.localUsernameToLowerCase = true` (not yet implemented) |
| 161 | |
| 162 | | user type | username used by user | username in NoteDB | username used in DB lookup | login successful | |
| 163 | |----------------------------|-----------------------|--------------------|----------------------------|------------------| |
| 164 | | SAML (human) | johndoe | johndoe | johndoe | true | |
| 165 | | SAML (human) | JohnDoe | johndoe | johndoe | true | |
| 166 | | formerly LDAP (technical) | johndoe | johndoe | johndoe | true | |
| 167 | | formerly LDAP (technical) | JohnDoe | johndoe | johndoe | true | |
| 168 | | internal user/service user | johndoe | johndoe | johndoe | true | |
| 169 | | internal user/service user | JohnDoe | JohnDoe | johndoe | false | |
| 170 | |
| 171 | * SAML scenario 4 |
| 172 | * technical users (internal users and LDAP-managed technical users) are registered as service users |
| 173 | * `auth.userNameToLowerCase = false` |
| 174 | * `saml.localUsernameToLowerCase = true` (not yet implemented) |
| 175 | |
| 176 | | user type | username used by user | username in NoteDB | username used in DB lookup | login successful | |
| 177 | |----------------------------|-----------------------|--------------------|----------------------------|------------------| |
| 178 | | SAML (human) | johndoe | johndoe | johndoe | true | |
| 179 | | SAML (human) | JohnDoe | johndoe | johndoe | true | |
| 180 | | formerly LDAP (technical) | johndoe | johndoe | johndoe | true | |
| 181 | | formerly LDAP (technical) | JohnDoe | johndoe | JohnDoe | false | |
| 182 | | internal user/service user | johndoe | johndoe | johndoe | true | |
| 183 | | internal user/service user | JohnDoe | JohnDoe | johndoe | true | |
| 184 | |
| 185 | |
| 186 | While this example uses non-core plugins and is very specific, it shows that |
| 187 | having different accounts with usernames only different in capitalization can |
| 188 | lead to hard to resolve issues or disruption of users. Further, there is likely |
| 189 | no good use case to support case sensitive usernames. Usernames only different |
| 190 | in capitalization might rather lead to confusion in identifying a user. Thus, |
| 191 | account data could be made easier to manage by matching accounts case insensitive |
| 192 | during authentication. |