Rename Office365AuthService to AzureActiveDirectoryService * Running plugin init step will check for previously installed Office365 oauth and if it is found it will ask the user y/n based on the Office365 naming. If no configuration for Office365 where found it will ask to install Azure oauth instead. * Added link-to-existing-office365-accounts to support automatic linking of any old Office365 accounts. Change-Id: I52e8834f5c9bac0c62f5f9b19523341f0e94c874
diff --git a/README.md b/README.md index de036fb..3e70431 100644 --- a/README.md +++ b/README.md
@@ -17,7 +17,7 @@ * [Google](https://developers.google.com/identity/protocols/OAuth2) * [Keycloak](http://www.keycloak.org/) * [LemonLDAP::NG](https://lemonldap-ng.org) -* [Office365](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols) +* [Azure (previously named Office365)](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols) * [Phabricator](https://secure.phabricator.com/book/phabcontrib/article/using_oauthserver/) See the [Wiki](https://github.com/davido/gerrit-oauth-provider/wiki) what it can do for you.
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/Office365OAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/AzureActiveDirectoryService.java similarity index 87% rename from src/main/java/com/googlesource/gerrit/plugins/oauth/Office365OAuthService.java rename to src/main/java/com/googlesource/gerrit/plugins/oauth/AzureActiveDirectoryService.java index bdafd92..81dc53f 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/oauth/Office365OAuthService.java +++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/AzureActiveDirectoryService.java
@@ -49,14 +49,16 @@ import org.slf4j.LoggerFactory; @Singleton -class Office365OAuthService implements OAuthServiceProvider { - private static final Logger log = LoggerFactory.getLogger(Office365OAuthService.class); - static final String CONFIG_SUFFIX = "-office365-oauth"; +class AzureActiveDirectoryService implements OAuthServiceProvider { + private static final Logger log = LoggerFactory.getLogger(AzureActiveDirectoryService.class); + static final String CONFIG_SUFFIX_LEGACY = "-office365-oauth"; + static final String CONFIG_SUFFIX = "-azure-oauth"; + private static final String AZURE_PROVIDER_PREFIX = "azure-oauth:"; private static final String OFFICE365_PROVIDER_PREFIX = "office365-oauth:"; private static final String PROTECTED_RESOURCE_URL = "https://graph.microsoft.com/v1.0/me"; private static final String SCOPE = "openid offline_access https://graph.microsoft.com/user.readbasic.all"; - private static final String DEFAULT_TENANT = "organizations"; + public static final String DEFAULT_TENANT = "organizations"; private static final ImmutableSet<String> TENANTS_WITHOUT_VALIDATION = ImmutableSet.<String>builder().add(DEFAULT_TENANT).add("common").add("consumers").build(); private final OAuth20Service service; @@ -65,13 +67,26 @@ private final boolean useEmailAsUsername; private final String tenant; private final String clientId; + private String providerPrefix; + private final boolean linkOffice365Id; @Inject - Office365OAuthService( + AzureActiveDirectoryService( PluginConfigFactory cfgFactory, @PluginName String pluginName, @CanonicalWebUrl Provider<String> urlProvider) { PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX); + providerPrefix = AZURE_PROVIDER_PREFIX; + + // ?: Did we find the client_id with the CONFIG_SUFFIX + if (cfg.getString(InitOAuth.CLIENT_ID) == null) { + // -> No, we did not find the client_id in the azure config so we should try the old legacy + // office365 section + cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX_LEGACY); + // We must also use the new provider prefix + providerPrefix = OFFICE365_PROVIDER_PREFIX; + } + this.linkOffice365Id = cfg.getBoolean(InitOAuth.LINK_TO_EXISTING_OFFICE365_ACCOUNT, false); this.canonicalWebUrl = CharMatcher.is('/').trimTrailingFrom(urlProvider.get()) + "/"; this.useEmailAsUsername = cfg.getBoolean(InitOAuth.USE_EMAIL_AS_USERNAME, false); this.tenant = cfg.getString(InitOAuth.TENANT, DEFAULT_TENANT); @@ -167,12 +182,13 @@ if (useEmailAsUsername && !email.isJsonNull()) { login = email.getAsString().split("@")[0]; } + return new OAuthUserInfo( - OFFICE365_PROVIDER_PREFIX + id.getAsString() /*externalId*/, + providerPrefix + id.getAsString() /*externalId*/, login /*username*/, email == null || email.isJsonNull() ? null : email.getAsString() /*email*/, name == null || name.isJsonNull() ? null : name.getAsString() /*displayName*/, - null); + linkOffice365Id ? OFFICE365_PROVIDER_PREFIX + id.getAsString() : null); } } catch (ExecutionException | InterruptedException e) { throw new RuntimeException("Cannot retrieve user info resource", e);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/HttpModule.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/HttpModule.java index 80bc605..b9517b2 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/oauth/HttpModule.java +++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/HttpModule.java
@@ -20,6 +20,7 @@ import com.google.gerrit.server.config.PluginConfig; import com.google.gerrit.server.config.PluginConfigFactory; import com.google.inject.Inject; +import com.google.inject.ProvisionException; import com.google.inject.servlet.ServletModule; class HttpModule extends ServletModule { @@ -99,11 +100,28 @@ .to(KeycloakOAuthService.class); } - cfg = cfgFactory.getFromGerritConfig(pluginName + Office365OAuthService.CONFIG_SUFFIX); + boolean office365LegacyProviderBound = false; + cfg = + cfgFactory.getFromGerritConfig( + pluginName + AzureActiveDirectoryService.CONFIG_SUFFIX_LEGACY); if (cfg.getString(InitOAuth.CLIENT_ID) != null) { + office365LegacyProviderBound = true; bind(OAuthServiceProvider.class) - .annotatedWith(Exports.named(Office365OAuthService.CONFIG_SUFFIX)) - .to(Office365OAuthService.class); + .annotatedWith(Exports.named(AzureActiveDirectoryService.CONFIG_SUFFIX)) + .to(AzureActiveDirectoryService.class); + } + cfg = cfgFactory.getFromGerritConfig(pluginName + AzureActiveDirectoryService.CONFIG_SUFFIX); + if (cfg.getString(InitOAuth.CLIENT_ID) != null) { + // ?: Check if the legacy Office365 is already bound, we can only have one of these bound at + // one time + if (office365LegacyProviderBound) { + // -> Yes, the legacy Office365 is already bound and we are trying to bind the + // AzureActiveDirectoryService.CONFIG_SUFFIX at the same time. + throw new ProvisionException("Legacy Office365 OAuth provider is already bound!"); + } + bind(OAuthServiceProvider.class) + .annotatedWith(Exports.named(AzureActiveDirectoryService.CONFIG_SUFFIX)) + .to(AzureActiveDirectoryService.class); } cfg = cfgFactory.getFromGerritConfig(pluginName + AirVantageOAuthService.CONFIG_SUFFIX);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/InitOAuth.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/InitOAuth.java index 6f7ca01..3fbb2ca 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/oauth/InitOAuth.java +++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/InitOAuth.java
@@ -35,6 +35,7 @@ static final String ROOT_URL = "root-url"; static final String REALM = "realm"; static final String TENANT = "tenant"; + static final String LINK_TO_EXISTING_OFFICE365_ACCOUNT = "link-to-existing-office365-accounts"; static final String SERVICE_NAME = "service-name"; static String FIX_LEGACY_USER_ID_QUESTION = "Fix legacy user id, without oauth provider prefix?"; @@ -49,6 +50,7 @@ private final Section dexOAuthProviderSection; private final Section keycloakOAuthProviderSection; private final Section office365OAuthProviderSection; + private final Section azureActiveDirectoryAuthProviderSection; private final Section airVantageOAuthProviderSection; private final Section phabricatorOAuthProviderSection; @@ -74,7 +76,9 @@ this.keycloakOAuthProviderSection = sections.get(PLUGIN_SECTION, pluginName + KeycloakOAuthService.CONFIG_SUFFIX); this.office365OAuthProviderSection = - sections.get(PLUGIN_SECTION, pluginName + Office365OAuthService.CONFIG_SUFFIX); + sections.get(PLUGIN_SECTION, pluginName + AzureActiveDirectoryService.CONFIG_SUFFIX_LEGACY); + this.azureActiveDirectoryAuthProviderSection = + sections.get(PLUGIN_SECTION, pluginName + AzureActiveDirectoryService.CONFIG_SUFFIX); this.airVantageOAuthProviderSection = sections.get(PLUGIN_SECTION, pluginName + AirVantageOAuthService.CONFIG_SUFFIX); this.phabricatorOAuthProviderSection = @@ -159,12 +163,28 @@ keycloakOAuthProviderSection.string("Keycloak Realm", REALM, null); } - boolean configureOffice365OAuthProvider = - ui.yesno( - isConfigured(office365OAuthProviderSection), - "Use Office365 OAuth provider for Gerrit login ?"); - if (configureOffice365OAuthProvider) { - configureOAuth(office365OAuthProviderSection); + // ?: Are there legacy office365 already configured on the system? + if (isConfigured(office365OAuthProviderSection)) { + // -> Yes, this system has already configured the old legacy office365. + boolean configureOffice365OAuthProvider = + ui.yesno( + isConfigured(office365OAuthProviderSection), + "Use Office365 OAuth provider for Gerrit login ?"); + if (configureOffice365OAuthProvider) { + configureOAuth(office365OAuthProviderSection); + } + } + // E-> No, we either are setting up on an new system or using the new azure config + else { + boolean configureAzureActiveDirectoryAuthProvider = + ui.yesno( + isConfigured(azureActiveDirectoryAuthProviderSection), + "Use Azure OAuth provider for Gerrit login ?"); + if (configureAzureActiveDirectoryAuthProvider) { + configureOAuth(azureActiveDirectoryAuthProviderSection); + azureActiveDirectoryAuthProviderSection.string( + "Tenant", TENANT, AzureActiveDirectoryService.DEFAULT_TENANT); + } } boolean configureAirVantageOAuthProvider =
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md index fec6ecb..78eb908 100644 --- a/src/main/resources/Documentation/config.md +++ b/src/main/resources/Documentation/config.md
@@ -49,10 +49,18 @@ client-secret = "<client-secret>" root-url = "<phabricator url>" + # The office365 has been renamed to azure and is deprecated. [plugin "@PLUGIN@-office365-oauth"] client-id = "<client-id>" client-secret = "<client-secret>" tenant = "<tenant (optional defaults to organizations if not set)>" + + [plugin "@PLUGIN@-azure-oauth"] + client-id = "<client-id>" + client-secret = "<client-secret>" + tenant = "<tenant (optional defaults to organizations if not set)>" + link-to-existing-office365-accounts = true #Optional, if set will try to link old account with the @PLUGIN@-office365-oauth naming + ``` When one from the sections above is omitted, OAuth SSO is used. @@ -228,26 +236,41 @@ Client application. See [Using the Phabricator OAuth Server](https://secure.phabricator.com/book/phabcontrib/article/using_oauthserver/). -### Office365 -The client-id and client-secret for Office365/Azure can be obtained by registering a new application, +### Azure (previously named Office365) +Were previously named Office365 but both `plugin.gerrit-oauth-provider-azure-oauth` and +`plugin.gerrit-oauth-provider-office365-oauth` is supported by the Azure OAuth. +When running *java gerrit.war init* it will check the existing config to see if it finds the old +naming and use that during the init run, if it does not find the `office365-oauth` it will +use the new `azure-oauth` naming. + +The client-id and client-secret for Azure can be obtained by registering a new application, see [OAuth 2.0 and OpenID Connect protocols on Microsoft identity platform](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols). ####Username -By default Office365 OAuth will not set a username (used for ssh) and the user can choose one from the web ui -(needed before using ssh). To automatically set the user part from the email the option *use-email-as-username* +By default, Azure OAuth will not set a username (used for ssh) and the user can choose one from the web ui can be used. ``` -plugin.gerrit-oauth-provider-office365-oauth.use-email-as-username = true +plugin.gerrit-oauth-provider-azure-oauth.use-email-as-username = true ``` ####Tenant -The Gerrit OAuth plugin is default set to use the tenant `organizations` but a specific tenant can be used by +The Azure OAuth is default set to use the tenant `organizations` but a specific tenant can be used by the option `tenant`. If a tenant other than `common`, `organizations` or `consumers` is used then the tokens will be validated that they are originating from the same tenant that is configured in the Gerrit OAuth plugin. -See [https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-protocols-oidc#fetch-the-openid-connect-metadata-document](Microsoft identity platform and OpenID Connect protocol) +See [Microsoft identity platform and OpenID Connect protocol](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-protocols-oidc#fetch-the-openid-connect-metadata-document) ``` -plugin.gerrit-oauth-provider-office365-oauth.tenant = <tenant to use> +plugin.gerrit-oauth-provider-azure-oauth.tenant = <tenant to use> ``` Regardless of tenant all tokens will be checked that they contain the client_id set -in the Gerrit OAuth plugin. +in the Azure OAuth. + +####Migrating from Office365 naming +If this where previously installed with the `office365-oauth` you can migrate to `azure-oauth` by setting the +flag. +``` +plugin.gerrit-oauth-provider-azure-oauth.link-to-existing-office365-accounts = true +``` +This will try to link the old `office365-oauth` external id to the new `azure-oauth` external id automatically. +Another option is to migrate these manually offline, see [External IDs](https://gerrit-review.googlesource.com/Documentation/config-accounts.html#external-ids) +for more information.
diff --git a/src/test/java/com/googlesource/gerrit/plugins/oauth/Office365ApiTest.java b/src/test/java/com/googlesource/gerrit/plugins/oauth/MicrosoftAzureActiveDirectory20ApiTest.java similarity index 95% rename from src/test/java/com/googlesource/gerrit/plugins/oauth/Office365ApiTest.java rename to src/test/java/com/googlesource/gerrit/plugins/oauth/MicrosoftAzureActiveDirectory20ApiTest.java index a725167..a98348e 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/oauth/Office365ApiTest.java +++ b/src/test/java/com/googlesource/gerrit/plugins/oauth/MicrosoftAzureActiveDirectory20ApiTest.java
@@ -21,7 +21,7 @@ import org.junit.Before; import org.junit.Test; -public class Office365ApiTest { +public class MicrosoftAzureActiveDirectory20ApiTest { private MicrosoftAzureActiveDirectory20Api api; @Before