From ca4e15cfc95adf45156c0048f527b032163baea5 Mon Sep 17 00:00:00 2001 From: Brian Demers Date: Mon, 21 Sep 2020 13:51:54 -0400 Subject: [PATCH 1/5] Adds postLogoutRedirectUris to samples config If not set this value defaults to the redirect URI base URLs Added basic validating parsing to protect against missing `oauthClient` nodes in sample configuration --- .../java/com/okta/cli/commands/Start.java | 9 ++- .../cli/common/model/OktaSampleConfig.java | 1 + .../common/service/DefaultOidcAppCreator.java | 55 +++++++-------- .../service/DefaultSampleConfigParser.java | 10 ++- .../common/service/DefaultSetupService.java | 7 +- .../cli/common/service/OidcAppCreator.java | 6 +- .../okta/cli/common/service/SetupService.java | 14 ++++ .../service/DefaultOidcAppCreatorTest.groovy | 68 ++++++++++++++++++- .../service/DefaultSetupServiceTest.groovy | 40 ++++++++++- 9 files changed, 170 insertions(+), 40 deletions(-) diff --git a/cli/src/main/java/com/okta/cli/commands/Start.java b/cli/src/main/java/com/okta/cli/commands/Start.java index e93ee922..6e57d9ac 100644 --- a/cli/src/main/java/com/okta/cli/commands/Start.java +++ b/cli/src/main/java/com/okta/cli/commands/Start.java @@ -47,6 +47,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.concurrent.Callable; import java.util.stream.Collectors; @@ -113,6 +114,11 @@ public Integer call() throws Exception { // parse the `.okta.yaml` file OktaSampleConfig config = new DefaultSampleConfigParser().loadConfig(projectDirectory); + // default to SPA application + OpenIdConnectApplicationType applicationType = Optional.ofNullable(config.getOAuthClient().getApplicationType()) + .map(it -> it.toUpperCase(Locale.ENGLISH)) + .map(OpenIdConnectApplicationType::valueOf) + .orElse(OpenIdConnectApplicationType.BROWSER); // create the Okta application Client client = Clients.builder().build(); @@ -126,8 +132,9 @@ public Integer call() throws Exception { authorizationServer.getIssuer(), authorizationServer.getId(), true, - OpenIdConnectApplicationType.valueOf(config.getOAuthClient().getApplicationType().toUpperCase(Locale.ENGLISH)), // TODO default to SPA + applicationType, config.getOAuthClient().getRedirectUris(), + config.getOAuthClient().getPostLogoutRedirectUris(), config.getTrustedOrigins() ); diff --git a/common/src/main/java/com/okta/cli/common/model/OktaSampleConfig.java b/common/src/main/java/com/okta/cli/common/model/OktaSampleConfig.java index c7b4e70a..70aa9579 100644 --- a/common/src/main/java/com/okta/cli/common/model/OktaSampleConfig.java +++ b/common/src/main/java/com/okta/cli/common/model/OktaSampleConfig.java @@ -49,6 +49,7 @@ public void setOauthClient(OAuthClient oauthClient) { @Data public static class OAuthClient { private List redirectUris; + private List postLogoutRedirectUris; private String applicationType; } } diff --git a/common/src/main/java/com/okta/cli/common/service/DefaultOidcAppCreator.java b/common/src/main/java/com/okta/cli/common/service/DefaultOidcAppCreator.java index e2fdf23a..f4a5b46c 100644 --- a/common/src/main/java/com/okta/cli/common/service/DefaultOidcAppCreator.java +++ b/common/src/main/java/com/okta/cli/common/service/DefaultOidcAppCreator.java @@ -30,17 +30,15 @@ import com.okta.sdk.resource.application.OpenIdConnectApplicationType; import java.net.URI; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.stream.Collectors; class DefaultOidcAppCreator implements OidcAppCreator { @Override - public ExtensibleResource createOidcApp(Client client, String oidcAppName, List redirectUris) { + public ExtensibleResource createOidcApp(Client client, String oidcAppName, List redirectUris, List postLogoutRedirectUris) { Optional existingApp = getApplication(client, oidcAppName); @@ -53,16 +51,9 @@ public ExtensibleResource createOidcApp(Client client, String oidcAppName, List< .setGrantTypes(Collections.singletonList(OAuthGrantType.AUTHORIZATION_CODE)) .setApplicationType(OpenIdConnectApplicationType.WEB); - // TODO expose this setting to the user - // TODO the post redirect URI should be exposed in v2 of the SDK - Set postLogoutRedirect = redirectUris.stream() - .map(redirectUri -> { - URI uri = URI.create(redirectUri).resolve("/"); - return uri.toString(); - }) - .collect(Collectors.toSet()); - if (!postLogoutRedirect.isEmpty()) { - oauthClient.put("post_logout_redirect_uris", new ArrayList<>(postLogoutRedirect)); + List logoutRedirectUris = getOrDefaultWebBasedPostLogoutRedirectUris(postLogoutRedirectUris, redirectUris); + if (!logoutRedirectUris.isEmpty()) { + oauthClient.setPostLogoutRedirectUris(logoutRedirectUris); } Application app = client.instantiate(OpenIdConnectApplication.class) @@ -80,7 +71,7 @@ public ExtensibleResource createOidcApp(Client client, String oidcAppName, List< } @Override - public ExtensibleResource createOidcNativeApp(Client client, String oidcAppName, List redirectUris) { + public ExtensibleResource createOidcNativeApp(Client client, String oidcAppName, List redirectUris, List postLogoutRedirectUris) { Optional existingApp = getApplication(client, oidcAppName); @@ -101,8 +92,9 @@ public ExtensibleResource createOidcNativeApp(Client client, String oidcAppName, .setOAuthClient(client.instantiate(ApplicationCredentialsOAuthClient.class) .setTokenEndpointAuthMethod(OAuthEndpointAuthenticationMethod.NONE))); - // TODO expose post_logout_redirect_uris setting to the user - // for mobile apps this is likely to be something like protocol://logout + if (!postLogoutRedirectUris.isEmpty()) { + oauthClient.setPostLogoutRedirectUris(postLogoutRedirectUris); + } app = client.createApplication(app); assignAppToEveryoneGroup(client, app); @@ -115,7 +107,7 @@ public ExtensibleResource createOidcNativeApp(Client client, String oidcAppName, } @Override - public ExtensibleResource createOidcSpaApp(Client client, String oidcAppName, List redirectUris) { + public ExtensibleResource createOidcSpaApp(Client client, String oidcAppName, List redirectUris, List postLogoutRedirectUris) { Optional existingApp = getApplication(client, oidcAppName); @@ -128,16 +120,9 @@ public ExtensibleResource createOidcSpaApp(Client client, String oidcAppName, Li .setGrantTypes(Collections.singletonList(OAuthGrantType.AUTHORIZATION_CODE)) .setApplicationType(OpenIdConnectApplicationType.BROWSER); - // TODO expose this setting to the user - // TODO the post redirect URI should be exposed in v2 of the SDK - Set postLogoutRedirect = redirectUris.stream() - .map(redirectUri -> { - URI uri = URI.create(redirectUri).resolve("/"); - return uri.toString(); - }) - .collect(Collectors.toSet()); - if (!postLogoutRedirect.isEmpty()) { - oauthClient.put("post_logout_redirect_uris", new ArrayList<>(postLogoutRedirect)); + List logoutRedirectUris = getOrDefaultWebBasedPostLogoutRedirectUris(postLogoutRedirectUris, redirectUris); + if (!logoutRedirectUris.isEmpty()) { + oauthClient.setPostLogoutRedirectUris(logoutRedirectUris); } Application app = client.instantiate(OpenIdConnectApplication.class) @@ -203,4 +188,20 @@ private void assignAppToEveryoneGroup(Client client, Application app) { ApplicationGroupAssignment aga = client.instantiate(ApplicationGroupAssignment.class).setPriority(2); app.createApplicationGroupAssignment(everyoneGroupId, aga); } + + private List getOrDefaultWebBasedPostLogoutRedirectUris(List postLogoutRedirectUris, List redirectUris) { + + if (com.okta.commons.lang.Collections.isEmpty(postLogoutRedirectUris)) { + // default to using the redirect URIs base URL + return redirectUris.stream() + .map(redirectUri -> { + URI uri = URI.create(redirectUri).resolve("/"); + return uri.toString(); + }) + .distinct() + .collect(Collectors.toList()); + } else { + return postLogoutRedirectUris; + } + } } \ No newline at end of file diff --git a/common/src/main/java/com/okta/cli/common/service/DefaultSampleConfigParser.java b/common/src/main/java/com/okta/cli/common/service/DefaultSampleConfigParser.java index 45481512..6ce40017 100644 --- a/common/src/main/java/com/okta/cli/common/service/DefaultSampleConfigParser.java +++ b/common/src/main/java/com/okta/cli/common/service/DefaultSampleConfigParser.java @@ -32,7 +32,15 @@ public OktaSampleConfig parseConfig(File configFile) throws IOException { // ignore unknown properties, so we can add additional features and not break older clients Representer representer = new Representer(); representer.getPropertyUtils().setSkipMissingProperties(true); - return new Yaml(representer).loadAs(fileInputStream, OktaSampleConfig.class); + OktaSampleConfig config = new Yaml(representer).loadAs(fileInputStream, OktaSampleConfig.class); + + // TODO improve validation of configuration + if (config.getOAuthClient() == null) { + throw new IllegalArgumentException("Sample configuration file: '" + configFile.getAbsoluteFile() + + "' must contain an 'oauthClient' element, see: " + + "https://github.com/oktadeveloper/okta-cli/wiki/Create-an-Okta-Start-Samples"); + } + return config; } } } diff --git a/common/src/main/java/com/okta/cli/common/service/DefaultSetupService.java b/common/src/main/java/com/okta/cli/common/service/DefaultSetupService.java index 8c9d5219..6fd78ade 100644 --- a/common/src/main/java/com/okta/cli/common/service/DefaultSetupService.java +++ b/common/src/main/java/com/okta/cli/common/service/DefaultSetupService.java @@ -167,6 +167,7 @@ public void createOidcApplication(MutablePropertySource propertySource, boolean interactive, OpenIdConnectApplicationType appType, List redirectUris, + List postLogoutRedirectUris, List trustedOrigins) throws IOException { // Create new Application @@ -183,13 +184,13 @@ public void createOidcApplication(MutablePropertySource propertySource, ExtensibleResource clientCredsResponse; switch (appType) { case WEB: - clientCredsResponse = oidcAppCreator.createOidcApp(client, oidcAppName, redirectUris); + clientCredsResponse = oidcAppCreator.createOidcApp(client, oidcAppName, redirectUris, postLogoutRedirectUris); break; case NATIVE: - clientCredsResponse = oidcAppCreator.createOidcNativeApp(client, oidcAppName, redirectUris); + clientCredsResponse = oidcAppCreator.createOidcNativeApp(client, oidcAppName, redirectUris, postLogoutRedirectUris); break; case BROWSER: - clientCredsResponse = oidcAppCreator.createOidcSpaApp(client, oidcAppName, redirectUris); + clientCredsResponse = oidcAppCreator.createOidcSpaApp(client, oidcAppName, redirectUris, postLogoutRedirectUris); break; case SERVICE: clientCredsResponse = oidcAppCreator.createOidcServiceApp(client, oidcAppName, redirectUris); diff --git a/common/src/main/java/com/okta/cli/common/service/OidcAppCreator.java b/common/src/main/java/com/okta/cli/common/service/OidcAppCreator.java index f11032a6..60f715d3 100644 --- a/common/src/main/java/com/okta/cli/common/service/OidcAppCreator.java +++ b/common/src/main/java/com/okta/cli/common/service/OidcAppCreator.java @@ -22,11 +22,11 @@ public interface OidcAppCreator { - ExtensibleResource createOidcApp(Client client, String oidcAppName, List redirectUris); + ExtensibleResource createOidcApp(Client client, String oidcAppName, List redirectUris, List postLogoutRedirectUris); - ExtensibleResource createOidcNativeApp(Client client, String oidcAppName, List redirectUris); + ExtensibleResource createOidcNativeApp(Client client, String oidcAppName, List redirectUris, List postLogoutRedirectUris); - ExtensibleResource createOidcSpaApp(Client client, String oidcAppName, List redirectUris); + ExtensibleResource createOidcSpaApp(Client client, String oidcAppName, List redirectUris, List postLogoutRedirectUris); ExtensibleResource createOidcServiceApp(Client client, String oidcAppName, List redirectUris); } diff --git a/common/src/main/java/com/okta/cli/common/service/SetupService.java b/common/src/main/java/com/okta/cli/common/service/SetupService.java index 15284a58..4ff9c20e 100644 --- a/common/src/main/java/com/okta/cli/common/service/SetupService.java +++ b/common/src/main/java/com/okta/cli/common/service/SetupService.java @@ -59,6 +59,19 @@ default void createOidcApplication(MutablePropertySource propertySource, createOidcApplication(propertySource, oidcAppName, orgUrl, groupClaimName, issuerUri, authorizationServerId, interactive, appType, redirectUris, Collections.emptyList()); } + default void createOidcApplication(MutablePropertySource propertySource, + String oidcAppName, + String orgUrl, + String groupClaimName, + String issuerUri, + String authorizationServerId, + boolean interactive, + OpenIdConnectApplicationType appType, + List redirectUris, + List postLogoutRedirectUris) throws IOException { + createOidcApplication(propertySource, oidcAppName, orgUrl, groupClaimName, issuerUri, authorizationServerId, interactive, appType, redirectUris, postLogoutRedirectUris, Collections.emptyList()); + } + void createOidcApplication(MutablePropertySource propertySource, String oidcAppName, String orgUrl, @@ -68,5 +81,6 @@ void createOidcApplication(MutablePropertySource propertySource, boolean interactive, OpenIdConnectApplicationType appType, List redirectUris, + List postLogoutRedirectUris, List trustedOrigins) throws IOException; } diff --git a/common/src/test/groovy/com/okta/cli/common/service/DefaultOidcAppCreatorTest.groovy b/common/src/test/groovy/com/okta/cli/common/service/DefaultOidcAppCreatorTest.groovy index 2186d1d5..3da34411 100644 --- a/common/src/test/groovy/com/okta/cli/common/service/DefaultOidcAppCreatorTest.groovy +++ b/common/src/test/groovy/com/okta/cli/common/service/DefaultOidcAppCreatorTest.groovy @@ -52,7 +52,7 @@ class DefaultOidcAppCreatorTest { when(client.http()).thenReturn(http) when(http.get("/api/v1/internal/apps/${appId}/settings/clientcreds", ExtensibleResource)).thenReturn(response) - ExtensibleResource result = appCreator.createOidcApp(client, appName, []) + ExtensibleResource result = appCreator.createOidcApp(client, appName, [], []) assertThat result, is(response) } @@ -107,7 +107,7 @@ class DefaultOidcAppCreatorTest { when(client.http()).thenReturn(http) when(http.get("/api/v1/internal/apps/${appId}/settings/clientcreds", ExtensibleResource)).thenReturn(response) - ExtensibleResource result = appCreator.createOidcApp(client, appName, ["http://localhost:8080/callback", "http://localhost:8080/login/oauth2/code/okta"]) + ExtensibleResource result = appCreator.createOidcApp(client, appName, ["http://localhost:8080/callback", "http://localhost:8080/login/oauth2/code/okta"], []) assertThat result, is(response) @@ -116,6 +116,68 @@ class DefaultOidcAppCreatorTest { verify(settingsClient).setResponseTypes([OAuthResponseType.CODE]) verify(settingsClient).setGrantTypes([OAuthGrantType.AUTHORIZATION_CODE]) verify(settingsClient).setApplicationType(OpenIdConnectApplicationType.WEB) - verify(settingsClient).put("post_logout_redirect_uris", ["http://localhost:8080/"]) + verify(settingsClient).setPostLogoutRedirectUris(["http://localhost:8080/"]) + } + + @Test + void createNewAppWithPostLogoutUris() { + + String appName = "appLabel-createNewApp" + String appId = "appId-createNewApp" + String groupId = "everyone-id" + + Client client = mock(Client) + ApplicationList appList = mock(ApplicationList) + List apps = [] + RequestBuilder http = mock(RequestBuilder) + ExtensibleResource response = mock(ExtensibleResource) + + OpenIdConnectApplication newApp = mock(OpenIdConnectApplication) + OpenIdConnectApplicationSettings appSettings = mock(OpenIdConnectApplicationSettings) + OpenIdConnectApplicationSettingsClient settingsClient = mock(OpenIdConnectApplicationSettingsClient) + ApplicationGroupAssignment groupAssignment = mock(ApplicationGroupAssignment) + + GroupList groupList = mock(GroupList) + Group group = mock(Group) + + DefaultOidcAppCreator appCreator = new DefaultOidcAppCreator() + + when(client.listApplications(appName, null, null, null)).thenReturn(appList) + + when(newApp.setLabel(appName)).thenReturn(newApp) + when(newApp.setSettings(appSettings)).thenReturn(newApp) + when(appSettings.setOAuthClient(settingsClient)).thenReturn(appSettings) + + when(client.instantiate(OpenIdConnectApplication)).thenReturn(newApp) + when(client.instantiate(OpenIdConnectApplicationSettings)).thenReturn(appSettings) + when(client.instantiate(OpenIdConnectApplicationSettingsClient)).thenReturn(settingsClient) + when(client.instantiate(ApplicationGroupAssignment)).thenReturn(groupAssignment) + + when(settingsClient.setRedirectUris(any(List))).thenReturn(settingsClient) + when(settingsClient.setResponseTypes(any(List))).thenReturn(settingsClient) + when(settingsClient.setGrantTypes(any(List))).thenReturn(settingsClient) + when(settingsClient.setApplicationType(OpenIdConnectApplicationType.WEB)).thenReturn(settingsClient) + + when(client.createApplication(newApp)).thenReturn(newApp) + when(newApp.getId()).thenReturn(appId) + when(appList.stream()).thenReturn(apps.stream()) + + when(groupList.single()).thenReturn(group) + when(group.getId()).thenReturn(groupId) + when(client.listGroups("everyone", null)).thenReturn(groupList) + + when(client.http()).thenReturn(http) + when(http.get("/api/v1/internal/apps/${appId}/settings/clientcreds", ExtensibleResource)).thenReturn(response) + + ExtensibleResource result = appCreator.createOidcApp(client, appName, ["http://localhost:8080/callback", "http://localhost:8080/login/oauth2/code/okta"], ["http://localhost:8080/logout", "http://localhost:8080/logout2"]) + + assertThat result, is(response) + + verify(settingsClient).setRedirectUris(["http://localhost:8080/callback", + "http://localhost:8080/login/oauth2/code/okta"]) + verify(settingsClient).setResponseTypes([OAuthResponseType.CODE]) + verify(settingsClient).setGrantTypes([OAuthGrantType.AUTHORIZATION_CODE]) + verify(settingsClient).setApplicationType(OpenIdConnectApplicationType.WEB) + verify(settingsClient).setPostLogoutRedirectUris(["http://localhost:8080/logout", "http://localhost:8080/logout2"]) } } diff --git a/common/src/test/groovy/com/okta/cli/common/service/DefaultSetupServiceTest.groovy b/common/src/test/groovy/com/okta/cli/common/service/DefaultSetupServiceTest.groovy index 5208fa24..71d05d24 100644 --- a/common/src/test/groovy/com/okta/cli/common/service/DefaultSetupServiceTest.groovy +++ b/common/src/test/groovy/com/okta/cli/common/service/DefaultSetupServiceTest.groovy @@ -138,6 +138,42 @@ class DefaultSetupServiceTest { setupService.authorizationServerService) } + @Test + void createOidcApplicationLogoutRedirectUris() { + + MutablePropertySource propertySource = mock(MutablePropertySource) + String oidcAppName = "test-app-name" + String orgUrl = "https://org.example.com" + String groupClaimName = null + String authorizationServerId = "test-auth-id" + boolean interactive = false + + PowerMockito.mockStatic(Clients) + ClientBuilder clientBuilder = mock(ClientBuilder) + Client client = mock(Client) + when(clientBuilder.build()).thenReturn(client) + when(Clients.builder()).thenReturn(clientBuilder) + + DefaultSetupService setupService = setupService() + ExtensibleResource resource = mock(ExtensibleResource) + when(resource.getString("client_id")).thenReturn("test-client-id") + when(resource.getString("client_secret")).thenReturn("test-client-secret") + when(setupService.oidcAppCreator.createOidcApp(client, oidcAppName, ["https://test.example.com/callback", "https://test.example.com/callback2"], ["https://test.example.com/logout", "https://test.example.com/logout2"])).thenReturn(resource) + + setupService.createOidcApplication(propertySource, oidcAppName, orgUrl, groupClaimName, null, authorizationServerId, interactive, OpenIdConnectApplicationType.WEB, ["https://test.example.com/callback", "https://test.example.com/callback2"], ["https://test.example.com/logout", "https://test.example.com/logout2"]) + + ArgumentCaptor mapCapture = ArgumentCaptor.forClass(Map) + verify(propertySource).addProperties(mapCapture.capture()) + assertThat mapCapture.getValue(), is([ + "okta.oauth2.issuer": "${orgUrl}/oauth2/${authorizationServerId}".toString(), + "okta.oauth2.client-id": "test-client-id", + "okta.oauth2.client-secret": "test-client-secret" + ]) + + // no group claim created + PowerMockito.verifyNoMoreInteractions(setupService.authorizationServerService) + } + @Test void createOidcApplicationNoGroups() { @@ -158,7 +194,7 @@ class DefaultSetupServiceTest { ExtensibleResource resource = mock(ExtensibleResource) when(resource.getString("client_id")).thenReturn("test-client-id") when(resource.getString("client_secret")).thenReturn("test-client-secret") - when(setupService.oidcAppCreator.createOidcApp(client, oidcAppName, [])).thenReturn(resource) + when(setupService.oidcAppCreator.createOidcApp(client, oidcAppName, [], [])).thenReturn(resource) setupService.createOidcApplication(propertySource, oidcAppName, orgUrl, groupClaimName, null, authorizationServerId, interactive, OpenIdConnectApplicationType.WEB) @@ -194,7 +230,7 @@ class DefaultSetupServiceTest { ExtensibleResource resource = mock(ExtensibleResource) when(resource.getString("client_id")).thenReturn("test-client-id") when(resource.getString("client_secret")).thenReturn("test-client-secret") - when(setupService.oidcAppCreator.createOidcApp(client, oidcAppName, [])).thenReturn(resource) + when(setupService.oidcAppCreator.createOidcApp(client, oidcAppName, [], [])).thenReturn(resource) setupService.createOidcApplication(propertySource, oidcAppName, orgUrl, groupClaimName, null, authorizationServerId, interactive, OpenIdConnectApplicationType.WEB) From 016c2d2f3bc61c909ecb8a9acb17d027b046406e Mon Sep 17 00:00:00 2001 From: Brian Demers Date: Tue, 22 Sep 2020 13:36:32 -0400 Subject: [PATCH 2/5] Adds post logout redirect URI to `apps create` command Moves common URL methods to URIs util class --- .../okta/cli/commands/apps/AppsCreate.java | 41 +++++++++++----- .../main/java/com/okta/cli/common/URIs.java | 49 +++++++++++++++++++ .../com/okta/cli/common/URIsTest.groovy | 41 ++++++++++++++++ .../com/okta/cli/test/AppsCreateIT.groovy | 13 +++++ 4 files changed, 131 insertions(+), 13 deletions(-) create mode 100644 common/src/main/java/com/okta/cli/common/URIs.java create mode 100644 common/src/test/groovy/com/okta/cli/common/URIsTest.groovy diff --git a/cli/src/main/java/com/okta/cli/commands/apps/AppsCreate.java b/cli/src/main/java/com/okta/cli/commands/apps/AppsCreate.java index c26ed343..a5460cba 100644 --- a/cli/src/main/java/com/okta/cli/commands/apps/AppsCreate.java +++ b/cli/src/main/java/com/okta/cli/commands/apps/AppsCreate.java @@ -20,6 +20,7 @@ import com.okta.cli.commands.apps.templates.ServiceAppTemplate; import com.okta.cli.commands.apps.templates.SpaAppTemplate; import com.okta.cli.commands.apps.templates.WebAppTemplate; +import com.okta.cli.common.URIs; import com.okta.cli.common.config.MapPropertySource; import com.okta.cli.common.config.MutablePropertySource; import com.okta.cli.common.model.AuthorizationServer; @@ -28,19 +29,18 @@ import com.okta.cli.common.service.DefaultSetupService; import com.okta.cli.console.ConsoleOutput; import com.okta.cli.console.Prompter; +import com.okta.commons.lang.Assert; import com.okta.sdk.client.Client; import com.okta.sdk.client.Clients; import com.okta.sdk.resource.application.OpenIdConnectApplicationType; import picocli.CommandLine; import java.io.IOException; -import java.net.URI; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.stream.Collectors; -import java.util.stream.IntStream; @CommandLine.Command(name = "create", description = "Create an new Okta app") @@ -104,14 +104,14 @@ private int createWebApp(String appName, WebAppTemplate webAppTemplate) throws I List redirectUris = getRedirectUris(Map.of("Spring Security", "http://localhost:8080/login/oauth2/code/okta", "JHipster", "http://localhost:8080/login/oauth2/code/oidc"), appTemplate.getDefaultRedirectUri()); - + List postLogoutRedirectUris = getPostLogoutRedirectUris(redirectUris); Client client = Clients.builder().build(); AuthorizationServer issuer = getIssuer(client); String baseUrl = getBaseUrl(); String groupClaimName = appTemplate.getGroupsClaim(); MutablePropertySource propertySource = appCreationMixin.getPropertySource(appTemplate.getDefaultConfigFileName()); - new DefaultSetupService(appTemplate.getSpringPropertyKey()).createOidcApplication(propertySource, appName, baseUrl, groupClaimName, issuer.getIssuer(), issuer.getId(), true, OpenIdConnectApplicationType.WEB, redirectUris); + new DefaultSetupService(appTemplate.getSpringPropertyKey()).createOidcApplication(propertySource, appName, baseUrl, groupClaimName, issuer.getIssuer(), issuer.getId(), true, OpenIdConnectApplicationType.WEB, redirectUris, postLogoutRedirectUris); out.writeLine("Okta application configuration has been written to: " + propertySource.getName()); @@ -122,19 +122,16 @@ private Integer createNativeApp(String appName) throws IOException { ConsoleOutput out = standardOptions.getEnvironment().getConsoleOutput(); String baseUrl = getBaseUrl(); - - String[] parts = URI.create(baseUrl).getHost().split("\\."); - String reverseDomain = IntStream.rangeClosed(1, parts.length) - .mapToObj(i -> parts[parts.length - i]) - .collect(Collectors.joining(".")); + String reverseDomain = URIs.reverseDomain(baseUrl); String defaultRedirectUri = reverseDomain + ":/callback"; - List redirectUris = getRedirectUris(Map.of("Reverse Domain name", "com.example:/callback"), defaultRedirectUri); + List redirectUris = getRedirectUris(Map.of("Reverse Domain name", defaultRedirectUri), defaultRedirectUri); + List postLogoutRedirectUris = getPostLogoutRedirectUris(redirectUris); Client client = Clients.builder().build(); AuthorizationServer issuer = getIssuer(client); MutablePropertySource propertySource = new MapPropertySource(); - new DefaultSetupService(null).createOidcApplication(propertySource, appName, baseUrl, null, issuer.getIssuer(), issuer.getId(), standardOptions.getEnvironment().isInteractive(), OpenIdConnectApplicationType.NATIVE, redirectUris); + new DefaultSetupService(null).createOidcApplication(propertySource, appName, baseUrl, null, issuer.getIssuer(), issuer.getId(), standardOptions.getEnvironment().isInteractive(), OpenIdConnectApplicationType.NATIVE, redirectUris, postLogoutRedirectUris); out.writeLine("Okta application configuration: "); propertySource.getProperties().forEach((key, value) -> { @@ -171,11 +168,12 @@ private Integer createSpaApp(String appName) throws IOException { String baseUrl = getBaseUrl(); List redirectUris = getRedirectUris(Map.of("/callback", "http://localhost:8080/callback"), SpaAppTemplate.GENERIC.getDefaultRedirectUri()); + List postLogoutRedirectUris = getPostLogoutRedirectUris(redirectUris); Client client = Clients.builder().build(); AuthorizationServer authorizationServer = getIssuer(client); MutablePropertySource propertySource = new MapPropertySource(); - new DefaultSetupService(null).createOidcApplication(propertySource, appName, baseUrl, null, authorizationServer.getIssuer(), authorizationServer.getId(), standardOptions.getEnvironment().isInteractive(), OpenIdConnectApplicationType.BROWSER, redirectUris); + new DefaultSetupService(null).createOidcApplication(propertySource, appName, baseUrl, null, authorizationServer.getIssuer(), authorizationServer.getId(), standardOptions.getEnvironment().isInteractive(), OpenIdConnectApplicationType.BROWSER, redirectUris, postLogoutRedirectUris); out.writeLine("Okta application configuration: "); out.bold("Issuer: "); @@ -214,7 +212,24 @@ private List getRedirectUris(Map commonExamples, String redirectUriPrompt.append("Enter your Redirect URI"); String result = prompter.promptIfEmpty(appCreationMixin.redirectUri, redirectUriPrompt.toString(), defaultRedirectUri).trim(); - result = result.replaceFirst("^\\[", ""); + return split(result); + } + + private List getPostLogoutRedirectUris(List redirectUris) { + Prompter prompter = standardOptions.getEnvironment().prompter(); + + Assert.notEmpty(redirectUris, "Redirect Uris cannot be empty"); + String defaultPostLogoutUri = redirectUris.stream() + .findFirst() + .map(URIs::baseUrlOf) + .get(); + + String result = prompter.promptIfEmpty(appCreationMixin.redirectUri, "Enter your Post Logout Redirect URI", defaultPostLogoutUri).trim(); + return split(result); + } + + private List split(String input) { + String result = input.replaceFirst("^\\[", ""); result = result.replaceFirst("]$", ""); return Arrays.stream(result.split(",")) diff --git a/common/src/main/java/com/okta/cli/common/URIs.java b/common/src/main/java/com/okta/cli/common/URIs.java new file mode 100644 index 00000000..b6de7cd2 --- /dev/null +++ b/common/src/main/java/com/okta/cli/common/URIs.java @@ -0,0 +1,49 @@ +/* + * Copyright 2020-Present Okta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.okta.cli.common; + +import java.net.URI; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public final class URIs { + + private URIs() {} + + public static String reverseDomain(String uri) { + URI parsedUri = URI.create(uri); + + // if the parsed URI does NOT have a protocol, assume it's just a host name + if (parsedUri.getScheme() == null) { + return dnsReverse(uri); + } + + return dnsReverse(parsedUri.getHost()); + } + + public static String baseUrlOf(String url) { + return URI.create(url).resolve("/").toString(); + } + + private static String dnsReverse(String host) { + String[] parts = host.split("\\."); + String reverseDomain = IntStream.rangeClosed(1, parts.length) + .mapToObj(i -> parts[parts.length - i]) + .collect(Collectors.joining(".")); + + return reverseDomain; + } +} diff --git a/common/src/test/groovy/com/okta/cli/common/URIsTest.groovy b/common/src/test/groovy/com/okta/cli/common/URIsTest.groovy new file mode 100644 index 00000000..060566be --- /dev/null +++ b/common/src/test/groovy/com/okta/cli/common/URIsTest.groovy @@ -0,0 +1,41 @@ +/* + * Copyright 2020-Present Okta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.okta.cli.common + + +import org.testng.annotations.Test + +import static org.hamcrest.Matchers.equalTo +import static org.hamcrest.MatcherAssert.assertThat + +class URIsTest { + + @Test + void reverseDomain() { + assertThat URIs.reverseDomain("http://example.com"), equalTo("com.example") + assertThat URIs.reverseDomain("https://foo.example.com"), equalTo("com.example.foo") + assertThat URIs.reverseDomain("foo.example.com"), equalTo("com.example.foo") + assertThat URIs.reverseDomain("ionic://foo.example.com"), equalTo("com.example.foo") + } + + @Test + void baseUrl() { + assertThat URIs.baseUrlOf("http://example.com"), equalTo("http://example.com/") + assertThat URIs.baseUrlOf("http://example.com/foo/bar"), equalTo("http://example.com/") + assertThat URIs.baseUrlOf("ionic://foo.example.com"), equalTo("ionic://foo.example.com/") + assertThat URIs.baseUrlOf("com.example.foo:/callback"), equalTo("com.example.foo:/") + } +} diff --git a/integration-tests/src/test/groovy/com/okta/cli/test/AppsCreateIT.groovy b/integration-tests/src/test/groovy/com/okta/cli/test/AppsCreateIT.groovy index 4612e724..82eb0703 100644 --- a/integration-tests/src/test/groovy/com/okta/cli/test/AppsCreateIT.groovy +++ b/integration-tests/src/test/groovy/com/okta/cli/test/AppsCreateIT.groovy @@ -51,6 +51,7 @@ class AppsCreateIT implements MockWebSupport, CreateAppSupport { "", // default of "test-project" "2", // type of app choice "spa" "", // default callback "http://localhost:8080/callback" + "", // default post logout redirect ] def result = new CommandRunner() @@ -59,6 +60,8 @@ class AppsCreateIT implements MockWebSupport, CreateAppSupport { assertThat result, resultMatches(0, allOf( + containsString("Enter your Redirect URI [http://localhost:8080/callback]:"), + containsString("Enter your Post Logout Redirect URI [http://localhost:8080/]:"), containsString("Okta application configuration:"), containsString("Client ID: test-id"), containsString("Issuer: ${mockWebServer.url("/")}/oauth2/test-as"), @@ -95,6 +98,7 @@ class AppsCreateIT implements MockWebSupport, CreateAppSupport { "", // default of "test-project" "3", // "native" type of app choice "", // default callback "localhost:/callback" + "", // default post logout redirect localhost:/ ] def result = new CommandRunner() @@ -105,6 +109,8 @@ class AppsCreateIT implements MockWebSupport, CreateAppSupport { assertThat result, resultMatches(0, allOf( containsString("Okta application configuration:"), containsString("okta.oauth2.client-id: test-id"), + containsString("Enter your Redirect URI [localhost:/callback]:"), + containsString("Enter your Post Logout Redirect URI [localhost:/]:"), containsString("okta.oauth2.issuer: ${mockWebServer.url("/")}/oauth2/test-as"), not(containsString("okta.oauth2.client-secret"))), null) @@ -140,6 +146,7 @@ class AppsCreateIT implements MockWebSupport, CreateAppSupport { "", // default "web" type of app choice "", // default of "test-project" "", // default callback "http://localhost:8080/callback" + "", // default post logout redirect http://localhost:8080/ ] def result = new CommandRunner() @@ -149,6 +156,8 @@ class AppsCreateIT implements MockWebSupport, CreateAppSupport { assertThat result, resultMatches(0, allOf( containsString("Created OIDC application, client-id: test-id"), containsString("Okta application configuration has been written to"), + containsString("Enter your Redirect URI [http://localhost:8080/callback]:"), + containsString("Enter your Post Logout Redirect URI [http://localhost:8080/]:"), containsString(".okta.env")), null) @@ -182,6 +191,7 @@ class AppsCreateIT implements MockWebSupport, CreateAppSupport { "", // default of "test-project" "4", // "native" type of app choice "", // default callback "localhost:/callback" + "", // default post logout redirect ] def result = new CommandRunner() @@ -221,6 +231,7 @@ class AppsCreateIT implements MockWebSupport, CreateAppSupport { "", // default "web" type of app choice "", // default of "test-project" "", // default callback "http://localhost:8080/login/oauth2/code/okta" + "", // default post logout redirect ] def result = new CommandRunner() @@ -230,6 +241,8 @@ class AppsCreateIT implements MockWebSupport, CreateAppSupport { assertThat result, resultMatches(0, allOf( containsString("Created OIDC application, client-id: test-id"), containsString("Okta application configuration has been written to"), + containsString("Enter your Redirect URI [http://localhost:8080/login/oauth2/code/okta]:"), + containsString("Enter your Post Logout Redirect URI [http://localhost:8080/]:"), containsString("application.properties")), null) From 10782f84d8e02d8477a9abec7a7473a911b7e382 Mon Sep 17 00:00:00 2001 From: Brian Demers Date: Mon, 21 Sep 2020 15:29:05 -0400 Subject: [PATCH 3/5] Adds support for filtering values into .okta/samples-config.yaml * Adds `CLI_OKTA_REVERSE_DOMAIN` to available list of filter values * Adds basic tests --- .../java/com/okta/cli/commands/Start.java | 11 ++++- .../cli/common/model/FilterConfigBuilder.java | 21 +++++++++- .../service/DefaultSampleConfigParser.java | 42 +++++++++++-------- .../common/service/SampleConfigParser.java | 17 +++++--- .../model/FilterConfigBuilderTest.groovy | 42 +++++++++++++++++++ .../DefaultSampleConfigParserTest.groovy | 38 +++++++++++++++++ 6 files changed, 147 insertions(+), 24 deletions(-) create mode 100644 common/src/test/groovy/com/okta/cli/common/model/FilterConfigBuilderTest.groovy create mode 100644 common/src/test/groovy/com/okta/cli/common/service/DefaultSampleConfigParserTest.groovy diff --git a/cli/src/main/java/com/okta/cli/commands/Start.java b/cli/src/main/java/com/okta/cli/commands/Start.java index 6e57d9ac..b07ad793 100644 --- a/cli/src/main/java/com/okta/cli/commands/Start.java +++ b/cli/src/main/java/com/okta/cli/commands/Start.java @@ -23,9 +23,11 @@ import com.okta.cli.common.model.FilterConfigBuilder; import com.okta.cli.common.model.OktaSampleConfig; import com.okta.cli.common.model.SamplesListings; +import com.okta.cli.common.service.ClientConfigurationException; import com.okta.cli.common.service.DefaultInterpolator; import com.okta.cli.common.service.DefaultSampleConfigParser; import com.okta.cli.common.service.DefaultSamplesService; +import com.okta.cli.common.service.DefaultSdkConfigurationService; import com.okta.cli.common.service.DefaultSetupService; import com.okta.cli.common.service.TarballExtractor; import com.okta.cli.console.ConsoleOutput; @@ -112,8 +114,11 @@ public Integer call() throws Exception { extractedProject = true; } + // TODO need to better abstract away the ~/.okta/okta.yaml config values + Map sampleContext = new FilterConfigBuilder().setOrgUrl(oktaBaseUrl()).build(); + // parse the `.okta.yaml` file - OktaSampleConfig config = new DefaultSampleConfigParser().loadConfig(projectDirectory); + OktaSampleConfig config = new DefaultSampleConfigParser().loadConfig(projectDirectory, sampleContext); // default to SPA application OpenIdConnectApplicationType applicationType = Optional.ofNullable(config.getOAuthClient().getApplicationType()) .map(it -> it.toUpperCase(Locale.ENGLISH)) @@ -201,4 +206,8 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attr) { return FileVisitResult.CONTINUE; } } + + private String oktaBaseUrl() throws ClientConfigurationException { + return new DefaultSdkConfigurationService().loadUnvalidatedConfiguration().getBaseUrl(); + } } diff --git a/common/src/main/java/com/okta/cli/common/model/FilterConfigBuilder.java b/common/src/main/java/com/okta/cli/common/model/FilterConfigBuilder.java index eeada7bc..91f44828 100644 --- a/common/src/main/java/com/okta/cli/common/model/FilterConfigBuilder.java +++ b/common/src/main/java/com/okta/cli/common/model/FilterConfigBuilder.java @@ -21,6 +21,8 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; public class FilterConfigBuilder { @@ -29,6 +31,7 @@ public class FilterConfigBuilder { private static final String CLI_OKTA_ISSUER_ID = "CLI_OKTA_ISSUER_ID"; private static final String CLI_OKTA_CLIENT_ID = "CLI_OKTA_CLIENT_ID"; private static final String CLI_OKTA_CLIENT_SECRET = "CLI_OKTA_CLIENT_SECRET"; + private static final String CLI_OKTA_REVERSE_DOMAIN = "CLI_OKTA_REVERSE_DOMAIN"; private final Map filterValues = new HashMap<>(); @@ -48,7 +51,23 @@ public FilterConfigBuilder setIssuerId(String issuerId) { public FilterConfigBuilder setIssuer(String issuer) { filterValues.put(CLI_OKTA_ISSUER, issuer); - filterValues.put(CLI_OKTA_ORG_URL, URI.create(issuer).resolve("/").toString()); + setOrgUrl(URI.create(issuer).resolve("/").toString()); + return this; + } + + public FilterConfigBuilder setOrgUrl(String orgUrl) { + filterValues.put(CLI_OKTA_ORG_URL, orgUrl); + + String[] hostParts = URI.create(orgUrl).getHost().split("\\."); + String reverseDomain = IntStream.rangeClosed(1, hostParts.length) + .mapToObj(i -> hostParts[hostParts.length - i]) + .collect(Collectors.joining(".")); + setReverseDomain(reverseDomain); + return this; + } + + public FilterConfigBuilder setReverseDomain(String reverseDomain) { + filterValues.put(CLI_OKTA_REVERSE_DOMAIN, reverseDomain); return this; } diff --git a/common/src/main/java/com/okta/cli/common/service/DefaultSampleConfigParser.java b/common/src/main/java/com/okta/cli/common/service/DefaultSampleConfigParser.java index 6ce40017..41bc4b1b 100644 --- a/common/src/main/java/com/okta/cli/common/service/DefaultSampleConfigParser.java +++ b/common/src/main/java/com/okta/cli/common/service/DefaultSampleConfigParser.java @@ -20,27 +20,35 @@ import org.yaml.snakeyaml.representer.Representer; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Map; public class DefaultSampleConfigParser implements SampleConfigParser { - @Override - public OktaSampleConfig parseConfig(File configFile) throws IOException { - try (FileInputStream fileInputStream = new FileInputStream(configFile.getAbsoluteFile())) { - - // ignore unknown properties, so we can add additional features and not break older clients - Representer representer = new Representer(); - representer.getPropertyUtils().setSkipMissingProperties(true); - OktaSampleConfig config = new Yaml(representer).loadAs(fileInputStream, OktaSampleConfig.class); - - // TODO improve validation of configuration - if (config.getOAuthClient() == null) { - throw new IllegalArgumentException("Sample configuration file: '" + configFile.getAbsoluteFile() + - "' must contain an 'oauthClient' element, see: " + - "https://github.com/oktadeveloper/okta-cli/wiki/Create-an-Okta-Start-Samples"); - } - return config; + + public OktaSampleConfig parseConfig(File configFile, Map context) throws IOException { + + // NOTE this is not the most memory efficient way to do this, but this file is small + // if we need something more complex we can do that later. + String configFileContent = Files.readString(configFile.toPath().toAbsolutePath(), StandardCharsets.UTF_8); + + // filter the file + configFileContent = new DefaultInterpolator().interpolate(configFileContent, context); + + // ignore unknown properties, so we can add additional features and not break older clients + Representer representer = new Representer(); + representer.getPropertyUtils().setSkipMissingProperties(true); + + OktaSampleConfig config = new Yaml(representer).loadAs(configFileContent, OktaSampleConfig.class); + + // TODO improve validation of configuration + if (config.getOAuthClient() == null) { + throw new IllegalArgumentException("Sample configuration file: '" + configFile.getAbsoluteFile() + + "' must contain an 'oauthClient' element, see: " + + "https://github.com/oktadeveloper/okta-cli/wiki/Create-an-Okta-Start-Samples"); } + return config; } } diff --git a/common/src/main/java/com/okta/cli/common/service/SampleConfigParser.java b/common/src/main/java/com/okta/cli/common/service/SampleConfigParser.java index d6e4f466..7cd2d30d 100644 --- a/common/src/main/java/com/okta/cli/common/service/SampleConfigParser.java +++ b/common/src/main/java/com/okta/cli/common/service/SampleConfigParser.java @@ -19,18 +19,25 @@ import java.io.File; import java.io.IOException; +import java.util.Map; + +import static java.util.Collections.emptyMap; public interface SampleConfigParser { String SAMPLE_CONFIG_PATH = ".okta/sample-config.yaml"; - default OktaSampleConfig loadConfig() throws IOException { - return parseConfig(new File(SAMPLE_CONFIG_PATH)); - } - default OktaSampleConfig loadConfig(File localPath) throws IOException { return parseConfig(new File(localPath, SAMPLE_CONFIG_PATH)); } - OktaSampleConfig parseConfig(File configFile) throws IOException; + default OktaSampleConfig loadConfig(File localPath, Map context) throws IOException { + return parseConfig(new File(localPath, SAMPLE_CONFIG_PATH), context); + } + + default OktaSampleConfig parseConfig(File configFile) throws IOException { + return parseConfig(configFile, emptyMap()); + } + + OktaSampleConfig parseConfig(File configFile, Map context) throws IOException; } \ No newline at end of file diff --git a/common/src/test/groovy/com/okta/cli/common/model/FilterConfigBuilderTest.groovy b/common/src/test/groovy/com/okta/cli/common/model/FilterConfigBuilderTest.groovy new file mode 100644 index 00000000..898d7603 --- /dev/null +++ b/common/src/test/groovy/com/okta/cli/common/model/FilterConfigBuilderTest.groovy @@ -0,0 +1,42 @@ +/* + * Copyright 2020-Present Okta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.okta.cli.common.model + + +import org.testng.annotations.Test + +import static org.hamcrest.MatcherAssert.assertThat +import static org.hamcrest.Matchers.equalTo +import static org.mockito.Mockito.spy +import static org.mockito.Mockito.verify + +class FilterConfigBuilderTest { + + @Test + void issuerTest() { + // setting the issuer should cascade and call setOrgUrl, and setReverseDomain + FilterConfigBuilder configBuilder = spy(new FilterConfigBuilder()) + configBuilder.setIssuer("https://foobar.example.com/my/issuer") + def result = configBuilder.build() + + assertThat result, equalTo([CLI_OKTA_ORG_URL: "https://foobar.example.com/", + CLI_OKTA_ISSUER: "https://foobar.example.com/my/issuer", + CLI_OKTA_REVERSE_DOMAIN: "com.example.foobar"]) + + verify(configBuilder).setOrgUrl("https://foobar.example.com/") + verify(configBuilder).setReverseDomain("com.example.foobar") + } +} \ No newline at end of file diff --git a/common/src/test/groovy/com/okta/cli/common/service/DefaultSampleConfigParserTest.groovy b/common/src/test/groovy/com/okta/cli/common/service/DefaultSampleConfigParserTest.groovy new file mode 100644 index 00000000..dd021050 --- /dev/null +++ b/common/src/test/groovy/com/okta/cli/common/service/DefaultSampleConfigParserTest.groovy @@ -0,0 +1,38 @@ +/* + * Copyright 2020-Present Okta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.okta.cli.common.service + +import com.okta.cli.common.model.OktaSampleConfig +import org.hamcrest.MatcherAssert +import org.testng.annotations.Test + +import static org.hamcrest.Matchers.equalTo + +class DefaultSampleConfigParserTest { + + @Test + void basicFilterTest() { + + File file = File.createTempFile("basicFilterTest", "-sample.yaml") + file << "oauthClient:\n" + file << " redirectUris:\n" + file << ' - ${CLI_OKTA_REVERSE_DOMAIN}.myApp://endpoint\n' + file << ' - http://example.com/foo\n' + + OktaSampleConfig config = new DefaultSampleConfigParser().parseConfig(file, [CLI_OKTA_REVERSE_DOMAIN: "com.example.id"]) + MatcherAssert.assertThat config.getOAuthClient().redirectUris, equalTo(["com.example.id.myApp://endpoint", "http://example.com/foo"]) + } +} From c2d076f2a9d8d82a3501bc91ed8ad3a5d610ae86 Mon Sep 17 00:00:00 2001 From: Brian Demers Date: Tue, 22 Sep 2020 16:31:57 -0400 Subject: [PATCH 4/5] Move default sample logic out of start command --- .../java/com/okta/cli/commands/Start.java | 8 ++----- .../cli/common/model/OktaSampleConfig.java | 2 +- .../DefaultSampleConfigParserTest.groovy | 21 +++++++++++++++++-- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/cli/src/main/java/com/okta/cli/commands/Start.java b/cli/src/main/java/com/okta/cli/commands/Start.java index b07ad793..9f0d312d 100644 --- a/cli/src/main/java/com/okta/cli/commands/Start.java +++ b/cli/src/main/java/com/okta/cli/commands/Start.java @@ -49,7 +49,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Optional; import java.util.concurrent.Callable; import java.util.stream.Collectors; @@ -119,11 +118,8 @@ public Integer call() throws Exception { // parse the `.okta.yaml` file OktaSampleConfig config = new DefaultSampleConfigParser().loadConfig(projectDirectory, sampleContext); - // default to SPA application - OpenIdConnectApplicationType applicationType = Optional.ofNullable(config.getOAuthClient().getApplicationType()) - .map(it -> it.toUpperCase(Locale.ENGLISH)) - .map(OpenIdConnectApplicationType::valueOf) - .orElse(OpenIdConnectApplicationType.BROWSER); + OpenIdConnectApplicationType applicationType = OpenIdConnectApplicationType.valueOf( + config.getOAuthClient().getApplicationType().toUpperCase(Locale.ENGLISH)); // create the Okta application Client client = Clients.builder().build(); diff --git a/common/src/main/java/com/okta/cli/common/model/OktaSampleConfig.java b/common/src/main/java/com/okta/cli/common/model/OktaSampleConfig.java index 70aa9579..60f17e1f 100644 --- a/common/src/main/java/com/okta/cli/common/model/OktaSampleConfig.java +++ b/common/src/main/java/com/okta/cli/common/model/OktaSampleConfig.java @@ -50,6 +50,6 @@ public void setOauthClient(OAuthClient oauthClient) { public static class OAuthClient { private List redirectUris; private List postLogoutRedirectUris; - private String applicationType; + private String applicationType = "browser"; } } diff --git a/common/src/test/groovy/com/okta/cli/common/service/DefaultSampleConfigParserTest.groovy b/common/src/test/groovy/com/okta/cli/common/service/DefaultSampleConfigParserTest.groovy index dd021050..f11f0071 100644 --- a/common/src/test/groovy/com/okta/cli/common/service/DefaultSampleConfigParserTest.groovy +++ b/common/src/test/groovy/com/okta/cli/common/service/DefaultSampleConfigParserTest.groovy @@ -16,10 +16,10 @@ package com.okta.cli.common.service import com.okta.cli.common.model.OktaSampleConfig -import org.hamcrest.MatcherAssert import org.testng.annotations.Test import static org.hamcrest.Matchers.equalTo +import static org.hamcrest.MatcherAssert.assertThat class DefaultSampleConfigParserTest { @@ -31,8 +31,25 @@ class DefaultSampleConfigParserTest { file << " redirectUris:\n" file << ' - ${CLI_OKTA_REVERSE_DOMAIN}.myApp://endpoint\n' file << ' - http://example.com/foo\n' + file << ' applicationType: web\n' OktaSampleConfig config = new DefaultSampleConfigParser().parseConfig(file, [CLI_OKTA_REVERSE_DOMAIN: "com.example.id"]) - MatcherAssert.assertThat config.getOAuthClient().redirectUris, equalTo(["com.example.id.myApp://endpoint", "http://example.com/foo"]) + assertThat config.getOAuthClient().redirectUris, equalTo(["com.example.id.myApp://endpoint", "http://example.com/foo"]) + assertThat config.getOAuthClient().getApplicationType(), equalTo("web") + + } + + @Test + void defaultsTest() { + + File file = File.createTempFile("basicFilterTest", "-sample.yaml") + file << "oauthClient:\n" + file << " redirectUris:\n" + file << ' - http://example.com/foo\n' + + OktaSampleConfig config = new DefaultSampleConfigParser().parseConfig(file) + assertThat config.getOAuthClient().redirectUris, equalTo(["http://example.com/foo"]) + assertThat config.getOAuthClient().getApplicationType(), equalTo("browser") + } } From fdea4bed35a037366f961341128b17b66a1f6b6a Mon Sep 17 00:00:00 2001 From: Brian Demers Date: Fri, 25 Sep 2020 10:45:10 -0400 Subject: [PATCH 5/5] Bump version to 0.7 --- cli/pom.xml | 2 +- common/pom.xml | 2 +- coverage/pom.xml | 2 +- integration-tests/pom.xml | 2 +- pom.xml | 8 ++++---- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index f438670f..03b62ea1 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -20,7 +20,7 @@ com.okta.cli okta-cli-tools - 0.6.1-SNAPSHOT + 0.7.0-SNAPSHOT okta-cli diff --git a/common/pom.xml b/common/pom.xml index 2c3499bc..685f4b6e 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ com.okta.cli okta-cli-tools - 0.6.1-SNAPSHOT + 0.7.0-SNAPSHOT okta-cli-common diff --git a/coverage/pom.xml b/coverage/pom.xml index 0ddfa6ad..082b0301 100644 --- a/coverage/pom.xml +++ b/coverage/pom.xml @@ -21,7 +21,7 @@ com.okta.cli okta-cli-tools - 0.6.1-SNAPSHOT + 0.7.0-SNAPSHOT okta-cli-coverage diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 1a79d25e..0f38da39 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -20,7 +20,7 @@ com.okta.cli okta-cli-tools - 0.6.1-SNAPSHOT + 0.7.0-SNAPSHOT okta-cli-its diff --git a/pom.xml b/pom.xml index 31c461ea..126a77ba 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ com.okta.cli okta-cli-tools - 0.6.1-SNAPSHOT + 0.7.0-SNAPSHOT pom Okta CLI Tools @@ -73,19 +73,19 @@ com.okta.cli okta-cli-common - 0.6.1-SNAPSHOT + 0.7.0-SNAPSHOT com.okta.cli okta-cli - 0.6.1-SNAPSHOT + 0.7.0-SNAPSHOT com.okta.cli okta-cli-its - 0.6.1-SNAPSHOT + 0.7.0-SNAPSHOT