Merge branch 'stable-2.15'
* stable-2.15:
Fix typo in README.md
SAML providers: put proper links to SAML providers config
Documentation: move build instructions into README
SamlWebFilter: Fix AnonymousHttpRequest.getHeaderNames method
Change-Id: I99f0c1df34a88a83f89241b42b53c7f57d2ee09f
diff --git a/README.md b/README.md
index c5d1a3b..13a0625 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# Gerrit SAML Plugin
+# Gerrit SAML Authentication Filter
-This plugin allows you to authenticate to Gerrit using a SAML identity
+This filter allows you to authenticate to Gerrit using a SAML identity
provider.
## Installation
@@ -14,35 +14,65 @@
If any of these attributes is not found in the assertion, their value is
taken from the NameId field of the SAML assertion.
-### Setting Gerrit in your IdP (Okta, Onelogin, ...)
+### Setting Gerrit in your IdP
-- Create a new SAML 2.0 application.
-- Set the following parameters:
- - Single sign on URL: http://gerrit.site.com/plugins/saml/callback
- - Check "Use this for Recipient URL and Destination URL".
- - Audience URI (SP Entity Id): http://gerrit.site.com/plugins/saml/callback
- - We need to set up the attributes in the assertion to send the right
- information. Here is how to do it with Okta:
- - Application username: "Okta username prefix"
- - Add attribute statement: Name: "DisplayName" with Value
- "user.displayName"
- - Add attribute statement: Name: "EmailAddress" with Value
- "user.email"
- - **IMPORTANT**: If you are not using Okta, you need to set up an attribute
- "UserName" with the value of the username (not email, without @). If you
- do not do so, the name will be taken from the NameId provided by
- the assertion. This is why in Okta we set the application username to
- "Okta username prefix".
-- Obtain your IdP metadata (either URL or a local XML file)
-
-If you are using Active Directory Federation Services (ADFS), follow the below steps to configure Gerrit.
-You can then [go here](doc/Setup_ADFS.md) for more details on how to make the saml plugin work with ADFS.
+- [Okta](okta/README.md)
+- [Keycloak](keycloak/README.md)
+- [ADFS](adfs/README.md)
### Download the plugin
Download Gerrit SAML plugin for the appropriate version of gerrit from the [Gerrit-CI](https://gerrit-ci.gerritforge.com/search/?q=saml)
into $gerrit_site/lib/.
+### Building the SAML filter
+
+This authentication filter is built with Bazel.
+
+## Build in Gerrit tree
+
+Clone or link this filter to the plugins directory of Gerrit's
+source tree. Put the external dependency Bazel build file into
+the Gerrit /plugins directory, replacing the existing empty one.
+
+```
+ cd gerrit/plugins
+ rm external_plugin_deps.bzl
+ ln -s @PLUGIN@/external_plugin_deps.bzl .
+```
+
+Then issue
+
+```
+ bazel build plugins/@PLUGIN@
+```
+
+The output is created in
+
+```
+ bazel-genfiles/plugins/@PLUGIN@/@PLUGIN@.jar
+```
+
+The @PLUGIN@.jar should be deployed to `gerrit_site/lib` directory:
+
+```
+ cp bazel-genfiles/plugins/@PLUGIN@/@PLUGIN@.jar `$gerrit_site/lib`
+```
+
+__NOTE__: Even though the project is built as a Gerrit plugin, it must be loaded
+as a Servlet filter by Gerrit and thus needs to be located with the libraries and
+cannot be dynamically loaded like other plugins.
+
+This project can be imported into the Eclipse IDE.
+Add the plugin name to the `CUSTOM_PLUGINS` set in
+Gerrit core in `tools/bzl/plugins.bzl`, and execute:
+
+```
+ ./tools/eclipse/project.py
+```
+
+How to build the Gerrit Plugin API is described in the [Gerrit documentation](../../../Documentation/dev-bazel.html#_extension_and_plugin_api_jar_files).
+
### Configure Gerrit to use the SAML filter:
In `$site_path/etc/gerrit.config` file, the `[httpd]` section should contain
diff --git a/doc/Setup_ADFS.md b/adfs/README.md
similarity index 97%
rename from doc/Setup_ADFS.md
rename to adfs/README.md
index d262569..60ddb1f 100644
--- a/doc/Setup_ADFS.md
+++ b/adfs/README.md
@@ -1,4 +1,5 @@
-# Setting-up gerrit-saml-plugin for Active Directory Federation Services (ADFS)
+# ADFS as Gerrit SAML authentication provider
+
Note: replace `fs.hc.sct` with the name of your ADFS, replace gerrit.hc.sct with the name of your Gerrit host.
## Setup on the Gerrit machine
diff --git a/adfs/images/0.png b/adfs/images/0.png
new file mode 100644
index 0000000..2330f30
--- /dev/null
+++ b/adfs/images/0.png
Binary files differ
diff --git a/adfs/images/1.png b/adfs/images/1.png
new file mode 100644
index 0000000..a965a46
--- /dev/null
+++ b/adfs/images/1.png
Binary files differ
diff --git a/adfs/images/10.png b/adfs/images/10.png
new file mode 100644
index 0000000..3e0e911
--- /dev/null
+++ b/adfs/images/10.png
Binary files differ
diff --git a/adfs/images/11.png b/adfs/images/11.png
new file mode 100644
index 0000000..1544a2a
--- /dev/null
+++ b/adfs/images/11.png
Binary files differ
diff --git a/adfs/images/12.png b/adfs/images/12.png
new file mode 100644
index 0000000..838ebea
--- /dev/null
+++ b/adfs/images/12.png
Binary files differ
diff --git a/adfs/images/13.png b/adfs/images/13.png
new file mode 100644
index 0000000..acb2acb
--- /dev/null
+++ b/adfs/images/13.png
Binary files differ
diff --git a/adfs/images/14.png b/adfs/images/14.png
new file mode 100644
index 0000000..4a85a4b
--- /dev/null
+++ b/adfs/images/14.png
Binary files differ
diff --git a/adfs/images/15.png b/adfs/images/15.png
new file mode 100644
index 0000000..bb86e86
--- /dev/null
+++ b/adfs/images/15.png
Binary files differ
diff --git a/adfs/images/2.png b/adfs/images/2.png
new file mode 100644
index 0000000..0039e21
--- /dev/null
+++ b/adfs/images/2.png
Binary files differ
diff --git a/adfs/images/3.png b/adfs/images/3.png
new file mode 100644
index 0000000..f5d528a
--- /dev/null
+++ b/adfs/images/3.png
Binary files differ
diff --git a/adfs/images/4.png b/adfs/images/4.png
new file mode 100644
index 0000000..f9da22c
--- /dev/null
+++ b/adfs/images/4.png
Binary files differ
diff --git a/adfs/images/5.png b/adfs/images/5.png
new file mode 100644
index 0000000..c6e0e13
--- /dev/null
+++ b/adfs/images/5.png
Binary files differ
diff --git a/adfs/images/6.png b/adfs/images/6.png
new file mode 100644
index 0000000..03a3c5d
--- /dev/null
+++ b/adfs/images/6.png
Binary files differ
diff --git a/adfs/images/7.png b/adfs/images/7.png
new file mode 100644
index 0000000..fe8b665
--- /dev/null
+++ b/adfs/images/7.png
Binary files differ
diff --git a/adfs/images/8.png b/adfs/images/8.png
new file mode 100644
index 0000000..2943fe7
--- /dev/null
+++ b/adfs/images/8.png
Binary files differ
diff --git a/adfs/images/9.png b/adfs/images/9.png
new file mode 100644
index 0000000..2fc2f20
--- /dev/null
+++ b/adfs/images/9.png
Binary files differ
diff --git a/keycloak/README.md b/keycloak/README.md
new file mode 100644
index 0000000..bf9939d
--- /dev/null
+++ b/keycloak/README.md
@@ -0,0 +1,85 @@
+# Keycloak as Gerrit SAML provider
+
+[Keycloak](https://www.keycloak.org/) is open source Identity and Access
+Management tool and supports the SAML authentication protocol.
+
+## Objective
+
+This document provides a step-by-step tutorial how to set-up Keycloak as
+SAML provider for Gerrit Code Review for development and guidance only.
+For production HTTPS protocol and other more secure credentials and keys
+would need to be put in place.
+
+## Prerequisites
+
+- [Docker](https://www.docker.com/get-started)
+- [Docker-compose](https://docs.docker.com/compose/)
+- [Gerrit Code Review v2.15 or later](https://www.gerritcodereview.com)
+
+## Steps
+
+1. Install Keycloak official Docker image from this repository and start it:
+
+```bash
+ $ git clone https://github.com/jboss-dockerfiles/keycloak
+ $ cd keycloak/docker-compose-examples
+ $ docker-compose -f keycloak-postgres.yml up
+```
+
+2. Login to Keycloak using user=admin and password=Pa55w0rd credentials and import
+the Gerrit client [keycloak json file](keycloak-gerrit-client-export.json).
+
+3. Create test user (e.g., fullname="John Doe", username "jdoe", email: "john@doe.org", password "secret", Temporary=OFF)
+
+4. Add the following configuration settings to $GERRIT_SITE/etc/gerrit.config:
+
+```
+[auth]
+ type = HTTP
+ logoutUrl = http://localhost:8080/auth/realms/master/protocol/openid-connect/logout
+ httpHeader = X-SAML-UserName
+ httpDisplaynameHeader = X-SAML-DisplayName
+ httpEmailHeader = X-SAML-EmailHeader
+ httpExternalIdHeader = X-SAML-ExternalId
+
+[httpd]
+ listenUrl = http://*:8081/
+ filterClass = com.googlesource.gerrit.plugins.saml.SamlWebFilter
+
+[saml]
+ serviceProviderEntityId = SAML2Client
+ keystorePath = etc/samlKeystore.jks
+ keystorePassword = pac4j-demo-password
+ privateKeyPassword = pac4j-demo-password
+ metadataPath = http://localhost:8080/auth/realms/master/protocol/saml/descriptor
+ userNameAttr = UserName
+ displayNameAttr = DisplayName
+ emailAddressAttr = EmailAddress
+ computedDisplayName = true
+ firstNameAttr = firstName
+ lastNameAttr = lastName
+```
+
+5. Generate keystore in `$GERRIT_SITE/etc` local keystore:
+
+```
+keytool -genkeypair -alias pac4j -keypass pac4j-demo-password \
+ -keystore samlKeystore.jks \
+ -storepass pac4j-demo-password -keyalg RSA -keysize 2048 -validity 365
+```
+
+6. Install the saml.jar filter into the `$GERRIT_SITE/lib` directory
+
+7. Start gerrit using: `$GERRIT_SITE/bin/gerrit.sh start`
+
+8. Enter gerrit URL in browser: http://localhost:8081 and hit "Sign In" button
+
+9. Keycloak Login Dialog should appear
+
+10. Enter user: "jdoe" and password: "secret"
+
+11. You are redirected to gerrit and the first user/admin John Doe is created
+in gerrit with the right user name and email address.
+
+12. Congrats, you have Gerrit / Keycloak SAML integration up and running.
+
diff --git a/keycloak/keycloak-gerrit-client-export.json b/keycloak/keycloak-gerrit-client-export.json
new file mode 100644
index 0000000..e6b6df6
--- /dev/null
+++ b/keycloak/keycloak-gerrit-client-export.json
@@ -0,0 +1,1692 @@
+{
+ "id": "master",
+ "realm": "master",
+ "displayName": "Keycloak",
+ "displayNameHtml": "<div class=\"kc-logo-text\"><span>Keycloak</span></div>",
+ "notBefore": 0,
+ "revokeRefreshToken": false,
+ "refreshTokenMaxReuse": 0,
+ "accessTokenLifespan": 60,
+ "accessTokenLifespanForImplicitFlow": 900,
+ "ssoSessionIdleTimeout": 1800,
+ "ssoSessionMaxLifespan": 36000,
+ "ssoSessionIdleTimeoutRememberMe": 0,
+ "ssoSessionMaxLifespanRememberMe": 0,
+ "offlineSessionIdleTimeout": 2592000,
+ "offlineSessionMaxLifespanEnabled": false,
+ "offlineSessionMaxLifespan": 5184000,
+ "accessCodeLifespan": 60,
+ "accessCodeLifespanUserAction": 300,
+ "accessCodeLifespanLogin": 1800,
+ "actionTokenGeneratedByAdminLifespan": 43200,
+ "actionTokenGeneratedByUserLifespan": 300,
+ "enabled": true,
+ "sslRequired": "external",
+ "registrationAllowed": false,
+ "registrationEmailAsUsername": false,
+ "rememberMe": false,
+ "verifyEmail": false,
+ "loginWithEmailAllowed": true,
+ "duplicateEmailsAllowed": false,
+ "resetPasswordAllowed": false,
+ "editUsernameAllowed": false,
+ "bruteForceProtected": false,
+ "permanentLockout": false,
+ "maxFailureWaitSeconds": 900,
+ "minimumQuickLoginWaitSeconds": 60,
+ "waitIncrementSeconds": 60,
+ "quickLoginCheckMilliSeconds": 1000,
+ "maxDeltaTimeSeconds": 43200,
+ "failureFactor": 30,
+ "defaultRoles": [
+ "offline_access",
+ "uma_authorization"
+ ],
+ "requiredCredentials": [
+ "password"
+ ],
+ "otpPolicyType": "totp",
+ "otpPolicyAlgorithm": "HmacSHA1",
+ "otpPolicyInitialCounter": 0,
+ "otpPolicyDigits": 6,
+ "otpPolicyLookAheadWindow": 1,
+ "otpPolicyPeriod": 30,
+ "otpSupportedApplications": [
+ "FreeOTP",
+ "Google Authenticator"
+ ],
+ "scopeMappings": [
+ {
+ "clientScope": "offline_access",
+ "roles": [
+ "offline_access"
+ ]
+ }
+ ],
+ "clients": [
+ {
+ "id": "3896eefc-4131-4911-96cd-6e1d2aea787f",
+ "clientId": "SAML2Client",
+ "name": "Gerrit",
+ "description": "Gerrit SSO",
+ "rootUrl": "http://localhost:8081/plugins/saml/callback",
+ "adminUrl": "http://localhost:8081/",
+ "baseUrl": "http://localhost:8081/",
+ "surrogateAuthRequired": false,
+ "enabled": true,
+ "clientAuthenticatorType": "client-secret",
+ "secret": "**********",
+ "redirectUris": [
+ "http://localhost:8081/plugins/saml/callback?client_name=SAML2Client"
+ ],
+ "webOrigins": [],
+ "notBefore": 0,
+ "bearerOnly": false,
+ "consentRequired": false,
+ "standardFlowEnabled": true,
+ "implicitFlowEnabled": false,
+ "directAccessGrantsEnabled": false,
+ "serviceAccountsEnabled": false,
+ "publicClient": false,
+ "frontchannelLogout": true,
+ "protocol": "saml",
+ "attributes": {
+ "saml.assertion.signature": "true",
+ "saml.force.post.binding": "false",
+ "saml.multivalued.roles": "false",
+ "saml_single_logout_service_url_post": "http://localhost:8081/plugins/saml/callback?client_name=SAML2Client&logoutendpoint=true",
+ "saml.encrypt": "false",
+ "login_theme": "keycloak",
+ "saml_assertion_consumer_url_post": "http://localhost:8081/",
+ "saml.server.signature": "true",
+ "saml.server.signature.keyinfo.ext": "false",
+ "exclude.session.state.from.auth.response": "false",
+ "saml.signing.certificate": "MIICmzCCAYMCBgFn6ouxezANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZnZXJyaXQwHhcNMTgxMjI2MTI0MzAyWhcNMjgxMjI2MTI0NDQyWjARMQ8wDQYDVQQDDAZnZXJyaXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCBtzB/DRx30RzqRPtJhbe0fWFzZf7wzO9ZjhoS4uYwWBO/FLfI2Qma3/9Q060kKuTlHFHZvrMemUP9Xlc/2+qv+M5pNKRabfwnufdR1zofPnJdazQ6RP41FK/cLLN1rd8naJQ+ZjKE6Di0k55YTUbnbiVgcfjtl/2oQI/HxdpQE5dDWbOkCtBQa6mmznxtjTsOjrE9xKZUNIBNkTGGuWU+NfFkx8oOIpNlChdbcsbZiXceYU8LS6mOjiycZ7XmQe4IEVHVdBbUnsmRK++rqoIY5tNAVWsT+YoiCdGJ+iNYbm7B1Jq0EDYOymiGvcKeMi5wmUdGJ7yxIZxTeenqfq39AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFGgLTbNO8CfqN/MblAKLAKWvycV5FtFlJ2z75NsL2Oz7vc/pwWfLPCQoG5bsPUVwQnp3TmIQeZtoWwkh6Ews5hmS3cRbbswgYpT60FtHuEZAdrqsbXzHv4xfQd5hT5uvhMRj98OReKKap+Se+qNYzKvB6fxYLp0SWm9VOwHEeh+kMA0ZkAeW2m5YHN1T98fsFMLEiOJdGJS6hKpoMm6SofKEgVRdoz72xwEW96dqbDoJlo2/IFqw/tMsDXfyrhQbVlHPBMgRb++hQGyh4hps82TH2mzcvks1geVqXrx+1MyjSJd12+BgOHX40k25FPoQdu15AGYwET5nq/7HZ3F+/M=",
+ "saml_single_logout_service_url_redirect": "",
+ "saml.signature.algorithm": "RSA_SHA256",
+ "saml_force_name_id_format": "true",
+ "saml.client.signature": "false",
+ "tls.client.certificate.bound.access.tokens": "false",
+ "saml.authnstatement": "true",
+ "display.on.consent.screen": "false",
+ "saml.signing.private.key": "MIIEowIBAAKCAQEAgbcwfw0cd9Ec6kT7SYW3tH1hc2X+8MzvWY4aEuLmMFgTvxS3yNkJmt//UNOtJCrk5RxR2b6zHplD/V5XP9vqr/jOaTSkWm38J7n3Udc6Hz5yXWs0OkT+NRSv3Cyzda3fJ2iUPmYyhOg4tJOeWE1G524lYHH47Zf9qECPx8XaUBOXQ1mzpArQUGupps58bY07Do6xPcSmVDSATZExhrllPjXxZMfKDiKTZQoXW3LG2Yl3HmFPC0upjo4snGe15kHuCBFR1XQW1J7JkSvvq6qCGObTQFVrE/mKIgnRifojWG5uwdSatBA2Dspohr3CnjIucJlHRie8sSGcU3np6n6t/QIDAQABAoIBABip6TmvF4Ocqh/NH+5501UpJddYRGIqxTPE9iYzKEt248JIQS2aPt5IyvXmWZxv33fEq7d4L/yYbboGLxbATN5Ks4yDauCa2v/+twzDnJSGPh6PHK4boi4bkdiOU00D01Nd6hn3OpHUybtj+g6WGu40Hj05Tnh2ls9f/zaf4wOofQxo3YctgtHtM5QI0/QwVmuo4p6XoFIpyItHyt3o4MigodNQLkcAQHFSiNdoMLX+BG8dobB/QSFeJhrrXjOSQyk09Kpwl7dDRLtKXx5pthJJitBkuon3aJ60vY7UN/tAcxvzThHiOiiXasJ2ahRvw2ikhAJEONxUxV9em542t80CgYEA+ydmunyUglhhswzHK2HFhdq+T8SgXh3VOy6j4qe1e9vWL1zAmG24cDtQ0lWIZ/KtTLl2LwEtTjx3JVLw3QJ+79WTRJ7GA9ZINJc2ahwaRT9FZgDZA1UvA4CdsWy3Nxm+uAE97Dtke4/C4gqglgVCPRU2m9DsKKqESFYoe0sfBt8CgYEAhDfuk/5/E7Jk3EzlzzT8TwliV0DDgCiF9ebF5sfDvPpkTzYmQ9MwUTDFU1007D3O5+ko0rV6YaKtwDP3JIOGC0tzeVl/NbyOa8DirKPdTyeqPPM8VGDil27q5cxniUt5+9A20oW7EvZ7Q3jyVCCX2lbYw68UhXLedX56r1cScqMCgYAe0qAV5PIo6QXcfoX2+gHOwqC2k3AG/OxNXhT43RI1yC4KE/0C9/w5sd3iYmLiNvMzxlMNw7w2rM8Ggp5S0VqYvoJbWoz8rZCg+6nO7fH/a5ttwE7hzNN/P4qa2rfFiGBSnrxlwIg9bdBCA4Hfx3dwnajdCI/jEcI4SnKfZwHAywKBgBqLuETSZeUofgOVFNbDRpz/v1TRVe8XM74pNTaeiPgaNaWJs6kOb7b9WcDhB80eo7oAIAgeE5IivUvHoykblwARh/+nLlk1oEqWEWykAbzws8dE9qniQdwxksvMfEUeeEFMjpU58FNgVWM4lz64xIQ7x42SS0Z3x55DZJJrovKNAoGBAPg56n+3drjj7FU11UnrkuSn2fqcdGbR5kKex/EQkBLOjB5mPxmI4h9Ycd2K9mBqTw+mYvmD962mJPPmDy4eyUtdcHe/mVCVT0jXPHCUumA2rg4VlF0Da1eiMzd7CwCNa6uq9flBzkEqx0ljO3d5nllFyLwwsYTyrmaxMbcziGTj",
+ "saml_name_id_format": "username",
+ "saml.onetimeuse.condition": "false",
+ "saml_signature_canonicalization_method": "http://www.w3.org/2001/10/xml-exc-c14n#"
+ },
+ "authenticationFlowBindingOverrides": {},
+ "fullScopeAllowed": true,
+ "nodeReRegistrationTimeout": -1,
+ "protocolMappers": [
+ {
+ "id": "771ab333-d236-449a-a298-9b3027fb760e",
+ "name": "lastName",
+ "protocol": "saml",
+ "protocolMapper": "saml-user-property-mapper",
+ "consentRequired": false,
+ "config": {
+ "attribute.nameformat": "Unspecified",
+ "user.attribute": "lastName",
+ "friendly.name": "lastName",
+ "attribute.name": "lastName"
+ }
+ },
+ {
+ "id": "537a1547-d97d-4dd6-8b65-f7204e19bcb1",
+ "name": "UserName",
+ "protocol": "saml",
+ "protocolMapper": "saml-user-property-mapper",
+ "consentRequired": false,
+ "config": {
+ "user.attribute": "username",
+ "friendly.name": "username",
+ "attribute.name": "UserName"
+ }
+ },
+ {
+ "id": "a5786003-1c28-48d9-b1d2-f760b1541057",
+ "name": "firstName",
+ "protocol": "saml",
+ "protocolMapper": "saml-user-property-mapper",
+ "consentRequired": false,
+ "config": {
+ "attribute.nameformat": "Unspecified",
+ "user.attribute": "firstName",
+ "friendly.name": "firstName",
+ "attribute.name": "firstName"
+ }
+ },
+ {
+ "id": "a3e8549e-3f92-43b8-89be-5368aaf2a6f9",
+ "name": "EmailAddress",
+ "protocol": "saml",
+ "protocolMapper": "saml-user-property-mapper",
+ "consentRequired": false,
+ "config": {
+ "user.attribute": "email",
+ "friendly.name": "email",
+ "attribute.name": "EmailAddress"
+ }
+ }
+ ],
+ "defaultClientScopes": [
+ "web-origins",
+ "role_list",
+ "roles",
+ "profile",
+ "email"
+ ],
+ "optionalClientScopes": [
+ "address",
+ "phone",
+ "offline_access"
+ ]
+ },
+ {
+ "id": "61635f19-0db9-4553-a386-5ff668d4da74",
+ "clientId": "security-admin-console",
+ "name": "${client_security-admin-console}",
+ "baseUrl": "/auth/admin/master/console/index.html",
+ "surrogateAuthRequired": false,
+ "enabled": true,
+ "clientAuthenticatorType": "client-secret",
+ "secret": "**********",
+ "redirectUris": [
+ "/auth/admin/master/console/*"
+ ],
+ "webOrigins": [],
+ "notBefore": 0,
+ "bearerOnly": false,
+ "consentRequired": false,
+ "standardFlowEnabled": true,
+ "implicitFlowEnabled": false,
+ "directAccessGrantsEnabled": false,
+ "serviceAccountsEnabled": false,
+ "publicClient": true,
+ "frontchannelLogout": false,
+ "protocol": "openid-connect",
+ "attributes": {},
+ "authenticationFlowBindingOverrides": {},
+ "fullScopeAllowed": false,
+ "nodeReRegistrationTimeout": 0,
+ "protocolMappers": [
+ {
+ "id": "0abb343c-01b6-4736-84df-0b2a2de919e9",
+ "name": "locale",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "locale",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "locale",
+ "jsonType.label": "String"
+ }
+ }
+ ],
+ "defaultClientScopes": [
+ "web-origins",
+ "role_list",
+ "roles",
+ "profile",
+ "email"
+ ],
+ "optionalClientScopes": [
+ "address",
+ "phone",
+ "offline_access"
+ ]
+ },
+ {
+ "id": "a8d57542-328a-4c13-a4ae-ab3a60f1c503",
+ "clientId": "gerrit-oauth",
+ "name": "Gerrit OAuth2 Client",
+ "description": "Gerrit code review client applications",
+ "rootUrl": "http://localhost:8081",
+ "adminUrl": "http://localhost:8081",
+ "surrogateAuthRequired": false,
+ "enabled": true,
+ "clientAuthenticatorType": "client-secret",
+ "secret": "**********",
+ "redirectUris": [
+ "http://localhost:8081/*"
+ ],
+ "webOrigins": [
+ "http://localhost:8081"
+ ],
+ "notBefore": 0,
+ "bearerOnly": false,
+ "consentRequired": false,
+ "standardFlowEnabled": true,
+ "implicitFlowEnabled": false,
+ "directAccessGrantsEnabled": true,
+ "serviceAccountsEnabled": false,
+ "publicClient": false,
+ "frontchannelLogout": false,
+ "protocol": "openid-connect",
+ "attributes": {
+ "saml.assertion.signature": "false",
+ "saml.force.post.binding": "false",
+ "saml.multivalued.roles": "false",
+ "saml.encrypt": "false",
+ "login_theme": "keycloak",
+ "saml.server.signature": "false",
+ "saml.server.signature.keyinfo.ext": "false",
+ "exclude.session.state.from.auth.response": "false",
+ "saml_force_name_id_format": "false",
+ "saml.client.signature": "false",
+ "tls.client.certificate.bound.access.tokens": "false",
+ "saml.authnstatement": "false",
+ "display.on.consent.screen": "false",
+ "saml.onetimeuse.condition": "false"
+ },
+ "authenticationFlowBindingOverrides": {},
+ "fullScopeAllowed": true,
+ "nodeReRegistrationTimeout": -1,
+ "protocolMappers": [
+ {
+ "id": "f2f032f8-03f8-4597-8e84-370560f36686",
+ "name": "family name",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-property-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "lastName",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "family_name",
+ "jsonType.label": "String"
+ }
+ },
+ {
+ "id": "412be101-dfbd-4f77-aa54-6bcca51c1125",
+ "name": "full name",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-full-name-mapper",
+ "consentRequired": false,
+ "config": {
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "userinfo.token.claim": "true"
+ }
+ },
+ {
+ "id": "082f8ac6-123c-4e47-9838-5b5dcb9b08ab",
+ "name": "username",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-property-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "username",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "preferred_username",
+ "jsonType.label": "String"
+ }
+ },
+ {
+ "id": "d31c17bc-71c2-47de-b013-fbb5c43c736e",
+ "name": "email",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-property-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "email",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "email",
+ "jsonType.label": "String"
+ }
+ },
+ {
+ "id": "f69ca206-9484-4179-8b51-f8f5f6ea5f56",
+ "name": "role list",
+ "protocol": "saml",
+ "protocolMapper": "saml-role-list-mapper",
+ "consentRequired": false,
+ "config": {
+ "single": "false",
+ "attribute.nameformat": "Basic",
+ "attribute.name": "Role"
+ }
+ },
+ {
+ "id": "0d4c84a3-66d0-4620-bd11-7ecfcad90577",
+ "name": "given name",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-property-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "firstName",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "given_name",
+ "jsonType.label": "String"
+ }
+ }
+ ],
+ "defaultClientScopes": [
+ "web-origins",
+ "role_list",
+ "roles",
+ "profile",
+ "email"
+ ],
+ "optionalClientScopes": [
+ "address",
+ "phone",
+ "offline_access"
+ ]
+ },
+ {
+ "id": "e255464f-5c00-4db4-be47-921fef057716",
+ "clientId": "junk",
+ "surrogateAuthRequired": false,
+ "enabled": true,
+ "clientAuthenticatorType": "client-secret",
+ "secret": "**********",
+ "redirectUris": [],
+ "webOrigins": [],
+ "notBefore": 0,
+ "bearerOnly": false,
+ "consentRequired": false,
+ "standardFlowEnabled": true,
+ "implicitFlowEnabled": false,
+ "directAccessGrantsEnabled": true,
+ "serviceAccountsEnabled": false,
+ "publicClient": true,
+ "frontchannelLogout": false,
+ "protocol": "openid-connect",
+ "attributes": {},
+ "authenticationFlowBindingOverrides": {},
+ "fullScopeAllowed": true,
+ "nodeReRegistrationTimeout": -1,
+ "protocolMappers": [
+ {
+ "id": "86f4b092-94f7-4b02-a47b-405699d8ab01",
+ "name": "full name",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-full-name-mapper",
+ "consentRequired": false,
+ "config": {
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "userinfo.token.claim": "true"
+ }
+ }
+ ],
+ "defaultClientScopes": [
+ "web-origins",
+ "role_list",
+ "roles",
+ "profile",
+ "email"
+ ],
+ "optionalClientScopes": [
+ "address",
+ "phone",
+ "offline_access"
+ ]
+ },
+ {
+ "id": "00ccb200-7aff-4d0d-ae5f-9323453f99c7",
+ "clientId": "account",
+ "name": "${client_account}",
+ "baseUrl": "/auth/realms/master/account",
+ "surrogateAuthRequired": false,
+ "enabled": true,
+ "clientAuthenticatorType": "client-secret",
+ "secret": "**********",
+ "defaultRoles": [
+ "manage-account",
+ "view-profile"
+ ],
+ "redirectUris": [
+ "/auth/realms/master/account/*"
+ ],
+ "webOrigins": [],
+ "notBefore": 0,
+ "bearerOnly": false,
+ "consentRequired": false,
+ "standardFlowEnabled": true,
+ "implicitFlowEnabled": false,
+ "directAccessGrantsEnabled": false,
+ "serviceAccountsEnabled": false,
+ "publicClient": false,
+ "frontchannelLogout": false,
+ "protocol": "openid-connect",
+ "attributes": {},
+ "authenticationFlowBindingOverrides": {},
+ "fullScopeAllowed": false,
+ "nodeReRegistrationTimeout": 0,
+ "defaultClientScopes": [
+ "web-origins",
+ "role_list",
+ "roles",
+ "profile",
+ "email"
+ ],
+ "optionalClientScopes": [
+ "address",
+ "phone",
+ "offline_access"
+ ]
+ },
+ {
+ "id": "3ea8dde2-4f98-495a-9587-adb926ed34d7",
+ "clientId": "admin-cli",
+ "name": "${client_admin-cli}",
+ "surrogateAuthRequired": false,
+ "enabled": true,
+ "clientAuthenticatorType": "client-secret",
+ "secret": "**********",
+ "redirectUris": [],
+ "webOrigins": [],
+ "notBefore": 0,
+ "bearerOnly": false,
+ "consentRequired": false,
+ "standardFlowEnabled": false,
+ "implicitFlowEnabled": false,
+ "directAccessGrantsEnabled": true,
+ "serviceAccountsEnabled": false,
+ "publicClient": true,
+ "frontchannelLogout": false,
+ "protocol": "openid-connect",
+ "attributes": {},
+ "authenticationFlowBindingOverrides": {},
+ "fullScopeAllowed": false,
+ "nodeReRegistrationTimeout": 0,
+ "defaultClientScopes": [
+ "web-origins",
+ "role_list",
+ "roles",
+ "profile",
+ "email"
+ ],
+ "optionalClientScopes": [
+ "address",
+ "phone",
+ "offline_access"
+ ]
+ },
+ {
+ "id": "cf52d0fd-6efb-4899-9552-1a625d0b1990",
+ "clientId": "broker",
+ "name": "${client_broker}",
+ "surrogateAuthRequired": false,
+ "enabled": true,
+ "clientAuthenticatorType": "client-secret",
+ "secret": "**********",
+ "redirectUris": [],
+ "webOrigins": [],
+ "notBefore": 0,
+ "bearerOnly": false,
+ "consentRequired": false,
+ "standardFlowEnabled": true,
+ "implicitFlowEnabled": false,
+ "directAccessGrantsEnabled": false,
+ "serviceAccountsEnabled": false,
+ "publicClient": false,
+ "frontchannelLogout": false,
+ "protocol": "openid-connect",
+ "attributes": {},
+ "authenticationFlowBindingOverrides": {},
+ "fullScopeAllowed": false,
+ "nodeReRegistrationTimeout": 0,
+ "defaultClientScopes": [
+ "web-origins",
+ "role_list",
+ "roles",
+ "profile",
+ "email"
+ ],
+ "optionalClientScopes": [
+ "address",
+ "phone",
+ "offline_access"
+ ]
+ },
+ {
+ "id": "02ba9bac-1b26-4a8a-ba3b-9ff0b6bc19dd",
+ "clientId": "master-realm",
+ "name": "master Realm",
+ "surrogateAuthRequired": false,
+ "enabled": true,
+ "clientAuthenticatorType": "client-secret",
+ "secret": "**********",
+ "redirectUris": [],
+ "webOrigins": [],
+ "notBefore": 0,
+ "bearerOnly": true,
+ "consentRequired": false,
+ "standardFlowEnabled": true,
+ "implicitFlowEnabled": false,
+ "directAccessGrantsEnabled": false,
+ "serviceAccountsEnabled": false,
+ "publicClient": false,
+ "frontchannelLogout": false,
+ "attributes": {},
+ "authenticationFlowBindingOverrides": {},
+ "fullScopeAllowed": true,
+ "nodeReRegistrationTimeout": 0,
+ "defaultClientScopes": [
+ "web-origins",
+ "role_list",
+ "roles",
+ "profile",
+ "email"
+ ],
+ "optionalClientScopes": [
+ "address",
+ "phone",
+ "offline_access"
+ ]
+ }
+ ],
+ "clientScopes": [
+ {
+ "id": "1775cde0-555d-4ecb-aeeb-0af23c925352",
+ "name": "role_list",
+ "description": "SAML role list",
+ "protocol": "saml",
+ "attributes": {
+ "consent.screen.text": "${samlRoleListScopeConsentText}",
+ "display.on.consent.screen": "true"
+ },
+ "protocolMappers": [
+ {
+ "id": "7025caf9-5a67-450f-89e1-a7cd9cd08fb8",
+ "name": "role list",
+ "protocol": "saml",
+ "protocolMapper": "saml-role-list-mapper",
+ "consentRequired": false,
+ "config": {
+ "single": "false",
+ "attribute.nameformat": "Basic",
+ "attribute.name": "Role"
+ }
+ }
+ ]
+ },
+ {
+ "id": "23e21e4a-067a-42e8-9937-48d502fd9012",
+ "name": "email",
+ "description": "OpenID Connect built-in scope: email",
+ "protocol": "openid-connect",
+ "attributes": {
+ "include.in.token.scope": "true",
+ "display.on.consent.screen": "true",
+ "consent.screen.text": "${emailScopeConsentText}"
+ },
+ "protocolMappers": [
+ {
+ "id": "4d088fd7-6e36-4bbe-b1c3-e7a9424dc7a9",
+ "name": "email verified",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-property-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "emailVerified",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "email_verified",
+ "jsonType.label": "boolean"
+ }
+ },
+ {
+ "id": "67c834c0-835c-47bd-bc0a-7b12e8a83e0d",
+ "name": "email",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-property-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "email",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "email",
+ "jsonType.label": "String"
+ }
+ }
+ ]
+ },
+ {
+ "id": "34dcdfaa-1bfb-4051-b7ee-b80e51f83879",
+ "name": "address",
+ "description": "OpenID Connect built-in scope: address",
+ "protocol": "openid-connect",
+ "attributes": {
+ "include.in.token.scope": "true",
+ "display.on.consent.screen": "true",
+ "consent.screen.text": "${addressScopeConsentText}"
+ },
+ "protocolMappers": [
+ {
+ "id": "77725e59-1d89-44cf-9297-e90de6027803",
+ "name": "address",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-address-mapper",
+ "consentRequired": false,
+ "config": {
+ "user.attribute.formatted": "formatted",
+ "user.attribute.country": "country",
+ "user.attribute.postal_code": "postal_code",
+ "userinfo.token.claim": "true",
+ "user.attribute.street": "street",
+ "id.token.claim": "true",
+ "user.attribute.region": "region",
+ "access.token.claim": "true",
+ "user.attribute.locality": "locality"
+ }
+ }
+ ]
+ },
+ {
+ "id": "4fdfab14-497a-41d4-a3b7-23aad8add16a",
+ "name": "offline_access",
+ "description": "OpenID Connect built-in scope: offline_access",
+ "protocol": "openid-connect",
+ "attributes": {
+ "consent.screen.text": "${offlineAccessScopeConsentText}",
+ "display.on.consent.screen": "true"
+ }
+ },
+ {
+ "id": "71d9deb8-a7b0-4ec7-bb3f-051f7ee107c6",
+ "name": "roles",
+ "description": "OpenID Connect scope for add user roles to the access token",
+ "protocol": "openid-connect",
+ "attributes": {
+ "include.in.token.scope": "false",
+ "display.on.consent.screen": "true",
+ "consent.screen.text": "${rolesScopeConsentText}"
+ },
+ "protocolMappers": [
+ {
+ "id": "4336df7c-c071-4700-9aec-5b63c03cb7a4",
+ "name": "realm roles",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-realm-role-mapper",
+ "consentRequired": false,
+ "config": {
+ "user.attribute": "foo",
+ "access.token.claim": "true",
+ "claim.name": "realm_access.roles",
+ "jsonType.label": "String",
+ "multivalued": "true"
+ }
+ },
+ {
+ "id": "9bb08455-4dd0-4b2d-a764-889bb6b90929",
+ "name": "client roles",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-client-role-mapper",
+ "consentRequired": false,
+ "config": {
+ "user.attribute": "foo",
+ "access.token.claim": "true",
+ "claim.name": "resource_access.${client_id}.roles",
+ "jsonType.label": "String",
+ "multivalued": "true"
+ }
+ },
+ {
+ "id": "45f2232a-17aa-4feb-ac51-f7901ea83e33",
+ "name": "audience resolve",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-audience-resolve-mapper",
+ "consentRequired": false,
+ "config": {}
+ }
+ ]
+ },
+ {
+ "id": "96a5bbb9-2e17-4e24-a625-8629662d5aba",
+ "name": "phone",
+ "description": "OpenID Connect built-in scope: phone",
+ "protocol": "openid-connect",
+ "attributes": {
+ "include.in.token.scope": "true",
+ "display.on.consent.screen": "true",
+ "consent.screen.text": "${phoneScopeConsentText}"
+ },
+ "protocolMappers": [
+ {
+ "id": "853ba347-192a-4ece-ad5d-ca93b0e01701",
+ "name": "phone number",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "phoneNumber",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "phone_number",
+ "jsonType.label": "String"
+ }
+ },
+ {
+ "id": "b2705f58-a4c2-4eb4-9227-6b0154e49388",
+ "name": "phone number verified",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "phoneNumberVerified",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "phone_number_verified",
+ "jsonType.label": "boolean"
+ }
+ }
+ ]
+ },
+ {
+ "id": "b04c6ff4-37b8-43dd-a5c9-f10bdc6c1aa0",
+ "name": "web-origins",
+ "description": "OpenID Connect scope for add allowed web origins to the access token",
+ "protocol": "openid-connect",
+ "attributes": {
+ "include.in.token.scope": "false",
+ "display.on.consent.screen": "false",
+ "consent.screen.text": ""
+ },
+ "protocolMappers": [
+ {
+ "id": "6e7bcd9c-6e8e-4454-ac6a-eb3de841b1c9",
+ "name": "allowed web origins",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-allowed-origins-mapper",
+ "consentRequired": false,
+ "config": {}
+ }
+ ]
+ },
+ {
+ "id": "b55212bf-3a39-450a-98da-d5e44be43536",
+ "name": "profile",
+ "description": "OpenID Connect built-in scope: profile",
+ "protocol": "openid-connect",
+ "attributes": {
+ "include.in.token.scope": "true",
+ "display.on.consent.screen": "true",
+ "consent.screen.text": "${profileScopeConsentText}"
+ },
+ "protocolMappers": [
+ {
+ "id": "f0818a18-e35d-4298-a8a0-a2b6aa28488e",
+ "name": "website",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "website",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "website",
+ "jsonType.label": "String"
+ }
+ },
+ {
+ "id": "9834dcd1-223b-43f1-a6b9-b92ad07bf255",
+ "name": "picture",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "picture",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "picture",
+ "jsonType.label": "String"
+ }
+ },
+ {
+ "id": "d5127eb9-14db-48a0-b53b-e2b8c9ae08c3",
+ "name": "birthdate",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "birthdate",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "birthdate",
+ "jsonType.label": "String"
+ }
+ },
+ {
+ "id": "a322d700-98c0-470a-b9dd-58b785f2df46",
+ "name": "locale",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "locale",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "locale",
+ "jsonType.label": "String"
+ }
+ },
+ {
+ "id": "f6835607-6c5a-4bc0-b2f6-f5df5fac837e",
+ "name": "gender",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "gender",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "gender",
+ "jsonType.label": "String"
+ }
+ },
+ {
+ "id": "4924843e-fef9-4a14-a0c0-dce2dcaf054a",
+ "name": "family name",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-property-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "lastName",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "family_name",
+ "jsonType.label": "String"
+ }
+ },
+ {
+ "id": "acfa2cb8-aacb-435f-b7c8-45a17e66a796",
+ "name": "updated at",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "updatedAt",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "updated_at",
+ "jsonType.label": "String"
+ }
+ },
+ {
+ "id": "00c5a3a2-50ea-4dfb-9cec-f2afa2f2e555",
+ "name": "profile",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "profile",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "profile",
+ "jsonType.label": "String"
+ }
+ },
+ {
+ "id": "51c4633d-8c5f-40e9-94dc-df5104189fa5",
+ "name": "middle name",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "middleName",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "middle_name",
+ "jsonType.label": "String"
+ }
+ },
+ {
+ "id": "a23a3da8-75f3-4e39-b873-7f07139357ff",
+ "name": "nickname",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "nickname",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "nickname",
+ "jsonType.label": "String"
+ }
+ },
+ {
+ "id": "dead5484-aeaa-4ac4-9fb3-799bc760748f",
+ "name": "given name",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-property-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "firstName",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "given_name",
+ "jsonType.label": "String"
+ }
+ },
+ {
+ "id": "e7acd2fa-9f3b-467c-9449-259f2d3f95d7",
+ "name": "zoneinfo",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "zoneinfo",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "zoneinfo",
+ "jsonType.label": "String"
+ }
+ },
+ {
+ "id": "bf0264da-ca81-48f9-9f8e-a6c49852aeb6",
+ "name": "full name",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-full-name-mapper",
+ "consentRequired": false,
+ "config": {
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "userinfo.token.claim": "true"
+ }
+ },
+ {
+ "id": "468ff5d7-7706-4e23-b5b7-1291473666d0",
+ "name": "username",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-property-mapper",
+ "consentRequired": false,
+ "config": {
+ "userinfo.token.claim": "true",
+ "user.attribute": "username",
+ "id.token.claim": "true",
+ "access.token.claim": "true",
+ "claim.name": "preferred_username",
+ "jsonType.label": "String"
+ }
+ }
+ ]
+ }
+ ],
+ "defaultDefaultClientScopes": [
+ "role_list",
+ "email",
+ "roles",
+ "web-origins",
+ "profile"
+ ],
+ "defaultOptionalClientScopes": [
+ "address",
+ "offline_access",
+ "phone"
+ ],
+ "browserSecurityHeaders": {
+ "contentSecurityPolicyReportOnly": "",
+ "xContentTypeOptions": "nosniff",
+ "xRobotsTag": "none",
+ "xFrameOptions": "SAMEORIGIN",
+ "xXSSProtection": "1; mode=block",
+ "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';",
+ "strictTransportSecurity": "max-age=31536000; includeSubDomains"
+ },
+ "smtpServer": {},
+ "eventsEnabled": false,
+ "eventsListeners": [
+ "jboss-logging"
+ ],
+ "enabledEventTypes": [],
+ "adminEventsEnabled": false,
+ "adminEventsDetailsEnabled": false,
+ "components": {
+ "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [
+ {
+ "id": "0df6509e-2901-4f55-b0e7-b973a2d6da64",
+ "name": "Full Scope Disabled",
+ "providerId": "scope",
+ "subType": "anonymous",
+ "subComponents": {},
+ "config": {}
+ },
+ {
+ "id": "064f52ed-0285-41fb-b4b7-8f3dfd40f798",
+ "name": "Allowed Protocol Mapper Types",
+ "providerId": "allowed-protocol-mappers",
+ "subType": "anonymous",
+ "subComponents": {},
+ "config": {
+ "allowed-protocol-mapper-types": [
+ "saml-role-list-mapper",
+ "oidc-address-mapper",
+ "oidc-full-name-mapper",
+ "saml-user-property-mapper",
+ "oidc-usermodel-property-mapper",
+ "saml-user-attribute-mapper",
+ "oidc-sha256-pairwise-sub-mapper",
+ "oidc-usermodel-attribute-mapper"
+ ]
+ }
+ },
+ {
+ "id": "4c351585-dc77-4c11-9520-2e3ae3c2b256",
+ "name": "Max Clients Limit",
+ "providerId": "max-clients",
+ "subType": "anonymous",
+ "subComponents": {},
+ "config": {
+ "max-clients": [
+ "200"
+ ]
+ }
+ },
+ {
+ "id": "e0de7b20-41af-4f25-a954-abf124c4d895",
+ "name": "Allowed Protocol Mapper Types",
+ "providerId": "allowed-protocol-mappers",
+ "subType": "authenticated",
+ "subComponents": {},
+ "config": {
+ "allowed-protocol-mapper-types": [
+ "oidc-address-mapper",
+ "oidc-usermodel-property-mapper",
+ "oidc-sha256-pairwise-sub-mapper",
+ "saml-user-property-mapper",
+ "saml-role-list-mapper",
+ "saml-user-attribute-mapper",
+ "oidc-usermodel-attribute-mapper",
+ "oidc-full-name-mapper"
+ ]
+ }
+ },
+ {
+ "id": "ce3c7aa2-b373-4fa7-84b6-9b618615837b",
+ "name": "Trusted Hosts",
+ "providerId": "trusted-hosts",
+ "subType": "anonymous",
+ "subComponents": {},
+ "config": {
+ "host-sending-registration-request-must-match": [
+ "true"
+ ],
+ "client-uris-must-match": [
+ "true"
+ ]
+ }
+ },
+ {
+ "id": "4cd26343-0905-4771-8b07-cf796d967c34",
+ "name": "Allowed Client Scopes",
+ "providerId": "allowed-client-templates",
+ "subType": "anonymous",
+ "subComponents": {},
+ "config": {
+ "allow-default-scopes": [
+ "true"
+ ]
+ }
+ },
+ {
+ "id": "273dc8c5-031d-46fc-92eb-7530afd4ba88",
+ "name": "Consent Required",
+ "providerId": "consent-required",
+ "subType": "anonymous",
+ "subComponents": {},
+ "config": {}
+ },
+ {
+ "id": "99d0fa48-4f67-4b84-a067-219223523697",
+ "name": "Allowed Client Scopes",
+ "providerId": "allowed-client-templates",
+ "subType": "authenticated",
+ "subComponents": {},
+ "config": {
+ "allow-default-scopes": [
+ "true"
+ ]
+ }
+ }
+ ],
+ "org.keycloak.keys.KeyProvider": [
+ {
+ "id": "dfa01a4f-bc52-4373-a9bc-a3b42bb1e00c",
+ "name": "aes-generated",
+ "providerId": "aes-generated",
+ "subComponents": {},
+ "config": {
+ "priority": [
+ "100"
+ ]
+ }
+ },
+ {
+ "id": "930505a0-0ecf-49af-8b25-282d572d9b3a",
+ "name": "rsa-generated",
+ "providerId": "rsa-generated",
+ "subComponents": {},
+ "config": {
+ "priority": [
+ "100"
+ ]
+ }
+ },
+ {
+ "id": "1c7e1d88-541e-403b-9170-2f15a47a1a75",
+ "name": "hmac-generated",
+ "providerId": "hmac-generated",
+ "subComponents": {},
+ "config": {
+ "priority": [
+ "100"
+ ],
+ "algorithm": [
+ "HS256"
+ ]
+ }
+ }
+ ]
+ },
+ "internationalizationEnabled": false,
+ "supportedLocales": [],
+ "authenticationFlows": [
+ {
+ "id": "425f6f8d-8aec-49ef-9ba0-31261f5ae5ca",
+ "alias": "Handle Existing Account",
+ "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider",
+ "providerId": "basic-flow",
+ "topLevel": false,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "idp-confirm-link",
+ "requirement": "REQUIRED",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "idp-email-verification",
+ "requirement": "ALTERNATIVE",
+ "priority": 20,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "requirement": "ALTERNATIVE",
+ "priority": 30,
+ "flowAlias": "Verify Existing Account by Re-authentication",
+ "userSetupAllowed": false,
+ "autheticatorFlow": true
+ }
+ ]
+ },
+ {
+ "id": "e60dd309-3ebd-45a7-bcc9-cc84762c74a7",
+ "alias": "Verify Existing Account by Re-authentication",
+ "description": "Reauthentication of existing account",
+ "providerId": "basic-flow",
+ "topLevel": false,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "idp-username-password-form",
+ "requirement": "REQUIRED",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "auth-otp-form",
+ "requirement": "OPTIONAL",
+ "priority": 20,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ }
+ ]
+ },
+ {
+ "id": "032f0dbc-ccd1-4a6c-8450-c9f228ad2c85",
+ "alias": "browser",
+ "description": "browser based authentication",
+ "providerId": "basic-flow",
+ "topLevel": true,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "auth-cookie",
+ "requirement": "ALTERNATIVE",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "auth-spnego",
+ "requirement": "DISABLED",
+ "priority": 20,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "identity-provider-redirector",
+ "requirement": "ALTERNATIVE",
+ "priority": 25,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "requirement": "ALTERNATIVE",
+ "priority": 30,
+ "flowAlias": "forms",
+ "userSetupAllowed": false,
+ "autheticatorFlow": true
+ }
+ ]
+ },
+ {
+ "id": "cab51fee-abdf-4140-aecb-551809650d79",
+ "alias": "clients",
+ "description": "Base authentication for clients",
+ "providerId": "client-flow",
+ "topLevel": true,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "client-secret",
+ "requirement": "ALTERNATIVE",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "client-jwt",
+ "requirement": "ALTERNATIVE",
+ "priority": 20,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "client-secret-jwt",
+ "requirement": "ALTERNATIVE",
+ "priority": 30,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "client-x509",
+ "requirement": "ALTERNATIVE",
+ "priority": 40,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ }
+ ]
+ },
+ {
+ "id": "526d355a-24bd-4efb-9ce3-571f76f98ceb",
+ "alias": "direct grant",
+ "description": "OpenID Connect Resource Owner Grant",
+ "providerId": "basic-flow",
+ "topLevel": true,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "direct-grant-validate-username",
+ "requirement": "REQUIRED",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "direct-grant-validate-password",
+ "requirement": "REQUIRED",
+ "priority": 20,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "direct-grant-validate-otp",
+ "requirement": "OPTIONAL",
+ "priority": 30,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ }
+ ]
+ },
+ {
+ "id": "ced266f5-cd87-45c5-9d49-0ca5bc7fc6f0",
+ "alias": "docker auth",
+ "description": "Used by Docker clients to authenticate against the IDP",
+ "providerId": "basic-flow",
+ "topLevel": true,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "docker-http-basic-authenticator",
+ "requirement": "REQUIRED",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ }
+ ]
+ },
+ {
+ "id": "bcafdcc7-b574-472c-92fa-0805fea218af",
+ "alias": "first broker login",
+ "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account",
+ "providerId": "basic-flow",
+ "topLevel": true,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticatorConfig": "review profile config",
+ "authenticator": "idp-review-profile",
+ "requirement": "REQUIRED",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticatorConfig": "create unique user config",
+ "authenticator": "idp-create-user-if-unique",
+ "requirement": "ALTERNATIVE",
+ "priority": 20,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "requirement": "ALTERNATIVE",
+ "priority": 30,
+ "flowAlias": "Handle Existing Account",
+ "userSetupAllowed": false,
+ "autheticatorFlow": true
+ }
+ ]
+ },
+ {
+ "id": "5ad9445d-718a-4488-9efa-f1d2dccee114",
+ "alias": "forms",
+ "description": "Username, password, otp and other auth forms.",
+ "providerId": "basic-flow",
+ "topLevel": false,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "auth-username-password-form",
+ "requirement": "REQUIRED",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "auth-otp-form",
+ "requirement": "OPTIONAL",
+ "priority": 20,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ }
+ ]
+ },
+ {
+ "id": "ef1a1c69-426b-48e5-88df-ce1248357f13",
+ "alias": "http challenge",
+ "description": "An authentication flow based on challenge-response HTTP Authentication Schemes",
+ "providerId": "basic-flow",
+ "topLevel": true,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "no-cookie-redirect",
+ "requirement": "REQUIRED",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "basic-auth",
+ "requirement": "REQUIRED",
+ "priority": 20,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "basic-auth-otp",
+ "requirement": "DISABLED",
+ "priority": 30,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "auth-spnego",
+ "requirement": "DISABLED",
+ "priority": 40,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ }
+ ]
+ },
+ {
+ "id": "c5eef7d9-3262-4f44-a14b-e1292d05f4df",
+ "alias": "registration",
+ "description": "registration flow",
+ "providerId": "basic-flow",
+ "topLevel": true,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "registration-page-form",
+ "requirement": "REQUIRED",
+ "priority": 10,
+ "flowAlias": "registration form",
+ "userSetupAllowed": false,
+ "autheticatorFlow": true
+ }
+ ]
+ },
+ {
+ "id": "89aa8bf0-d261-4287-b137-9d75dbf819d6",
+ "alias": "registration form",
+ "description": "registration form",
+ "providerId": "form-flow",
+ "topLevel": false,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "registration-user-creation",
+ "requirement": "REQUIRED",
+ "priority": 20,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "registration-profile-action",
+ "requirement": "REQUIRED",
+ "priority": 40,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "registration-password-action",
+ "requirement": "REQUIRED",
+ "priority": 50,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "registration-recaptcha-action",
+ "requirement": "DISABLED",
+ "priority": 60,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ }
+ ]
+ },
+ {
+ "id": "a66eb1c7-37bf-48c3-9220-291efe8846c5",
+ "alias": "reset credentials",
+ "description": "Reset credentials for a user if they forgot their password or something",
+ "providerId": "basic-flow",
+ "topLevel": true,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "reset-credentials-choose-user",
+ "requirement": "REQUIRED",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "reset-credential-email",
+ "requirement": "REQUIRED",
+ "priority": 20,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "reset-password",
+ "requirement": "REQUIRED",
+ "priority": 30,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ },
+ {
+ "authenticator": "reset-otp",
+ "requirement": "OPTIONAL",
+ "priority": 40,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ }
+ ]
+ },
+ {
+ "id": "fde82557-2e53-4405-bdd9-d1177df51af9",
+ "alias": "saml ecp",
+ "description": "SAML ECP Profile Authentication Flow",
+ "providerId": "basic-flow",
+ "topLevel": true,
+ "builtIn": true,
+ "authenticationExecutions": [
+ {
+ "authenticator": "http-basic-authenticator",
+ "requirement": "REQUIRED",
+ "priority": 10,
+ "userSetupAllowed": false,
+ "autheticatorFlow": false
+ }
+ ]
+ }
+ ],
+ "authenticatorConfig": [
+ {
+ "id": "25ffee1d-c9df-438b-a378-b86138545aba",
+ "alias": "create unique user config",
+ "config": {
+ "require.password.update.after.registration": "false"
+ }
+ },
+ {
+ "id": "0097c3d6-ab28-42b2-a3ce-967cf0d43d6b",
+ "alias": "review profile config",
+ "config": {
+ "update.profile.on.first.login": "missing"
+ }
+ }
+ ],
+ "requiredActions": [
+ {
+ "alias": "CONFIGURE_TOTP",
+ "name": "Configure OTP",
+ "providerId": "CONFIGURE_TOTP",
+ "enabled": true,
+ "defaultAction": false,
+ "priority": 10,
+ "config": {}
+ },
+ {
+ "alias": "terms_and_conditions",
+ "name": "Terms and Conditions",
+ "providerId": "terms_and_conditions",
+ "enabled": false,
+ "defaultAction": false,
+ "priority": 20,
+ "config": {}
+ },
+ {
+ "alias": "UPDATE_PASSWORD",
+ "name": "Update Password",
+ "providerId": "UPDATE_PASSWORD",
+ "enabled": true,
+ "defaultAction": false,
+ "priority": 30,
+ "config": {}
+ },
+ {
+ "alias": "UPDATE_PROFILE",
+ "name": "Update Profile",
+ "providerId": "UPDATE_PROFILE",
+ "enabled": true,
+ "defaultAction": false,
+ "priority": 40,
+ "config": {}
+ },
+ {
+ "alias": "VERIFY_EMAIL",
+ "name": "Verify Email",
+ "providerId": "VERIFY_EMAIL",
+ "enabled": true,
+ "defaultAction": false,
+ "priority": 50,
+ "config": {}
+ }
+ ],
+ "browserFlow": "browser",
+ "registrationFlow": "registration",
+ "directGrantFlow": "direct grant",
+ "resetCredentialsFlow": "reset credentials",
+ "clientAuthenticationFlow": "clients",
+ "dockerAuthenticationFlow": "docker auth",
+ "attributes": {
+ "_browser_header.xXSSProtection": "1; mode=block",
+ "_browser_header.strictTransportSecurity": "max-age=31536000; includeSubDomains",
+ "_browser_header.xFrameOptions": "SAMEORIGIN",
+ "permanentLockout": "false",
+ "quickLoginCheckMilliSeconds": "1000",
+ "displayName": "Keycloak",
+ "_browser_header.xRobotsTag": "none",
+ "maxFailureWaitSeconds": "900",
+ "displayNameHtml": "<div class=\"kc-logo-text\"><span>Keycloak</span></div>",
+ "minimumQuickLoginWaitSeconds": "60",
+ "failureFactor": "30",
+ "maxDeltaTimeSeconds": "43200",
+ "_browser_header.xContentTypeOptions": "nosniff",
+ "offlineSessionMaxLifespan": "5184000",
+ "_browser_header.contentSecurityPolicyReportOnly": "",
+ "bruteForceProtected": "false",
+ "_browser_header.contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';",
+ "offlineSessionMaxLifespanEnabled": "false",
+ "waitIncrementSeconds": "60"
+ },
+ "keycloakVersion": "4.8.3.Final",
+ "userManagedAccessAllowed": false
+}
\ No newline at end of file
diff --git a/okta/README.md b/okta/README.md
new file mode 100644
index 0000000..4d67ba9
--- /dev/null
+++ b/okta/README.md
@@ -0,0 +1,20 @@
+# Okta as Gerrit SAML authentication provider
+
+- Create a new SAML 2.0 application.
+- Set the following parameters:
+ - Single sign on URL: http://gerrit.site.com/plugins/saml/callback
+ - Check "Use this for Recipient URL and Destination URL".
+ - Audience URI (SP Entity Id): http://gerrit.site.com/plugins/saml/callback
+ - We need to set up the attributes in the assertion to send the right
+ information. Here is how to do it with Okta:
+ - Application username: "Okta username prefix"
+ - Add attribute statement: Name: "DisplayName" with Value
+ "user.displayName"
+ - Add attribute statement: Name: "EmailAddress" with Value
+ "user.email"
+ - **IMPORTANT**: If you are not using Okta, you need to set up an attribute
+ "UserName" with the value of the username (not email, without @). If you
+ do not do so, the name will be taken from the NameId provided by
+ the assertion. This is why in Okta we set the application username to
+ "Okta username prefix".
+- Obtain your IdP metadata (either URL or a local XML file)
\ No newline at end of file
diff --git a/src/main/java/com/googlesource/gerrit/plugins/saml/SamlWebFilter.java b/src/main/java/com/googlesource/gerrit/plugins/saml/SamlWebFilter.java
index 5f0508e..f71398a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/saml/SamlWebFilter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/saml/SamlWebFilter.java
@@ -313,7 +313,7 @@
while (wrappedHeaderNames.hasMoreElements()) {
String header = wrappedHeaderNames.nextElement();
if (!authHeaders.contains(header.toUpperCase())) {
- headerNames.add(wrappedHeaderNames.nextElement());
+ headerNames.add(header);
}
}
return Iterators.asEnumeration(headerNames.iterator());
diff --git a/src/main/resources/Documentation/build.md b/src/main/resources/Documentation/build.md
deleted file mode 100644
index ea50282..0000000
--- a/src/main/resources/Documentation/build.md
+++ /dev/null
@@ -1,45 +0,0 @@
-Build
-=====
-
-This plugin is built with Bazel.
-
-## Build in Gerrit tree
-
-Clone or link this plugin to the plugins directory of Gerrit's
-source tree. Put the external dependency Bazel build file into
-the Gerrit /plugins directory, replacing the existing empty one.
-
-```
- cd gerrit/plugins
- rm external_plugin_deps.bzl
- ln -s @PLUGIN@/external_plugin_deps.bzl .
-```
-
-Then issue
-
-```
- bazel build plugins/@PLUGIN@
-```
-
-The output is created in
-
-```
- bazel-genfiles/plugins/@PLUGIN@/@PLUGIN@.jar
-```
-
-The @PLUGIN@.jar should be deployed to `gerrit_site/lib` directory:
-
-```
- cp bazel-genfiles/plugins/@PLUGIN@/@PLUGIN@.jar `$gerrit_site/lib`
-```
-
-This project can be imported into the Eclipse IDE.
-Add the plugin name to the `CUSTOM_PLUGINS` set in
-Gerrit core in `tools/bzl/plugins.bzl`, and execute:
-
-```
- ./tools/eclipse/project.py
-```
-
-How to build the Gerrit Plugin API is described in the [Gerrit
-documentation](../../../Documentation/dev-bazel.html#_extension_and_plugin_api_jar_files).