diff --git a/OktaGettingStarted.md b/OktaGettingStarted.md new file mode 100644 index 0000000000..516d2ba390 --- /dev/null +++ b/OktaGettingStarted.md @@ -0,0 +1,103 @@ + +Getting Started With Okta +========================= + +Okay, so you have been using Stormpath for a while and now you want to checkout out Okta. This guide will walk through +setting up a new account, creating an API token, and everything else needed to get you up and running on Okta. + +Create an Okta Developer Account +-------------------------------- + +1. Head on over to: https://www.okta.com/developer/signup/ +2. Fill out the signup form, and click the "Get Started" button +3. Within a few minutes you will get a conformation email +4. Follow the instructions in the email to finish setting up your account + + +Your Okta URLs +-------------- + +When setting up a developer account, you end up with a couple URL: +An admin console URL that looks something like this: + +https://dev-123456-admin.oktapreview.com/admin/getting-started + +Use this one to manually change organization settings, create users, or other general administrative work. The other URL looks similar, but is missing the `admin` part: + +https://dev-123456.oktapreview.com/ + +This is the one your yours could interact with, and will be the base URL for any API access. + +**Important:** The second URL (the non-admin one) is the one you will need to remember, you will use this one for API access. + +Setup your Okta 'Organization' +------------------------------ + +### Create an Application + +1. Navigate to your Admin console: i.e. https://dev-123456-admin.oktapreview.com/admin/dashboard +2. On the top menu click on 'Applications' +3. Press the 'Add Application' button +4. Press the 'Create New App' button +5. On the 'Create a New Application Integration' popup fill select the following values, then press the 'Create' button + - Platform - Native + - Sign-on Method - OpenID Connect +6. On the 'Create OpenID Connect Integration' page enter the following values, and press the 'Next' button + - Application Name - 'My Test Application' +7. Use `http://localhost:8080/client/callback` for the Redirect URI's, and press the 'Finish' button +8. Your application has been created, but you still have a few settings to change. On the 'General' tab, click the 'Edit' button on the 'General Settings' panel +9. Select the 'Refresh Token', and 'Resource Owner Password' checkboxes and click the 'Save' button +10. Click the 'Edit' button on the 'Client Credentials' panel +11. Select the 'Use Client Authentication' radio button, and press the 'Save' button +12. Click on the 'Groups' tab +13. Select the 'Assign' button in the 'Everyone' column, and press the 'Done' button +14. Grab the ID portion of the URL of your browsers current page, for example: if my URL was: `https://dev-123456-admin.oktapreview.com/admin/app/oidc_client/instance/00icu81200icu812/#tab-groups` then `00icu81200icu812` would be your application's ID + +**Important:** You will need to remember your application's ID. + +### Create an Access Token + +1. Navigate to your Admin console: i.e. https://dev-123456-admin.oktapreview.com/admin/dashboard +2. On the top menu click on 'Security' -> 'API' +3. Select the 'Tokens' tab +4. Press the 'Create Token' button +5. On the popup, give your new token a name, for example: 'My Test Token', and press the 'Create Token' button + +**Important:** You will need to remember this token value, so copy/paste it somewhere safe. + +For more information take a look at the official [Create an API token](http://developer.okta.com/docs/api/getting_started/getting_a_token.html) guide. + + +Run an Example Application +-------------------------- + +Since you are reading this page on Github, I'm going to assume you know how to clone this repo, and switch to the `okta` branch, once you have done that, build the current SNAPSHOT with Apache Maven. + +``` bash +$ mvn clean install +``` + +This should not take more then a couple minutes. + +Once complete change directories to examples/spring-boot-default + +``` bash +$ cd examples/spring-boot-default +``` + +The last step before running our example is to set your configuration variables, there are a [few different ways](https://docs.stormpath.com/java/servlet-plugin/config.html) you can do this, but I'll just use environment variables here. + +``` bash +$ export STORMPATH_CLIENT_BASEURL=[baseurl_from_above] +$ export OKTA_APPLICATION_ID=[aapplication_id_from_above] +$ export OKTA_API_TOKEN=[api_token_from_above] +``` + +Start it up! + +``` bash +$ mvn spring-boot:run +``` + +Point your browser to: http://localhost:8080 and start using the example application! + diff --git a/README.md b/README.md index ff3d3d7b85..e680c2679d 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,22 @@ We are incredibly excited to announce that [Stormpath is joining forces with Okt We're available to answer all questions at [support@stormpath.com](mailto:support@stormpath.com). +## Okta Support Branch + +The aim of this branch is to port the Stormpath Java integrations (Spring, Spring-Boot, and Servlet) to work with Okta's API instead of Stormpath. + +Take a look at the [Getting Started With Okta](OktaGettingStarted.md) guide for instructions on how to get started. + +**Work on this branch is on going**, but if you want to try it out you can build it with Apache Maven: `mvn install` + +You will also need to set the following properties (these can be set the same way as your [existing Stormpath configuration properties](https://docs.stormpath.com/java/servlet-plugin/config.html#id10)). + +| Key | Description | +|-----|-------------| +| okta.api.token | [An Okta API key](http://developer.okta.com/docs/api/getting_started/getting_a_token.html) | +| okta.application.id | You find your Application's id with an [API call](http://developer.okta.com/docs/api/resources/apps.html), or by opening your 'application' config in the Okta Admin console and grab the ID from your browsers URL | +| stormpath.client.baseUrl | The base url of your Okta organization, for example in a preview enviornment this would be something like: https://dev-123456.oktapreview.com | + # Stormpath Java SDK # *An advanced, reliable and easy-to-use user management API, built by Java security experts* diff --git a/api/pom.xml b/api/pom.xml index 2b631cc486..e2067b8e6a 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,7 +21,7 @@ com.stormpath.sdk stormpath-sdk-root - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml diff --git a/api/src/main/java/com/stormpath/sdk/application/OAuthApplication.java b/api/src/main/java/com/stormpath/sdk/application/OAuthApplication.java new file mode 100644 index 0000000000..de91735774 --- /dev/null +++ b/api/src/main/java/com/stormpath/sdk/application/OAuthApplication.java @@ -0,0 +1,34 @@ +package com.stormpath.sdk.application; + +import com.stormpath.sdk.oauth.IdSiteAuthenticator; +import com.stormpath.sdk.oauth.OAuthBearerRequestAuthenticator; +import com.stormpath.sdk.oauth.OAuthClientCredentialsGrantRequestAuthenticator; +import com.stormpath.sdk.oauth.OAuthPasswordGrantRequestAuthenticator; +import com.stormpath.sdk.oauth.OAuthRefreshTokenRequestAuthenticator; +import com.stormpath.sdk.oauth.OAuthStormpathFactorChallengeGrantRequestAuthenticator; +import com.stormpath.sdk.oauth.OAuthStormpathSocialGrantRequestAuthenticator; +import com.stormpath.sdk.oauth.OAuthTokenRevocator; + +/** + * Marks an Application as supporting OAuth and adds required methods for handling tokens. + */ +public interface OAuthApplication extends Application { + + OAuthClientCredentialsGrantRequestAuthenticator createClientCredentialsGrantAuthenticator(); + + OAuthStormpathSocialGrantRequestAuthenticator createStormpathSocialGrantAuthenticator(); + + OAuthStormpathFactorChallengeGrantRequestAuthenticator createStormpathFactorChallengeGrantAuthenticator(); + + OAuthPasswordGrantRequestAuthenticator createPasswordGrantAuthenticator(); + + OAuthRefreshTokenRequestAuthenticator createRefreshGrantAuthenticator(); + + OAuthBearerRequestAuthenticator createJwtAuthenticator(); + + OAuthTokenRevocator createOAuhtTokenRevocator(); + + // FIXME: this shouldn't be here, but not sure how much of the IdSite code is just OAuth functionality. + IdSiteAuthenticator createIdSiteAuthenticator(); + +} diff --git a/api/src/main/java/com/stormpath/sdk/application/okta/AuthNRequest.java b/api/src/main/java/com/stormpath/sdk/application/okta/AuthNRequest.java new file mode 100644 index 0000000000..48868fae77 --- /dev/null +++ b/api/src/main/java/com/stormpath/sdk/application/okta/AuthNRequest.java @@ -0,0 +1,21 @@ +package com.stormpath.sdk.application.okta; + +import com.stormpath.sdk.resource.Resource; + +import java.util.Map; + +/** + * Representation of /api/v1/authn + */ +public interface AuthNRequest extends Resource { + + String getUsername(); + AuthNRequest setUsername(String username); + + String getPassword(); + AuthNRequest setPassword(String password); + + Map getOptions(); + AuthNRequest setOptions(Map options); + +} diff --git a/api/src/main/java/com/stormpath/sdk/application/okta/AuthNResult.java b/api/src/main/java/com/stormpath/sdk/application/okta/AuthNResult.java new file mode 100644 index 0000000000..9d77c08ef0 --- /dev/null +++ b/api/src/main/java/com/stormpath/sdk/application/okta/AuthNResult.java @@ -0,0 +1,16 @@ +package com.stormpath.sdk.application.okta; + +import com.stormpath.sdk.resource.Resource; + +import java.util.Map; + +/** + * Representation of /api/v1/authn + */ +public interface AuthNResult extends Resource { + + String getSessionToken(); + + String getUserId(); + +} diff --git a/api/src/main/java/com/stormpath/sdk/application/okta/OIDCKey.java b/api/src/main/java/com/stormpath/sdk/application/okta/OIDCKey.java new file mode 100644 index 0000000000..2a70d41fd9 --- /dev/null +++ b/api/src/main/java/com/stormpath/sdk/application/okta/OIDCKey.java @@ -0,0 +1,17 @@ +package com.stormpath.sdk.application.okta; + +import com.stormpath.sdk.resource.Resource; + +/** + * + */ +public interface OIDCKey extends Resource { + + String getAlgorithm(); + String getId(); + String getType(); + String getUse(); + + String get(String id); + +} diff --git a/api/src/main/java/com/stormpath/sdk/application/okta/OIDCKeysList.java b/api/src/main/java/com/stormpath/sdk/application/okta/OIDCKeysList.java new file mode 100644 index 0000000000..9cc5ef4ae2 --- /dev/null +++ b/api/src/main/java/com/stormpath/sdk/application/okta/OIDCKeysList.java @@ -0,0 +1,16 @@ +package com.stormpath.sdk.application.okta; + +import com.stormpath.sdk.resource.Resource; + +import java.security.Key; +import java.util.Set; + +/** + * + */ +public interface OIDCKeysList extends Resource { + + Set getKeys(); + OIDCKey getKeyById(String keyId); + +} diff --git a/api/src/main/java/com/stormpath/sdk/application/okta/OktaForgotPasswordRequest.java b/api/src/main/java/com/stormpath/sdk/application/okta/OktaForgotPasswordRequest.java new file mode 100644 index 0000000000..f66c3f6610 --- /dev/null +++ b/api/src/main/java/com/stormpath/sdk/application/okta/OktaForgotPasswordRequest.java @@ -0,0 +1,18 @@ +package com.stormpath.sdk.application.okta; + +import com.stormpath.sdk.resource.Resource; + +/** + * + */ +public interface OktaForgotPasswordRequest extends Resource { + + String getUsername(); + OktaForgotPasswordRequest setUsername(String username); + + String getFactorType(); + OktaForgotPasswordRequest setFactorType(String factorType); + + String getRelayState(); + OktaForgotPasswordRequest setRelayState(String relayState); +} diff --git a/api/src/main/java/com/stormpath/sdk/application/okta/OktaForgotPasswordResult.java b/api/src/main/java/com/stormpath/sdk/application/okta/OktaForgotPasswordResult.java new file mode 100644 index 0000000000..0712f4c46f --- /dev/null +++ b/api/src/main/java/com/stormpath/sdk/application/okta/OktaForgotPasswordResult.java @@ -0,0 +1,16 @@ +package com.stormpath.sdk.application.okta; + +import com.stormpath.sdk.resource.Resource; + +/** + * + */ +public interface OktaForgotPasswordResult extends Resource { + + String getStatus(); + String getFactorResult(); + String getRelayState(); + String getFactorType(); + String getRecoveryType(); + +} diff --git a/api/src/main/java/com/stormpath/sdk/application/okta/OktaTokenRequest.java b/api/src/main/java/com/stormpath/sdk/application/okta/OktaTokenRequest.java new file mode 100644 index 0000000000..d0b38447c0 --- /dev/null +++ b/api/src/main/java/com/stormpath/sdk/application/okta/OktaTokenRequest.java @@ -0,0 +1,25 @@ +package com.stormpath.sdk.application.okta; + +import com.stormpath.sdk.resource.Resource; + +/** + * + */ +public interface OktaTokenRequest extends Resource { + + String getGrantType(); + OktaTokenRequest setGrantType(String grantType); + + String getRedirectUri(); + OktaTokenRequest setRedirectUri(String redirectUri); + + String getUsername(); + OktaTokenRequest setUsername(String username); + + String getPassword(); + OktaTokenRequest setPassword(String password); + + String getScope(); + OktaTokenRequest setScope(String scope); + +} diff --git a/api/src/main/java/com/stormpath/sdk/application/okta/OktaTokenResponse.java b/api/src/main/java/com/stormpath/sdk/application/okta/OktaTokenResponse.java new file mode 100644 index 0000000000..b53f654fa8 --- /dev/null +++ b/api/src/main/java/com/stormpath/sdk/application/okta/OktaTokenResponse.java @@ -0,0 +1,21 @@ +package com.stormpath.sdk.application.okta; + +import com.stormpath.sdk.oauth.TokenResponse; +import com.stormpath.sdk.resource.Resource; + +/** + * + */ +public interface OktaTokenResponse extends Resource, TokenResponse { + + String getAccessToken(); + + String getTokenType(); + + String getExpiresIn(); + + String getScope(); + + String getRefreshToken(); + +} diff --git a/api/src/main/java/com/stormpath/sdk/application/okta/TokenIntrospectRequest.java b/api/src/main/java/com/stormpath/sdk/application/okta/TokenIntrospectRequest.java new file mode 100644 index 0000000000..d1c7b95bb0 --- /dev/null +++ b/api/src/main/java/com/stormpath/sdk/application/okta/TokenIntrospectRequest.java @@ -0,0 +1,16 @@ +package com.stormpath.sdk.application.okta; + +import com.stormpath.sdk.resource.Resource; + +/** + * + */ +public interface TokenIntrospectRequest extends Resource { + + String getToken(); + TokenIntrospectRequest setToken(String token); + + String getTokenTypeHint(); + TokenIntrospectRequest setTokenTypeHint(String tokenTypeHint); + +} diff --git a/api/src/main/java/com/stormpath/sdk/application/okta/TokenIntrospectResponse.java b/api/src/main/java/com/stormpath/sdk/application/okta/TokenIntrospectResponse.java new file mode 100644 index 0000000000..a1b8d97013 --- /dev/null +++ b/api/src/main/java/com/stormpath/sdk/application/okta/TokenIntrospectResponse.java @@ -0,0 +1,24 @@ +package com.stormpath.sdk.application.okta; + +import com.stormpath.sdk.resource.Resource; + +import java.util.Date; + +/** + * + */ +public interface TokenIntrospectResponse extends Resource { + + boolean isActive(); + String getScope(); + String getUsername(); + Date getExpiresAt(); + Date getIssuedAt(); + String getSubject(); + String getAudience(); + String getIssuer(); + String getJwtId(); + String getTokenType(); + String getClientId(); + String getUid(); +} diff --git a/api/src/main/java/com/stormpath/sdk/authc/OktaAuthNAuthenticator.java b/api/src/main/java/com/stormpath/sdk/authc/OktaAuthNAuthenticator.java new file mode 100644 index 0000000000..703d3fcad6 --- /dev/null +++ b/api/src/main/java/com/stormpath/sdk/authc/OktaAuthNAuthenticator.java @@ -0,0 +1,13 @@ +package com.stormpath.sdk.authc; + +import com.stormpath.sdk.resource.Resource; + +/** + * + */ +public interface OktaAuthNAuthenticator extends Resource { + + AuthenticationResult authenticate(AuthenticationRequest request); + + void assertValidAccessToken(String accessToken); +} diff --git a/api/src/main/java/com/stormpath/sdk/client/ClientBuilder.java b/api/src/main/java/com/stormpath/sdk/client/ClientBuilder.java index 3ce04401e2..ed267f90bf 100644 --- a/api/src/main/java/com/stormpath/sdk/client/ClientBuilder.java +++ b/api/src/main/java/com/stormpath/sdk/client/ClientBuilder.java @@ -222,6 +222,7 @@ public interface ClientBuilder { String DEFAULT_CLIENT_PROXY_HOST_PROPERTY_NAME = "stormpath.client.proxy.host"; String DEFAULT_CLIENT_PROXY_USERNAME_PROPERTY_NAME = "stormpath.client.proxy.username"; String DEFAULT_CLIENT_PROXY_PASSWORD_PROPERTY_NAME = "stormpath.client.proxy.password"; + String DEFAULT_OKTA_ENABLED_PROPERTY_NAME = "okta.enabled"; /** * Allows specifying an {@code ApiKey} instance directly instead of relying on the diff --git a/api/src/main/java/com/stormpath/sdk/directory/OktaPasswordPolicy.java b/api/src/main/java/com/stormpath/sdk/directory/OktaPasswordPolicy.java new file mode 100644 index 0000000000..a71bfd806e --- /dev/null +++ b/api/src/main/java/com/stormpath/sdk/directory/OktaPasswordPolicy.java @@ -0,0 +1,23 @@ +package com.stormpath.sdk.directory; + +import com.stormpath.sdk.resource.Resource; + +import java.util.Date; +import java.util.Map; + +public interface OktaPasswordPolicy extends Resource { + + String getType(); + String getId(); + String getStatus(); + String getName(); + String getDescription(); + int getPriority(); + boolean getSystem(); + Map getConditions(); + Date getCreated(); + Date getLastUpdated(); + Map getSettings(); + Map getDelegation(); + Map getRules(); +} diff --git a/api/src/main/java/com/stormpath/sdk/directory/OktaPasswordPolicyList.java b/api/src/main/java/com/stormpath/sdk/directory/OktaPasswordPolicyList.java new file mode 100644 index 0000000000..616ad27c7d --- /dev/null +++ b/api/src/main/java/com/stormpath/sdk/directory/OktaPasswordPolicyList.java @@ -0,0 +1,6 @@ +package com.stormpath.sdk.directory; + +import com.stormpath.sdk.resource.CollectionResource; + +public interface OktaPasswordPolicyList extends CollectionResource { +} diff --git a/api/src/main/java/com/stormpath/sdk/oauth/OAuthPolicies.java b/api/src/main/java/com/stormpath/sdk/oauth/OAuthPolicies.java deleted file mode 100644 index 44ec638f5d..0000000000 --- a/api/src/main/java/com/stormpath/sdk/oauth/OAuthPolicies.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2013 Stormpath, 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.stormpath.sdk.oauth; - -import com.stormpath.sdk.lang.Classes; - -/** - * @since 1.6.0 - */ -public final class OAuthPolicies { - - public static OAuthPolicyOptions options() { - return (OAuthPolicyOptions) Classes.newInstance("com.stormpath.sdk.impl.oauth.DefaultOAuthPolicyOptions"); - } -} diff --git a/api/src/main/java/com/stormpath/sdk/oauth/OAuthPolicy.java b/api/src/main/java/com/stormpath/sdk/oauth/OAuthPolicy.java index a4dd2459ce..e28dfc89bc 100644 --- a/api/src/main/java/com/stormpath/sdk/oauth/OAuthPolicy.java +++ b/api/src/main/java/com/stormpath/sdk/oauth/OAuthPolicy.java @@ -16,15 +16,10 @@ package com.stormpath.sdk.oauth; import com.stormpath.sdk.application.Application; -import com.stormpath.sdk.oauth.openidconnect.Scope; -import com.stormpath.sdk.oauth.openidconnect.ScopeList; import com.stormpath.sdk.resource.Resource; -import com.stormpath.sdk.resource.ResourceException; import com.stormpath.sdk.resource.Saveable; import com.stormpath.sdk.tenant.Tenant; -import java.util.Map; - /** * An OAuthPolicy resource is used to configure different aspects of the OAuth tokens associated * with an {@link Application Application} @@ -47,14 +42,6 @@ public interface OAuthPolicy extends Resource, Saveable { */ String getRefreshTokenTtl(); - /** - * Returns the Time To Live for the id tokens created for the parent {@link Application Application} expressed in a period of time format, for example: PT1H. - * - * @return the String representation of the Time To Live for the id tokens created for the parent {@link Application Application} - * @since 1.6.0 - */ - String getIdTokenTtl(); - /** * The href corresponding to the Endpoint for Access Tokens created for the parent {@link Application Application} * @@ -84,80 +71,6 @@ public interface OAuthPolicy extends Resource, Saveable { */ OAuthPolicy setRefreshTokenTtl(String refreshTokenTtl); - /** - * Sets the Time To Live for the id tokens created for the parent {@link Application Application} expressed in a period of time format, for example: PT1H. - * - * @return this instance for method chaining. - * @since 1.6.0 - */ - OAuthPolicy setIdTokenTtl(String idTokenTtl); - - /** - * Creates a new {@link Scope} assigned to this oauthPolicy in the Stormpath server and returns the created resource. - * The scope is used for openid connect flows. - * - * @param scope {@link Scope} pojo to hold necessary data to send to the back-end to create a {@link Scope}. - * @return the newly created {@link Scope}. - * - * @since 1.6.0 - */ - Scope createScope(Scope scope) throws ResourceException; - - /** - * Returns a paginated list of all the scopes that belong to the oAuthPolicy. - * - * @return a paginated list of all the oAuthPolicy's scopes. - * - * @since 1.6.0 - */ - ScopeList getScopes(); - - /** - * Returns access token attribute mappings. - *

Open Id provider (OP) would enter any custom mappings used for their internal purposes in this map. - * Authorization server would then add all these mappings as part of the access token upon its generation.

- * - * @return access token attribute mappings. - * - * @since 1.6.0 - */ - Map getAccessTokenAttributeMap(); - - /** - * Sets access token attribute mappings to be inserted into access tokens. - * - * @param accessTokenAttributeMap access token attribute mappings to be inserted into access tokens - *

Open Id provider (OP) would enter any custom mappings used for their internal purposes in this map. - * Authorization server would then add all these mappings as part of the access token upon its generation.

- * @return this instance for method chaining. - * - * @since 1.6.0 - */ - OAuthPolicy setAccessTokenAttributeMap(Map accessTokenAttributeMap); - - /** - * Returns id token attribute mappings. - *

Open Id provider (OP) would enter any custom mappings used for their internal purposes in this map. - * Authorization server would then add all these mappings as part of the id token upon its generation.

- * - * @return id token attribute mappings. - * - * @since 1.6.0 - */ - Map getIdTokenAttributeMap(); - - /** - * Sets id token attribute mappings to be inserted into id tokens. - * - * @param idTokenAttributeMap id token attribute mappings to be inserted into id tokens - *

Open Id provider (OP) would enter any custom mappings used for their internal purposes in this map. - * Authorization server would then add all these mappings as part of the id token upon its generation.

- * @return this instance for method chaining. - * - * @since 1.6.0 - */ - OAuthPolicy setIdTokenAttributeMap(Map idTokenAttributeMap); - /** * Returns the {@link Application Application} associated to this {@link OAuthPolicy OAuthPolicy} * diff --git a/api/src/main/java/com/stormpath/sdk/oauth/OAuthPolicyOptions.java b/api/src/main/java/com/stormpath/sdk/oauth/OAuthPolicyOptions.java deleted file mode 100644 index 97b3c74fe9..0000000000 --- a/api/src/main/java/com/stormpath/sdk/oauth/OAuthPolicyOptions.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017 Stormpath, 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.stormpath.sdk.oauth; - -import com.stormpath.sdk.query.Options; - -/** - * @since 1.6.0 - */ -public interface OAuthPolicyOptions extends Options { - - /** - * Ensures that when retrieving an OAuthPolicy, the OAuthPolicy's assigned {@link OAuthPolicy#getScopes()} scopes} - * are also retrieved in the same request. This enhances performance by leveraging a single request - * to retrieve multiple related resources you know you will use. - * - * @return this instance for method chaining. - */ - T withScopes(); - - /** - * Ensures that when retrieving an OAuthPolicy, the OAuthPolicy's assigned {@link OAuthPolicy#getScopes()} scopes} - * are also retrieved in the same request. This enhances performance by leveraging a single request - * to retrieve multiple related resources you know you will use. - * - * @param limit defines the maximum quantity of items to be retrieved when expanding this Collection Resource. Min:1, Max: 100. The default value is 25. - * @return this instance for method chaining. - */ - T withScopes(int limit); - - /** - * Ensures that when retrieving an OAuthPolicy, the OAuthPolicy's assigned {@link OAuthPolicy#getScopes()} scopes} - * are also retrieved in the same request. This enhances performance by leveraging a single request - * to retrieve multiple related resources you know you will use. - * - * @param limit defines the maximum quantity of items to be retrieved when expanding this Collection Resource. Min:1, Max: 100. The default value is 25. - * @param offset the zero-based starting index in the entire collection of the first item to return. Default is 0 - * @return this instance for method chaining. - */ - T withScopes(int limit, int offset); -} diff --git a/api/src/main/java/com/stormpath/sdk/oauth/openidconnect/Scope.java b/api/src/main/java/com/stormpath/sdk/oauth/openidconnect/Scope.java deleted file mode 100644 index 745bea5151..0000000000 --- a/api/src/main/java/com/stormpath/sdk/oauth/openidconnect/Scope.java +++ /dev/null @@ -1,108 +0,0 @@ -/* -* Copyright 2017 Stormpath, 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.stormpath.sdk.oauth.openidconnect; - -import com.stormpath.sdk.oauth.OAuthPolicy; -import com.stormpath.sdk.resource.Auditable; -import com.stormpath.sdk.resource.Deletable; -import com.stormpath.sdk.resource.Resource; -import com.stormpath.sdk.resource.Saveable; - -import java.util.Map; - -/** - * A Scope resource is used to configure different openid connect scopes - * with an {@link OAuthPolicy OAuthPolicy} - * - * @since 1.6.0 - */ -public interface Scope extends Resource, Saveable, Deletable, Auditable { - /** - * Returns the scope's name. - * - * @return the scope's name. - */ - String getName(); - - /** - * Sets the scope's name. - * @param name the scope's name. - * - * @return this instance for method chaining. - */ - Scope setName(String name); - - /** - * Returns the scope's friendly name. - * - * @return the scope's friendly name. - */ - String getFriendlyName(); - - /** - * Sets the scope's friendly name. - * - * @param name the scope's friendly name. - * @return this instance for method chaining. - */ - Scope setFriendlyName(String name); - - /** - * Returns the scope's description. - * - * @return the scope's description. - */ - String getDescription(); - - /** - * Sets the scope's description. - * @param description the scope's description. - * - * @return this instance for method chaining. - */ - Scope setDescription(String description); - - - /** - * Returns the scope's attribute mappings. - * - * @return the scope's attribute mappings. - */ - Map getAttributeMappings(); - - /** - * Sets the scope's attribute mappings. - * @param attributeMappings the scope's attribute mappings. - * - * @return this instance for method chaining. - */ - Scope setAttributeMappings(Map attributeMappings); - - /** - * Returns the {@link OAuthPolicy} associated with this scope. - * - * @return the {@link OAuthPolicy} associated with this scope. - */ - OAuthPolicy getOAuthPolicy(); - - /** - * Sets the {@link OAuthPolicy} associated with this scope. - * @param oAuthPolicy the {@link OAuthPolicy} associated with this scope. - * - * @return this instance for method chaining. - */ - Scope setOAuthPolicy(OAuthPolicy oAuthPolicy); -} diff --git a/api/src/main/java/com/stormpath/sdk/oauth/openidconnect/ScopeList.java b/api/src/main/java/com/stormpath/sdk/oauth/openidconnect/ScopeList.java deleted file mode 100644 index 7ae2825848..0000000000 --- a/api/src/main/java/com/stormpath/sdk/oauth/openidconnect/ScopeList.java +++ /dev/null @@ -1,26 +0,0 @@ -/* -* Copyright 2017 Stormpath, 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.stormpath.sdk.oauth.openidconnect; - -import com.stormpath.sdk.resource.CollectionResource; - -/** - * A {@link CollectionResource} containing {@link Scope} instances. - * - * @since 1.6.0 - */ -public interface ScopeList extends CollectionResource { -} diff --git a/bom/pom.xml b/bom/pom.xml index 050711ab63..e32b13e0b0 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -7,7 +7,7 @@ Stormpath Java SDK :: BOM Stormpath Bill of Materials pom - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT https://github.com/stormpath/stormpath-sdk-java @@ -50,92 +50,92 @@ com.stormpath.sdk stormpath-sdk-api - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT com.stormpath.sdk stormpath-sdk-impl - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT com.stormpath.sdk stormpath-sdk-oauth - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT com.stormpath.sdk stormpath-sdk-servlet - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT com.stormpath.sdk stormpath-sdk-httpclient - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT com.stormpath.sdk stormpath-servlet-plugin - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT com.stormpath.sdk stormpath-sdk-zuul - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT com.stormpath.spring stormpath-spring - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT com.stormpath.spring stormpath-spring-security - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT com.stormpath.spring stormpath-spring-security-webmvc - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT com.stormpath.spring stormpath-spring-webmvc - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT com.stormpath.spring stormpath-spring-boot-starter - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT com.stormpath.spring stormpath-webmvc-spring-boot-starter - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT com.stormpath.spring stormpath-thymeleaf-spring-boot-starter - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT com.stormpath.spring stormpath-spring-security-spring-boot-starter - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT com.stormpath.spring stormpath-spring-security-webmvc-spring-boot-starter - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT com.stormpath.spring stormpath-default-spring-boot-starter - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT com.stormpath.spring stormpath-zuul-spring-cloud-starter - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT diff --git a/changelog.md b/changelog.md index e822236775..2351a4f93d 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,11 @@ ## Change Log ## +### 1.5.5 ## + +* Refactored the /oauth/revoke endpoint to remove cookies + +See [1.5.5 closed issues](https://github.com/stormpath/stormpath-sdk-java/issues?q=milestone%3A1.5.5+is%3Aclosed) + ### 1.5.4 ## * Fixed a bug whereby Account with MCF password is not properly created via Directory when the RegistrationWorkflow is specified diff --git a/clover/pom.xml b/clover/pom.xml index 88ebc40279..4b5fa87ad3 100644 --- a/clover/pom.xml +++ b/clover/pom.xml @@ -21,7 +21,7 @@ com.stormpath.sdk stormpath-sdk-root - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml diff --git a/examples/pom.xml b/examples/pom.xml index eb1e0f97c2..8d3356e080 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-root - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml stormpath-sdk-examples com.stormpath.sdk - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Examples pom diff --git a/examples/quickstart/pom.xml b/examples/quickstart/pom.xml index bd027de258..42b32b56e4 100644 --- a/examples/quickstart/pom.xml +++ b/examples/quickstart/pom.xml @@ -5,13 +5,13 @@ com.stormpath.sdk stormpath-sdk-examples - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.sdk stormpath-sdk-examples-quickstart - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Examples :: Quickstart Code A simple (non-web) application to demonstrate the example code in the Stormpath Java Quickstart diff --git a/examples/servlet/pom.xml b/examples/servlet/pom.xml index 8a0e3b1c3d..8aa80f9fcd 100644 --- a/examples/servlet/pom.xml +++ b/examples/servlet/pom.xml @@ -21,12 +21,12 @@ com.stormpath.sdk stormpath-sdk-examples - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml stormpath-sdk-examples-servlet - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Examples :: Servlet Container-based Webapp A simple example webapp deployed in a servlet container. war diff --git a/examples/servlet/src/main/webapp/WEB-INF/stormpath.properties b/examples/servlet/src/main/webapp/WEB-INF/stormpath.properties index b0eb5c3320..0ebb1fec3a 100644 --- a/examples/servlet/src/main/webapp/WEB-INF/stormpath.properties +++ b/examples/servlet/src/main/webapp/WEB-INF/stormpath.properties @@ -187,4 +187,17 @@ stormpath.web.login.preHandler=com.stormpath.sdk.examples.servlet.PreLoginHandle stormpath.web.login.postHandler=com.stormpath.sdk.examples.servlet.PostLoginHandler stormpath.web.register.preHandler=com.stormpath.sdk.examples.servlet.PreRegisterHandler -stormpath.web.register.postHandler=com.stormpath.sdk.examples.servlet.PostRegisterHandler \ No newline at end of file +stormpath.web.register.postHandler=com.stormpath.sdk.examples.servlet.PostRegisterHandler + +####################################################### +# Okta required fields for servlet based applications # +####################################################### + +stormpath.client.authenticationScheme=SSWS +okta.enabled=true +stormpath.web.account.jwt.resolver = com.stormpath.sdk.servlet.filter.account.config.OktaJwtAccountResolverFactory +stormpath.web.account.jwt.signingKey.resolver = com.stormpath.sdk.servlet.application.okta.OktaJwtSigningKeyResolver + +# okta.api.token= +# okta.application.id= +# stormpath.client.baseUrl= diff --git a/examples/spring-boot-default/pom.xml b/examples/spring-boot-default/pom.xml index 55cdc7a485..c857593ac9 100644 --- a/examples/spring-boot-default/pom.xml +++ b/examples/spring-boot-default/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-examples - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-examples-spring-boot-default - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Examples :: Spring Boot Default A simple Spring Boot Web MVC application with out-of-the-box login and self-service screens! diff --git a/examples/spring-boot-default/src/main/java/com/stormpath/spring/boot/examples/SpringSecurityWebAppConfig.java b/examples/spring-boot-default/src/main/java/com/stormpath/spring/boot/examples/SpringSecurityWebAppConfig.java index b8cbec0363..ecdcc3038e 100644 --- a/examples/spring-boot-default/src/main/java/com/stormpath/spring/boot/examples/SpringSecurityWebAppConfig.java +++ b/examples/spring-boot-default/src/main/java/com/stormpath/spring/boot/examples/SpringSecurityWebAppConfig.java @@ -15,6 +15,12 @@ */ package com.stormpath.spring.boot.examples; +import com.stormpath.sdk.authc.AuthenticationResult; +import com.stormpath.sdk.client.Client; +import com.stormpath.sdk.servlet.http.Saver; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @@ -25,6 +31,19 @@ @Configuration public class SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter { + @Value("#{ @environment['stormpath.web.login.nextUri'] ?: '/' }") + protected String loginNextUri; + + @Value("#{ @environment['stormpath.web.produces'] ?: 'application/json, text/html' }") + protected String produces; + + @Autowired + @Qualifier("stormpathAuthenticationResultSaver") + protected Saver authenticationResultSaver; //provided by stormpath-spring-webmvc + + @Autowired + protected Client client; + /** * {@inheritDoc} */ diff --git a/examples/spring-boot-default/src/main/resources/logback.xml b/examples/spring-boot-default/src/main/resources/logback.xml index a168fbc828..b591f1345b 100644 --- a/examples/spring-boot-default/src/main/resources/logback.xml +++ b/examples/spring-boot-default/src/main/resources/logback.xml @@ -18,4 +18,8 @@ + + + + \ No newline at end of file diff --git a/examples/spring-boot-webmvc-angular/pom.xml b/examples/spring-boot-webmvc-angular/pom.xml index e0bdf4c9c5..1197eeefd0 100644 --- a/examples/spring-boot-webmvc-angular/pom.xml +++ b/examples/spring-boot-webmvc-angular/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-examples - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-examples-spring-boot-web-angular - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Examples :: Spring Boot + Angular Webapp A Spring Boot web application that uses Stormpath. diff --git a/examples/spring-boot-webmvc/pom.xml b/examples/spring-boot-webmvc/pom.xml index d67d022e5d..1376797b84 100644 --- a/examples/spring-boot-webmvc/pom.xml +++ b/examples/spring-boot-webmvc/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-examples - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-examples-spring-boot-web - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Examples :: Spring Boot Webapp A Spring Boot web application that uses Stormpath. diff --git a/examples/spring-boot/pom.xml b/examples/spring-boot/pom.xml index 8c6d9b6ad1..cf341a56a4 100644 --- a/examples/spring-boot/pom.xml +++ b/examples/spring-boot/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-examples - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-examples-spring-boot - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Examples :: Spring Boot A Spring Boot (non-web) application that uses Stormpath. diff --git a/examples/spring-security-spring-boot-webmvc-bare-bones/pom.xml b/examples/spring-security-spring-boot-webmvc-bare-bones/pom.xml index 17185eb6c3..a4a398e481 100644 --- a/examples/spring-security-spring-boot-webmvc-bare-bones/pom.xml +++ b/examples/spring-security-spring-boot-webmvc-bare-bones/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-examples - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-examples-spring-security-spring-boot-webmvc-bare-bones - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Examples :: Spring Security :: Spring Boot :: Bare Bones A simple Spring Boot Web MVC application with out-of-the-box login and self-service screens! diff --git a/examples/spring-security-spring-boot-webmvc/pom.xml b/examples/spring-security-spring-boot-webmvc/pom.xml index e7d142cc96..55fca0277f 100644 --- a/examples/spring-security-spring-boot-webmvc/pom.xml +++ b/examples/spring-security-spring-boot-webmvc/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-examples - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-examples-spring-security-spring-boot-webmvc - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Examples :: Spring Security :: Spring Boot Webapp A simple Spring Boot Web MVC application with out-of-the-box login and self-service screens! diff --git a/examples/spring-security-spring-boot-webmvc/src/main/resources/templates/hello.html b/examples/spring-security-spring-boot-webmvc/src/main/resources/templates/hello.html index 40f1560ae3..38f126ed9d 100644 --- a/examples/spring-security-spring-boot-webmvc/src/main/resources/templates/hello.html +++ b/examples/spring-security-spring-boot-webmvc/src/main/resources/templates/hello.html @@ -33,8 +33,9 @@

Login
-

-

+ + +
Restricted diff --git a/examples/spring-security-webmvc/pom.xml b/examples/spring-security-webmvc/pom.xml index 496e79321d..6abf6c3974 100644 --- a/examples/spring-security-webmvc/pom.xml +++ b/examples/spring-security-webmvc/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-examples - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-examples-spring-security-webmvc - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Examples :: Spring Security Web MVC A simple Spring Security Web MVC application with out-of-the-box login and self-service screens! diff --git a/examples/spring-webmvc/pom.xml b/examples/spring-webmvc/pom.xml index 7228f65333..f2fa5b2f2c 100644 --- a/examples/spring-webmvc/pom.xml +++ b/examples/spring-webmvc/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-examples - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-examples-spring-webmvc - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Examples :: Spring Web MVC A Spring Boot web application that uses Stormpath and Spring Security diff --git a/examples/spring/pom.xml b/examples/spring/pom.xml index bafdac412e..61853cc158 100644 --- a/examples/spring/pom.xml +++ b/examples/spring/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-examples - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-examples-spring - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Examples :: Spring A simple (non-web) Spring Application. diff --git a/examples/zuul-spring-cloud-starter/pom.xml b/examples/zuul-spring-cloud-starter/pom.xml index a216036db6..6ff512af75 100644 --- a/examples/zuul-spring-cloud-starter/pom.xml +++ b/examples/zuul-spring-cloud-starter/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-examples - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-examples-zuul-spring-cloud-starter - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Examples :: Zuul Spring Cloud Starter An example spring cloud zuul web gateway using Stormpath diff --git a/extensions/hazelcast/pom.xml b/extensions/hazelcast/pom.xml index 6fa43881a2..4adee3ed4e 100644 --- a/extensions/hazelcast/pom.xml +++ b/extensions/hazelcast/pom.xml @@ -21,7 +21,7 @@ com.stormpath.sdk stormpath-sdk-root - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../../pom.xml diff --git a/extensions/httpclient/pom.xml b/extensions/httpclient/pom.xml index d144b8323c..a747b05f31 100644 --- a/extensions/httpclient/pom.xml +++ b/extensions/httpclient/pom.xml @@ -21,7 +21,7 @@ com.stormpath.sdk stormpath-sdk-root - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../../pom.xml diff --git a/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/OAuthPolicyIT.groovy b/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/OAuthPolicyIT.groovy deleted file mode 100644 index 6ab380c443..0000000000 --- a/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/application/OAuthPolicyIT.groovy +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright 2017 Stormpath, 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.stormpath.sdk.impl.application - -import com.stormpath.sdk.client.ClientIT -import com.stormpath.sdk.impl.resource.AbstractResource -import com.stormpath.sdk.oauth.OAuthPolicies -import com.stormpath.sdk.oauth.OAuthPolicy -import com.stormpath.sdk.oauth.OAuthPolicyOptions -import com.stormpath.sdk.oauth.openidconnect.Scope -import com.stormpath.sdk.oauth.openidconnect.ScopeList -import org.testng.annotations.Test - -import java.lang.reflect.Field - -import static org.testng.Assert.assertEquals -import static org.testng.Assert.assertNotNull -import static org.testng.Assert.assertFalse -import static org.testng.Assert.assertTrue - -/** - * @since 1.6.0 - */ -class OAuthPolicyIT extends ClientIT { - - @Test - void testPolicyCreatedForNewApplication() { - def app = createTempApp() - - OAuthPolicy oauthPolicy = app.getOAuthPolicy() - assertNotNull oauthPolicy - assertNotNull oauthPolicy.href - assertEquals oauthPolicy.getApplication().getHref(), app.href - } - - @Test - void testNewlyCreatedPolicyGotDefaultScopes(){ - def app = createTempApp() - - OAuthPolicy oauthPolicy = app.getOAuthPolicy() - assertNotNull oauthPolicy - assertNotNull oauthPolicy.href - assertEquals oauthPolicy.getApplication().getHref(), app.href - - def oAuthPolicyScopesEndPoint = oauthPolicy.href + "/scopes" - def scopes = client.getResource(oAuthPolicyScopesEndPoint, ScopeList.class) - assertEquals scopes.asList().size(), 3 - - assertNotNull scopes.asList().get(0).href - assertNotNull scopes.asList().get(1).href - assertNotNull scopes.asList().get(2).href - - for(def currentDefaultScope : scopes.asList()){ - assertNotNull(currentDefaultScope.name) - assertNotNull(currentDefaultScope.friendlyName) - assertNotNull(currentDefaultScope.description) - assertNotNull(currentDefaultScope.attributeMappings) - assertFalse(currentDefaultScope.attributeMappings.isEmpty()) - } - } - - @Test - void testPolicyExpandOnScopes(){ - def app = createTempApp() - def policyHref = app.getOAuthPolicy().href - - OAuthPolicyOptions options = OAuthPolicies.options().withScopes() - - assertNotNull options - assertEquals options.expansions.size(), 1 - - options = OAuthPolicies.options().withScopes(10) - - assertNotNull options - assertEquals options.expansions.size(), 1 - - options = OAuthPolicies.options().withScopes(10,0) - - assertNotNull options - assertEquals options.expansions.size(), 1 - - def retrieved = client.getResource(policyHref, OAuthPolicy.class, options) - Map policyProperties = getValue(AbstractResource, retrieved, "properties") - def scopes = policyProperties.get("scopes").size() - assertTrue scopes == 5 - assertTrue policyProperties.get("scopes").items.get(0).name != null - assertTrue policyProperties.get("scopes").items.get(1).name != null - assertTrue policyProperties.get("scopes").items.get(2).name != null - } - - @Test - void testDeleteOAuthPolicyDeletesScopes(){ - // todo: openid connect uncomment the snippet below once cascading deletes are supported in SDK - // Cascading deletes are not supported in SDK for now - // Following issue will address it: https://github.com/stormpath/stormpath-sdk-java/issues/985 - //getDeletedResourceError(identityProvider.href, SamlIdentityProvider) - } - - // todo - currently failing against production @mrafiei - @Test(enabled = false) - void testRetrieveAndUpdateOAuthPolicy() { - def app = createTempApp() - - OAuthPolicy oauthPolicy = app.getOAuthPolicy() - assertNotNull oauthPolicy - assertEquals oauthPolicy.getApplication().getHref(), app.href - assertNotNull oauthPolicy.getTokenEndpoint() - assertNotNull oauthPolicy.getRevocationEndpoint() - assertNotNull oauthPolicy.getScopes() - assertNotNull oauthPolicy.getAccessTokenAttributeMap() - assertNotNull oauthPolicy.getIdTokenAttributeMap() - - ScopeList scopeList = client.getResource(oauthPolicy.getScopes().getHref(), ScopeList.class) - assertEquals scopeList.asList().size(), 3 - - Scope scope = client.instantiate(Scope.class) - scope.setName("testScope").setFriendlyName("friendly testing").setDescription("some description").setAttributeMappings(["name":"mapped_name"]) - scope = oauthPolicy.createScope(scope) - assertEquals scope.getOAuthPolicy().href, oauthPolicy.href - - scopeList = client.getResource(oauthPolicy.getScopes().getHref(), ScopeList.class) - assertEquals scopeList.asList().size(), 4 - - oauthPolicy.setAccessTokenTtl("P8D") - oauthPolicy.setRefreshTokenTtl("P2D") - oauthPolicy.setIdTokenTtl("P5D") - oauthPolicy.setAccessTokenAttributeMap(["atk1":"atv1"]) - oauthPolicy.setIdTokenAttributeMap(["itk1":"itv1", "itk2":"itv2"]) - oauthPolicy.save() - - oauthPolicy = app.getOAuthPolicy() - assertEquals oauthPolicy.getAccessTokenTtl(), "P8D" - assertEquals oauthPolicy.getRefreshTokenTtl(), "P2D" - assertEquals oauthPolicy.getIdTokenTtl(), "P5D" - assertEquals oauthPolicy.getAccessTokenAttributeMap(), ["atk1":"atv1"] - assertEquals oauthPolicy.getIdTokenAttributeMap(), ["itk1":"itv1", "itk2":"itv2"] - assertEquals oauthPolicy.getApplication().getHref(), app.href - - oauthPolicy.setAccessTokenAttributeMap(["atk1":"atv1", "atk2":"atv2"]) - oauthPolicy.setIdTokenAttributeMap(["itk1":"itv1"]) - oauthPolicy.save() - - oauthPolicy = app.getOAuthPolicy() - assertEquals oauthPolicy.getAccessTokenAttributeMap(), ["atk1":"atv1", "atk2":"atv2"] - assertEquals oauthPolicy.getIdTokenAttributeMap(), ["itk1":"itv1"] - } - - @Test - void testUpdateScopesUpdatesPolicyCollection(){ - def app = createTempApp() - - OAuthPolicy oauthPolicy = app.getOAuthPolicy() - assertNotNull oauthPolicy - assertEquals oauthPolicy.getApplication().getHref(), app.href - assertNotNull oauthPolicy.getScopes() - - def oAuthPolicyScopesEndPoint = oauthPolicy.href + "/scopes" - def scopes = client.getResource(oAuthPolicyScopesEndPoint, ScopeList.class) - - def items = scopes.asList() - - assertEquals(items.size, 3) - assertNotNull(items[0].href) - assertNotNull(items[1].href) - assertNotNull(items[2].href) - - items[0].setName("updatedName1") - items[1].setName("updatedName2") - items[2].setName("updatedName3") - - items[0].save() - items[1].save() - items[2].save() - - scopes = client.getResource(oAuthPolicyScopesEndPoint, ScopeList.class) - - items = scopes.asList() - - assertEquals(items.size, 3) - assertNotNull(items[0].href) - assertNotNull(items[1].href) - assertNotNull(items[2].href) - assertEquals(items[0].name, "updatedName1") - assertEquals(items[1].name, "updatedName2") - assertEquals(items[2].name, "updatedName3") - } - - private Object getValue(Class clazz, Object object, String fieldName) { - Field field = clazz.getDeclaredField(fieldName) - field.setAccessible(true) - return field.get(object) - } -} diff --git a/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/client/DefaultClientTest.groovy b/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/client/DefaultClientTest.groovy index df819ffeb6..05f4d23858 100644 --- a/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/client/DefaultClientTest.groovy +++ b/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/client/DefaultClientTest.groovy @@ -75,8 +75,9 @@ class DefaultClientTest { expect(proxy.getHost()).andReturn("192.168.2.110") expect(proxy.getPort()).andReturn(777) expect(proxy.isAuthenticationRequired()).andReturn(false) + expect(baseUrlResolver.getBaseUrl()).andReturn("https://api.example.com/") - replay(apiKeyCredentials, proxy, cacheManager) + replay(apiKeyCredentials, proxy, cacheManager, baseUrlResolver) Client client = new DefaultClient(apiKeyCredentials, apiKeyResolver, baseUrlResolver, proxy, cacheManager, authcScheme, null, connectionTimeout) @@ -84,7 +85,7 @@ class DefaultClientTest { assertEquals(client.dataStore.requestExecutor.httpClient.defaultConfig.socketTimeout, connectionTimeout * 1000) assertEquals(client.dataStore.requestExecutor.httpClient.defaultConfig.connectTimeout, connectionTimeout * 1000) - verify(apiKeyCredentials, proxy, cacheManager) + verify(apiKeyCredentials, proxy, cacheManager, baseUrlResolver) } @Test @@ -130,7 +131,10 @@ class DefaultClientTest { def apiKeyResolver = createStrictMock(ApiKeyResolver) def baseUrlResolver = createStrictMock(BaseUrlResolver) def authcScheme = AuthenticationScheme.SAUTHC1 - def cacheManager = Caches.newDisabledCacheManager(); + def cacheManager = Caches.newDisabledCacheManager() + + expect(baseUrlResolver.getBaseUrl()).andReturn("https://api.example.com/") + replay baseUrlResolver Client client = new DefaultClient(apiKeyCredentials, apiKeyResolver, baseUrlResolver, null, cacheManager , authcScheme, null, 20000) @@ -199,7 +203,9 @@ class DefaultClientTest { expect(response.getBody()).andReturn(is01AfterSave) expect(response.getHttpStatus()).andReturn(200) - replay requestExecutor, response, resourceFactory + expect(baseUrlResolver.getBaseUrl()).andReturn("https://api.example.com/") + + replay requestExecutor, response, resourceFactory, baseUrlResolver //Let's start def client = new DefaultClient(apiKeyCredentials, apiKeyResolver, baseUrlResolver, null, Caches.newDisabledCacheManager(), null, null, 20000) @@ -229,7 +235,7 @@ class DefaultClientTest { assertEquals(account02.dirtyProperties, Collections.emptyMap()) assertSame(propertiesField.get(account01), propertiesField.get(account02)) - verify requestExecutor, response, resourceFactory + verify requestExecutor, response, resourceFactory, baseUrlResolver } //@since 1.0.RC3 @@ -319,8 +325,10 @@ class DefaultClientTest { expect(tenantResolver.getCurrentTenant()).andReturn(tenant).times(11) + expect(baseUrlResolver.getBaseUrl()).andReturn("https://api.example.com/") + replay(apiKeyCredentials, dataStore, tenant, application, returnedApplication, createApplicationRequest, applicationList, - map, applicationCriteria, directory, returnedDir, createDirectoryRequest, directoryList, directoryCriteria, account, tenantResolver) + map, applicationCriteria, directory, returnedDir, createDirectoryRequest, directoryList, directoryCriteria, account, tenantResolver, baseUrlResolver) Client client = new DefaultClient(apiKeyCredentials, apiKeyResolver, baseUrlResolver, null, Caches.newDisabledCacheManager(), null, null, 20000, tenantResolver) setNewValue(client, "dataStore", dataStore) @@ -337,7 +345,7 @@ class DefaultClientTest { assertSame(client.verifyAccountEmail(token), account) verify(apiKeyCredentials, dataStore, tenant, application, returnedApplication, createApplicationRequest, applicationList, - map, applicationCriteria, directory, returnedDir, createDirectoryRequest, directoryList, directoryCriteria, account, tenantResolver) + map, applicationCriteria, directory, returnedDir, createDirectoryRequest, directoryList, directoryCriteria, account, tenantResolver, baseUrlResolver) } /** @@ -364,14 +372,16 @@ class DefaultClientTest { expect(proxy.getPort()).andReturn(777) expect(proxy.isAuthenticationRequired()).andReturn(false) - replay(apiKeyCredentials, proxy, cacheManager, tenantResolver) + expect(baseUrlResolver.getBaseUrl()).andReturn("https://api.example.com/") + + replay(apiKeyCredentials, proxy, cacheManager, tenantResolver, baseUrlResolver) Client client = new DefaultClient(apiKeyCredentials, apiKeyResolver, baseUrlResolver, proxy, cacheManager, authcScheme, null, connectionTimeout, tenantResolver) assertEquals(client.getCurrentTenant(), tenant) assertEquals(client.getCurrentTenant(tenantOptions), tenant) - verify(apiKeyCredentials, proxy, cacheManager, tenantResolver) + verify(apiKeyCredentials, proxy, cacheManager, tenantResolver, baseUrlResolver) } /** diff --git a/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/openidconnect/ScopeIT.groovy b/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/openidconnect/ScopeIT.groovy deleted file mode 100644 index 6bf94fa352..0000000000 --- a/extensions/httpclient/src/test/groovy/com/stormpath/sdk/impl/openidconnect/ScopeIT.groovy +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2017 Stormpath, 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.stormpath.sdk.impl.openidconnect - -import com.stormpath.sdk.client.ClientIT -import com.stormpath.sdk.oauth.OAuthPolicy -import com.stormpath.sdk.oauth.openidconnect.Scope -import com.stormpath.sdk.oauth.openidconnect.ScopeList -import com.stormpath.sdk.resource.ResourceException -import org.testng.annotations.Test - -import static org.testng.Assert.assertEquals -import static org.testng.Assert.assertNotNull -import static org.testng.Assert.assertNull -import static org.testng.Assert.assertTrue - -/** - * @since 1.6.0 - */ -class ScopeIT extends ClientIT { - - @Test - void testGetPolicyScopes() { - - def app = createTempApp() - - OAuthPolicy oauthPolicy = app.getOAuthPolicy() - assertNotNull oauthPolicy - - ScopeList scopeList = client.getResource(oauthPolicy.getScopes().getHref(), ScopeList.class) - assertEquals app.getOAuthPolicy().scopes.href, scopeList.href - assertEquals scopeList.asList().size(), 3 - } - - @Test - void testCreateScope() { - def app = createTempApp() - assertNotNull app.getOAuthPolicy().href - assertNotNull app.getOAuthPolicy().scopes.href - - OAuthPolicy oAuthPolicy = app.getOAuthPolicy() - assertNotNull oAuthPolicy.href - - def oAuthPolicyScopesEndPoint = oAuthPolicy.href + "/scopes" - def scopeList = client.getResource(oAuthPolicyScopesEndPoint, ScopeList.class) - assertNotNull scopeList.href - - Scope scope = client.instantiate(Scope.class) - scope.setDescription("description") - - Throwable e = null - try{ - oAuthPolicy.createScope(scope) - } - catch(ResourceException re){ - e = re - assertEquals(re.status, 400) - assertEquals(re.getCode(), 2000) - } - assertTrue(e instanceof ResourceException) - - scope.setName("name").setAttributeMappings(["attribute1":"value1"]) - scope = oAuthPolicy.createScope(scope) - scope = client.getResource(scope.href, Scope.class) - - assertNotNull(scope.href) - assertNotNull(scope.name) - assertNull(scope.friendlyName) - assertNotNull(scope.description) - assertNotNull(scope.attributeMappings) - assertNotNull(scope.createdAt) - assertNotNull(scope.modifiedAt) - assertNotNull(scope.getOAuthPolicy()) - assertNotNull(scope.getOAuthPolicy().href) - } - - // todo - currently failing against production @mrafiei - @Test(enabled = false) - void testUpdateScope() { - def app = createTempApp() - assertNotNull app.getOAuthPolicy().href - assertNotNull app.getOAuthPolicy().scopes.href - - OAuthPolicy oAuthPolicy = app.getOAuthPolicy() - assertNotNull oAuthPolicy.href - - Scope scope = client.instantiate(Scope.class) - scope.setName("myScopeName").setAttributeMappings(["attribute1":"value1"]) - scope.setDescription("myScopeDescription") - - scope = oAuthPolicy.createScope(scope) - scope = client.getResource(scope.href, Scope.class) - - assertNotNull(scope.href) - assertEquals scope.name, "myScopeName" - assertNull(scope.friendlyName) - assertEquals scope.description, "myScopeDescription" - assertNotNull(scope.attributeMappings) - assertEquals scope.attributeMappings, ["attribute1":"value1"] - assertNotNull(scope.createdAt) - assertNotNull(scope.modifiedAt) - assertNotNull(scope.getOAuthPolicy()) - assertNotNull(scope.getOAuthPolicy().href) - - scope.setFriendlyName("myFriendlyName") - scope.setAttributeMappings(["attribute1":"value1", "attribute2":"value2"]) - scope.save() - scope = client.getResource(scope.href, Scope.class) - - assertNotNull(scope.href) - assertNotNull(scope.name) - assertEquals scope.friendlyName, "myFriendlyName" - assertNotNull(scope.description) - assertEquals scope.attributeMappings, ["attribute1":"value1", "attribute2":"value2"] - assertNotNull(scope.createdAt) - assertNotNull(scope.modifiedAt) - assertNotNull(scope.getOAuthPolicy()) - assertNotNull(scope.getOAuthPolicy().href) - - scope.setAttributeMappings(["attribute1":"value1"]) - scope.save() - scope = client.getResource(scope.href, Scope.class) - - assertNotNull(scope.href) - assertNotNull(scope.name) - assertEquals scope.friendlyName, "myFriendlyName" - assertNotNull(scope.description) - assertEquals scope.attributeMappings, ["attribute1":"value1"] - assertNotNull(scope.createdAt) - assertNotNull(scope.modifiedAt) - assertNotNull(scope.getOAuthPolicy()) - assertNotNull(scope.getOAuthPolicy().href) - } - - @Test - void testDeleteScope() { - def app = createTempApp() - assertNotNull app.getOAuthPolicy().href - assertNotNull app.getOAuthPolicy().scopes.href - - OAuthPolicy oAuthPolicy = app.getOAuthPolicy() - assertNotNull oAuthPolicy.href - - Scope scope = client.instantiate(Scope.class) - scope.setName("myScopeName").setAttributeMappings(["attribute1":"value1"]) - scope.setDescription("myScopeDescription") - - oAuthPolicy.createScope(scope) - - def oAuthPolicyScopesEndPoint = oAuthPolicy.href + "/scopes" - - def scopes = client.getResource(oAuthPolicyScopesEndPoint, ScopeList.class) - assertEquals scopes.size, 4 - - scope.delete() - - Throwable e = null - try{ - client.getResource(scope.href, Scope.class) - } - catch(ResourceException re){ - e = re - assertEquals(re.status, 404) - assertEquals(re.getCode(), 404) - } - assertTrue(e instanceof ResourceException) - - scopes = client.getResource(oAuthPolicyScopesEndPoint, ScopeList.class) - assertEquals scopes.size, 3 - } - -} diff --git a/extensions/oauth/pom.xml b/extensions/oauth/pom.xml index b6d99aa706..8ac12f2118 100644 --- a/extensions/oauth/pom.xml +++ b/extensions/oauth/pom.xml @@ -21,7 +21,7 @@ com.stormpath.sdk stormpath-sdk-root - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../../pom.xml diff --git a/extensions/oauth/src/test/groovy/com/stormpath/sdk/impl/oauth/authz/DefaultTokenResponseTest.groovy b/extensions/oauth/src/test/groovy/com/stormpath/sdk/impl/oauth/authz/DefaultOktaTokenResponseTest.groovy similarity index 100% rename from extensions/oauth/src/test/groovy/com/stormpath/sdk/impl/oauth/authz/DefaultTokenResponseTest.groovy rename to extensions/oauth/src/test/groovy/com/stormpath/sdk/impl/oauth/authz/DefaultOktaTokenResponseTest.groovy diff --git a/extensions/pom.xml b/extensions/pom.xml index 483b8b0151..86847c6c8f 100644 --- a/extensions/pom.xml +++ b/extensions/pom.xml @@ -19,7 +19,7 @@ com.stormpath.sdk stormpath-sdk-root - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml diff --git a/extensions/servlet-plugin/pom.xml b/extensions/servlet-plugin/pom.xml index d7fcad52bc..e8322f302c 100644 --- a/extensions/servlet-plugin/pom.xml +++ b/extensions/servlet-plugin/pom.xml @@ -21,7 +21,7 @@ com.stormpath.sdk stormpath-sdk-root - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../../pom.xml diff --git a/extensions/servlet/pom.xml b/extensions/servlet/pom.xml index b3eeebece9..99e63211d1 100644 --- a/extensions/servlet/pom.xml +++ b/extensions/servlet/pom.xml @@ -21,7 +21,7 @@ com.stormpath.sdk stormpath-sdk-root - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../../pom.xml diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/application/DefaultApplicationResolver.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/application/DefaultApplicationResolver.java index ea0f02905b..99ab8540d1 100644 --- a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/application/DefaultApplicationResolver.java +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/application/DefaultApplicationResolver.java @@ -18,7 +18,10 @@ import com.stormpath.sdk.application.Application; import com.stormpath.sdk.application.ApplicationList; import com.stormpath.sdk.client.Client; +import com.stormpath.sdk.impl.application.okta.OktaApplication; +import com.stormpath.sdk.impl.ds.InternalDataStore; import com.stormpath.sdk.lang.Assert; +import com.stormpath.sdk.lang.Strings; import com.stormpath.sdk.servlet.client.DefaultServletContextClientFactory; import com.stormpath.sdk.servlet.config.Config; @@ -82,12 +85,16 @@ public Application getApplication(final ServletContext servletContext) { //get the client: Client client = getClient(servletContext); + Config config = getConfig(servletContext); + if (Boolean.valueOf(config.get("okta.enabled"))) { + return new OktaApplication((InternalDataStore) client.getDataStore()); + } + //this is a local cached href value that we use in case we have to query applications (see below): String href = (String) servletContext.getAttribute(STORMPATH_APPLICATION_HREF); if (href == null) { //no cached value = try config: - Config config = getConfig(servletContext); href = config.get(STORMPATH_APPLICATION_HREF); } diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/application/okta/OktaJwtSigningKeyResolver.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/application/okta/OktaJwtSigningKeyResolver.java new file mode 100644 index 0000000000..ac8a0cf39b --- /dev/null +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/application/okta/OktaJwtSigningKeyResolver.java @@ -0,0 +1,44 @@ +package com.stormpath.sdk.servlet.application.okta; + +import com.stormpath.sdk.authc.AuthenticationResult; +import com.stormpath.sdk.ds.DataStore; +import com.stormpath.sdk.impl.application.okta.OktaSigningKeyResolver; +import com.stormpath.sdk.servlet.filter.account.JwtSigningKeyResolver; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.JwsHeader; +import io.jsonwebtoken.SignatureAlgorithm; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.security.Key; + +/** + * + */ +public class OktaJwtSigningKeyResolver implements JwtSigningKeyResolver { + + private DataStore dataStore; + + public OktaJwtSigningKeyResolver() { + + } + + public OktaJwtSigningKeyResolver(DataStore dataStore) + { + this.dataStore = dataStore; + } + + public void setDataStore(DataStore dataStore) { + this.dataStore = dataStore; + } + + @Override + public Key getSigningKey(HttpServletRequest request, HttpServletResponse response, AuthenticationResult result, SignatureAlgorithm alg) { + throw new UnsupportedOperationException("getSigningKey() is not supported"); + } + + @Override + public Key getSigningKey(HttpServletRequest request, HttpServletResponse response, JwsHeader jwsHeader, Claims claims) { + return dataStore.instantiate(OktaSigningKeyResolver.class).resolveSigningKey(jwsHeader, claims); + } +} diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/client/DefaultServletContextClientFactory.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/client/DefaultServletContextClientFactory.java index 3e81db3d29..74de1e652c 100644 --- a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/client/DefaultServletContextClientFactory.java +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/client/DefaultServletContextClientFactory.java @@ -18,15 +18,21 @@ import com.stormpath.sdk.api.ApiKey; import com.stormpath.sdk.api.ApiKeyBuilder; import com.stormpath.sdk.api.ApiKeys; +import com.stormpath.sdk.application.okta.ApplicationCredentials; import com.stormpath.sdk.cache.CacheManager; import com.stormpath.sdk.client.AuthenticationScheme; import com.stormpath.sdk.client.Client; import com.stormpath.sdk.client.ClientBuilder; import com.stormpath.sdk.client.Clients; +import com.stormpath.sdk.client.DefaultPairedApiKey; +import com.stormpath.sdk.client.PairedApiKey; import com.stormpath.sdk.client.Proxy; +import com.stormpath.sdk.impl.api.ClientApiKey; +import com.stormpath.sdk.impl.application.okta.DefaultOktaSigningKeyResolver; import com.stormpath.sdk.impl.cache.DisabledCacheManager; import com.stormpath.sdk.lang.Assert; import com.stormpath.sdk.lang.Strings; +import com.stormpath.sdk.servlet.application.okta.OktaJwtSigningKeyResolver; import com.stormpath.sdk.servlet.config.Config; import com.stormpath.sdk.servlet.config.ConfigResolver; @@ -86,7 +92,8 @@ public Client createClient(ServletContext servletContext) { applyCacheManager(builder); - return builder.build(); + return postConfigure(builder.build()); + } protected void applyBaseUrl(ClientBuilder builder) { @@ -121,25 +128,33 @@ protected void applyApiKey(ClientBuilder clientBuilder) { protected ApiKey createApiKey() { - ApiKeyBuilder apiKeyBuilder = ApiKeys.builder(); - - String value = config.get("stormpath.client.apiKey.id"); - if (Strings.hasText(value)) { - apiKeyBuilder.setId(value); + if (isOktaEnabled()) { + String oktaToken = config.get("okta.api.token"); + Assert.hasText(oktaToken, "'okta.api.token' is required when 'okta.enable' is true"); // TODO: duplicate code from spring config + return new DefaultPairedApiKey(new ClientApiKey("okta_api_token", oktaToken)); } + else { - //check for API Key ID embedded in the properties configuration - value = config.get("stormpath.client.apiKey.secret"); - if (Strings.hasText(value)) { - apiKeyBuilder.setSecret(value); - } + ApiKeyBuilder apiKeyBuilder = ApiKeys.builder(); - value = config.get(STORMPATH_API_KEY_FILE); - if (Strings.hasText(value)) { - apiKeyBuilder.setFileLocation(value); - } + String value = config.get("stormpath.client.apiKey.id"); + if (Strings.hasText(value)) { + apiKeyBuilder.setId(value); + } + + //check for API Key ID embedded in the properties configuration + value = config.get("stormpath.client.apiKey.secret"); + if (Strings.hasText(value)) { + apiKeyBuilder.setSecret(value); + } + + value = config.get(STORMPATH_API_KEY_FILE); + if (Strings.hasText(value)) { + apiKeyBuilder.setFileLocation(value); + } - return apiKeyBuilder.build(); + return apiKeyBuilder.build(); + } } protected void applyProxy(ClientBuilder builder) { @@ -178,4 +193,45 @@ protected void applyAuthenticationScheme(ClientBuilder builder) { builder.setAuthenticationScheme(scheme); } } + + protected Client postConfigure(Client client) { + + if (isOktaEnabled()) { + String oktaApplicationId = config.get("okta.application.id"); + + Assert.hasText(oktaApplicationId, "When okta.enabled is true, okta.application.id " + + "must be configured with your Okta Application ID. This can be found in the URL when accessing " + + "you application in a browser."); + + String applicationCredentialsHref = "/api/v1/internal/apps/" + oktaApplicationId + "/settings/clientcreds"; + ApplicationCredentials applicationCredentials = client.getResource(applicationCredentialsHref, ApplicationCredentials.class); + + ApiKey secondary = ApiKeys.builder() + .setId(applicationCredentials.getClientId()) + .setSecret(applicationCredentials.getClientSecret()) + .build(); + + ((PairedApiKey)client.getApiKey()).setSecondaryApiKey(secondary); + + + try { + OktaJwtSigningKeyResolver keyResolver = config.getInstance("stormpath.web.account.jwt.signingKey.resolver"); + keyResolver.setDataStore(client.getDataStore()); + } catch (ServletException e) { + throw new UnsupportedOperationException("Expected 'stormpath.web.account.jwt.signingKey.resolver', to be of type 'DefaultOktaSigningKeyResolver'", e); // TODO: user interface if this works + } + + } + return client; + } + + private boolean isOktaEnabled() { + + boolean oktaEnabled = false; + String oktaEnabledString = config.get("okta.enabled"); + if (Strings.hasText(oktaEnabledString)) { + oktaEnabled = Boolean.valueOf(oktaEnabledString); + } + return oktaEnabled; + } } diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/config/filter/RevokeTokenFilterFactory.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/config/filter/RevokeTokenFilterFactory.java index abdb803cf1..b0fe6052a3 100644 --- a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/config/filter/RevokeTokenFilterFactory.java +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/config/filter/RevokeTokenFilterFactory.java @@ -33,5 +33,6 @@ protected RevokeTokenController newController() { @Override protected void configure(RevokeTokenController controller, Config config) throws Exception { controller.setApplicationResolver(config.getApplicationResolver()); + controller.setAuthenticationResultSaver(config.getAuthenticationResultSaver()); } } diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/config/impl/AbstractCookieConfig.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/config/impl/AbstractCookieConfig.java index af1e4bab08..4acdf7a1aa 100644 --- a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/config/impl/AbstractCookieConfig.java +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/config/impl/AbstractCookieConfig.java @@ -40,7 +40,7 @@ public AbstractCookieConfig(ConfigReader configReader) { Assert.hasText(this.name, NAME + " cannot be null or empty."); this.comment = configReader.getString(configKeyFor(COMMENT)); this.domain = configReader.getString(configKeyFor(DOMAIN)); - this.maxAge = configReader.getInt(configKeyFor(MAX_AGE)); + this.maxAge = 1; this.path = configReader.getString(configKeyFor(PATH)); this.secure = configReader.getBoolean(configKeyFor(SECURE)); this.httpOnly = configReader.getBoolean(configKeyFor(HTTP_ONLY)); diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/config/impl/DefaultConfigFactory.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/config/impl/DefaultConfigFactory.java index f19fac05ff..1c5b851522 100644 --- a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/config/impl/DefaultConfigFactory.java +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/config/impl/DefaultConfigFactory.java @@ -107,6 +107,12 @@ public String[] map(String key, String value) { key = envVarNameConverter.toDottedPropertyName(key); return new String[]{key, value}; } + else if (key.startsWith("OKTA_")) { + //we want to convert env var naming convention to dotted property convention + //to allow overrides. Overrides work based on overriding identically-named keys: + key = envVarNameConverter.toDottedPropertyName(key); + return new String[]{key, value}; + } return null; } })); @@ -119,6 +125,9 @@ public String[] map(String key, String value) { if (key.startsWith("stormpath.")) { return new String[]{key, value}; } + else if (key.startsWith("okta.")) { + return new String[]{key, value}; + } return null; } })); diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/CookieAccountResolver.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/CookieAccountResolver.java index 4a62e1a805..4a6a8963d6 100644 --- a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/CookieAccountResolver.java +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/CookieAccountResolver.java @@ -85,7 +85,7 @@ public Account get(HttpServletRequest request, HttpServletResponse response) { return getAccount(request, response, val); } catch (Exception e) { String msg = "Encountered invalid JWT in access_token cookie. It might have expired, let's try with the refresh token now."; - log.debug(msg, e); + log.warn(msg, e); } } } @@ -125,26 +125,26 @@ protected Account tryRefreshToken(HttpServletRequest request, HttpServletRespons return null; } - try { - OAuthRefreshTokenRequestAuthentication refreshGrantRequest = OAuthRequests.OAUTH_REFRESH_TOKEN_REQUEST.builder() - .setRefreshToken(val) - .build(); - - OAuthGrantRequestAuthenticationResult authenticationResult = Authenticators.OAUTH_REFRESH_TOKEN_REQUEST_AUTHENTICATOR - .forApplication(ApplicationResolver.INSTANCE.getApplication(request)) - .authenticate(refreshGrantRequest); - - AccessTokenResult accessTokenResult = this.accessTokenResultFactory.createAccessTokenResult(request, response, authenticationResult); - - authenticationResultSaver.set(request, response, accessTokenResult); - return getAccount(request, response, authenticationResult.getAccessToken().getJwt()); - - } catch (Exception e) { - String msg = "Encountered invalid JWT in refresh_token cookie. We will now delete both the access and refresh cookies for safety."; - log.error(msg, e); - deleteCookie(response, cookie); - deleteCookie(response, accessTokenCookieResolver.get(request, response)); - } +// try { +// OAuthRefreshTokenRequestAuthentication refreshGrantRequest = OAuthRequests.OAUTH_REFRESH_TOKEN_REQUEST.builder() +// .setRefreshToken(val) +// .build(); +// +// OAuthGrantRequestAuthenticationResult authenticationResult = Authenticators.OAUTH_REFRESH_TOKEN_REQUEST_AUTHENTICATOR +// .forApplication(ApplicationResolver.INSTANCE.getApplication(request)) +// .authenticate(refreshGrantRequest); +// +// AccessTokenResult accessTokenResult = this.accessTokenResultFactory.createAccessTokenResult(request, response, authenticationResult); +// +// authenticationResultSaver.set(request, response, accessTokenResult); +// return getAccount(request, response, authenticationResult.getAccessToken().getJwt()); +// +// } catch (Exception e) { +// String msg = "Encountered invalid JWT in refresh_token cookie. We will now delete both the access and refresh cookies for safety."; +// log.error(msg, e); +// deleteCookie(response, cookie); +// deleteCookie(response, accessTokenCookieResolver.get(request, response)); +// } return null; } diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/CookieAuthenticationResultSaver.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/CookieAuthenticationResultSaver.java index 325c5ab60a..ca1bbdc170 100644 --- a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/CookieAuthenticationResultSaver.java +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/CookieAuthenticationResultSaver.java @@ -39,6 +39,8 @@ import io.jsonwebtoken.JwsHeader; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.SigningKeyResolver; +import io.jsonwebtoken.SigningKeyResolverAdapter; import org.joda.time.DateTime; import org.joda.time.Seconds; import org.slf4j.Logger; @@ -47,6 +49,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.UnsupportedEncodingException; +import java.security.Key; import java.util.Date; /** @@ -65,15 +68,19 @@ public class CookieAuthenticationResultSaver implements Saver secureCookieRequired) { + Resolver secureCookieRequired, + JwtSigningKeyResolver signingKeyResolver) { Assert.notNull(accessTokenCookieConfig, "accessTokenCookieConfig cannot be null."); Assert.notNull(refreshTokenCookieConfig, "refreshTokenCookieConfig cannot be null."); Assert.notNull(secureCookieRequired, "secureCookieRequired cannot be null."); this.accessTokenCookieConfig = accessTokenCookieConfig; this.refreshTokenCookieConfig = refreshTokenCookieConfig; this.secureCookieRequired = secureCookieRequired; + this.signingKeyResolver = signingKeyResolver; } @Override @@ -93,10 +100,10 @@ public void set(HttpServletRequest request, HttpServletResponse response, Authen String accessToken = accessTokenResult.getTokenResponse().getAccessToken(); String refreshToken = accessTokenResult.getTokenResponse().getRefreshToken(); - getAccessTokenCookieSaver(request, getMaxAge(accessToken, clientSecret, accessTokenCookieConfig)).set(request, response, accessToken); + getAccessTokenCookieSaver(request, getMaxAge(accessToken, clientSecret, accessTokenCookieConfig, request, response)).set(request, response, accessToken); //Client Credentials auth workflow doesn't contains a refresh token if (Strings.hasText(refreshToken)) { - getRefreshTokenCookieSaver(request, getMaxAge(refreshToken, clientSecret, refreshTokenCookieConfig)).set(request, response, refreshToken); + getRefreshTokenCookieSaver(request, getMaxAge(refreshToken, clientSecret, refreshTokenCookieConfig, request, response)).set(request, response, refreshToken); } } if (value instanceof TransientAuthenticationResult) { @@ -121,8 +128,8 @@ public void set(HttpServletRequest request, HttpServletResponse response, Authen String accessToken = authenticationResult.getAccessTokenString(); String refreshToken = authenticationResult.getRefreshTokenString(); - getAccessTokenCookieSaver(request, getMaxAge(accessToken, clientSecret, accessTokenCookieConfig)).set(request, response, accessToken); - getRefreshTokenCookieSaver(request, getMaxAge(refreshToken, clientSecret, refreshTokenCookieConfig)).set(request, response, refreshToken); + getAccessTokenCookieSaver(request, getMaxAge(accessToken, clientSecret, accessTokenCookieConfig, request, response)).set(request, response, accessToken); + getRefreshTokenCookieSaver(request, getMaxAge(refreshToken, clientSecret, refreshTokenCookieConfig, request, response)).set(request, response, refreshToken); } catch (UnsupportedEncodingException e) { //Should not happen since UTF-8 should always be a supported encoding, but we logged just in case log.error("Error get the client API Secret", e); @@ -254,18 +261,28 @@ public boolean isHttpOnly() { }); } - private int getMaxAge(String token, byte[] clientSecret, CookieConfig cookieConfig) { + private int getMaxAge(String token, byte[] clientSecret, CookieConfig cookieConfig, HttpServletRequest request, HttpServletResponse response) { // non-zero indicates override from cookie config - if (cookieConfig.getMaxAge() != 0) { + if (cookieConfig.getMaxAge() != 0 || token.split("\\.").length != 3) { return cookieConfig.getMaxAge(); } // otherwise, use the claims in the JWT to determine maxAge - Jws claimsJws = Jwts.parser().setSigningKey(clientSecret).parseClaimsJws(token); + Jws claimsJws = Jwts.parser().setSigningKeyResolver(createKeyResolver(request, response)).parseClaimsJws(token); DateTime issueAt = new DateTime(claimsJws.getBody().getIssuedAt()); DateTime expiration = new DateTime(claimsJws.getBody().getExpiration()); return Seconds.secondsBetween(issueAt, expiration).getSeconds() - Seconds.secondsBetween(issueAt, DateTime.now()).getSeconds(); } + + private SigningKeyResolver createKeyResolver(final HttpServletRequest request, final HttpServletResponse response) { + return new SigningKeyResolverAdapter() { + + @Override + public Key resolveSigningKey(JwsHeader header, Claims claims) { + return signingKeyResolver.getSigningKey(request, response, header, claims); + } + }; + } } diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/OktaJwtAccountResolver.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/OktaJwtAccountResolver.java new file mode 100644 index 0000000000..e391aa37bb --- /dev/null +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/OktaJwtAccountResolver.java @@ -0,0 +1,61 @@ +package com.stormpath.sdk.servlet.filter.account; + +import com.stormpath.sdk.account.Account; +import com.stormpath.sdk.client.Client; +import com.stormpath.sdk.impl.okta.OktaApiPaths; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.JwsHeader; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SigningKeyResolver; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.security.Key; + +/** + * + */ +public class OktaJwtAccountResolver implements JwtAccountResolver { + + private final JwtSigningKeyResolver signingKeyResolver; + + public OktaJwtAccountResolver(JwtSigningKeyResolver signingKeyResolver) { + this.signingKeyResolver = signingKeyResolver; + } + + @Override + public Account getAccountByJwt(final HttpServletRequest request, final HttpServletResponse response, String jwt) { + + Client client = getClient(request); + + Jws jws = Jwts.parser().setSigningKeyResolver(new SigningKeyResolver() { + @Override + public Key resolveSigningKey(JwsHeader header, Claims claims) { + return signingKeyResolver.getSigningKey(request, response, header, claims); + } + + @Override + public Key resolveSigningKey(JwsHeader header, String plaintext) { + throw new UnsupportedOperationException("resolveSigningKey(JwsHeader header, String plaintext), has not been implemented"); + } + }).parseClaimsJws(jwt); + Claims claims = jws.getBody(); + + // TODO" not sure if this is needed + if ("refresh".equals(jws.getHeader().get("stt"))) { + //Fix for https://github.com/stormpath/stormpath-sdk-java/issues/674 + //This is a refresh token, let's not allow the account to be obtained from it + return null; + } + + String accountHref = OktaApiPaths.USERS + claims.get("uid"); + + //will hit the cache: + return client.getResource(accountHref, Account.class); + } + + protected Client getClient(HttpServletRequest request) { + return (Client)request.getAttribute(Client.class.getName()); + } +} diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/config/CookieAuthenticationResultSaverFactory.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/config/CookieAuthenticationResultSaverFactory.java index 2c1caf1b6e..2ce0a4636b 100644 --- a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/config/CookieAuthenticationResultSaverFactory.java +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/config/CookieAuthenticationResultSaverFactory.java @@ -21,6 +21,7 @@ import com.stormpath.sdk.servlet.config.ConfigSingletonFactory; import com.stormpath.sdk.servlet.config.CookieConfig; import com.stormpath.sdk.servlet.filter.account.CookieAuthenticationResultSaver; +import com.stormpath.sdk.servlet.filter.account.JwtSigningKeyResolver; import com.stormpath.sdk.servlet.http.Resolver; import com.stormpath.sdk.servlet.http.Saver; @@ -39,10 +40,12 @@ protected Saver createInstance(ServletContext servletConte CookieConfig accessTokenCookieConfig = config.getAccessTokenCookieConfig(); CookieConfig refreshTokenCookieConfig = config.getRefreshTokenCookieConfig(); Resolver secureCookieRequired = config.getInstance(COOKIE_SECURE_RESOLVER); + JwtSigningKeyResolver signingKeyResolver = config.getInstance("stormpath.web.account.jwt.signingKey.resolver"); return new CookieAuthenticationResultSaver( accessTokenCookieConfig, refreshTokenCookieConfig, - secureCookieRequired + secureCookieRequired, + signingKeyResolver ); } } diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/config/JwtAccountResolverFactory.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/config/JwtAccountResolverFactory.java index de9fc9d844..f7f6d7fd82 100644 --- a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/config/JwtAccountResolverFactory.java +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/filter/account/config/JwtAccountResolverFactory.java @@ -15,6 +15,8 @@ */ package com.stormpath.sdk.servlet.filter.account.config; +import com.stormpath.sdk.servlet.config.Config; +import com.stormpath.sdk.servlet.config.ConfigResolver; import com.stormpath.sdk.servlet.config.ConfigSingletonFactory; import com.stormpath.sdk.servlet.filter.account.DefaultJwtAccountResolver; import com.stormpath.sdk.servlet.filter.account.JwtAccountResolver; @@ -30,7 +32,8 @@ public class JwtAccountResolverFactory extends ConfigSingletonFactory { + + @Override + protected JwtAccountResolver createInstance(ServletContext servletContext) throws Exception { + Config config = ConfigResolver.INSTANCE.getConfig(servletContext); + JwtSigningKeyResolver resolver = config.getInstance("stormpath.web.account.jwt.signingKey.resolver"); + + return new OktaJwtAccountResolver(resolver); + } +} diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/http/impl/StormpathHttpServletRequest.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/http/impl/StormpathHttpServletRequest.java index ac39a4f453..48815d9809 100644 --- a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/http/impl/StormpathHttpServletRequest.java +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/http/impl/StormpathHttpServletRequest.java @@ -369,30 +369,7 @@ public void login(String username, String password) throws ServletException { throw ex; } - AccessTokenResult result; - try { - OAuthPasswordGrantRequestAuthenticationBuilder requestBuilder = OAuthRequests.OAUTH_PASSWORD_GRANT_REQUEST.builder() - .setPassword(password) - .setLogin(username); - - AccountStore accountStore = authcRequest.getAccountStore(); - - if (accountStore != null) { - requestBuilder.setAccountStore(accountStore); - } - - OAuthGrantRequestAuthenticationResult authenticationResult = Authenticators.OAUTH_PASSWORD_GRANT_REQUEST_AUTHENTICATOR - .forApplication(getApplication()) - .authenticate(requestBuilder.build()); - - result = createAccessTokenResult(authenticationResult); - } catch (ResourceException e) { - FailedAuthenticationRequestEvent evt = createEvent(authcRequest, e); - publish(evt); - - String msg = "Unable to authenticate account for submitted username [" + username + "]."; - throw new ServletException(msg, e); - } + AccessTokenResult result = (AccessTokenResult) this.getApplication().authenticateAccount(authcRequest); setAttribute(StormpathHttpServletRequest.AUTH_TYPE_REQUEST_ATTRIBUTE_NAME, "LOGIN_METHOD"); diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/LoginController.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/LoginController.java index fa50f733a4..c1db1e3bef 100644 --- a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/LoginController.java +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/LoginController.java @@ -174,7 +174,7 @@ public boolean isNotAllowedIfAuthenticated() { protected void appendModel(HttpServletRequest request, HttpServletResponse response, Form form, List errors, Map model) { -// final List accountStores = accountStoreModelFactory.getAccountStores(request); + // 748: If stormpath.web.idSite.enabled is false and stormpath.web.callback.enabled is false AND // there are SAML directories mapped to the application, that is a configuration error. @@ -189,8 +189,8 @@ protected void appendModel(HttpServletRequest request, HttpServletResponse respo errors.add(ErrorModel.builder().setStatus(HttpServletResponse.SC_OK).setMessage(errorMsg).build()); } } - -// model.put("accountStores", accountStores); + final List accountStores = accountStoreModelFactory.getAccountStores(request); + model.put("accountStores", accountStores); if (isHtmlPreferred(request, response)) { model.put("forgotPasswordEnabled", forgotPasswordEnabled); diff --git a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/RevokeTokenController.java b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/RevokeTokenController.java index 1ee3bacc3b..7c5006c168 100644 --- a/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/RevokeTokenController.java +++ b/extensions/servlet/src/main/java/com/stormpath/sdk/servlet/mvc/RevokeTokenController.java @@ -16,6 +16,7 @@ package com.stormpath.sdk.servlet.mvc; import com.stormpath.sdk.application.Application; +import com.stormpath.sdk.authc.AuthenticationResult; import com.stormpath.sdk.http.HttpMethod; import com.stormpath.sdk.impl.error.DefaultError; import com.stormpath.sdk.lang.Strings; @@ -28,6 +29,7 @@ import com.stormpath.sdk.servlet.filter.oauth.OAuthErrorCode; import com.stormpath.sdk.servlet.filter.oauth.OAuthException; import com.stormpath.sdk.servlet.http.MediaType; +import com.stormpath.sdk.servlet.http.Saver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,6 +48,8 @@ public class RevokeTokenController extends AbstractController { private final static String TOKEN = "token"; private final static String TOKEN_TYPE_HINT = "token_type_hint"; + private Saver authenticationResultSaver; + public void init() { } @@ -54,6 +58,10 @@ public boolean isNotAllowedIfAuthenticated() { return false; } + public void setAuthenticationResultSaver(Saver authenticationResultSaver) { + this.authenticationResultSaver = authenticationResultSaver; + } + @Override public ViewModel handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { String method = request.getMethod(); @@ -96,6 +104,8 @@ protected ViewModel doPost(HttpServletRequest request, HttpServletResponse respo this.revoke(getApplication(request), builder.setToken(token).build()); + authenticationResultSaver.set(request, response, null); + response.setStatus(HttpServletResponse.SC_OK); response.setHeader("Content-Length", "0"); diff --git a/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/filter/account/CookieAuthenticationResultSaverHttpsWarningTest.groovy b/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/filter/account/CookieAuthenticationResultSaverHttpsWarningTest.groovy index 4f2b1d9218..c202652694 100644 --- a/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/filter/account/CookieAuthenticationResultSaverHttpsWarningTest.groovy +++ b/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/filter/account/CookieAuthenticationResultSaverHttpsWarningTest.groovy @@ -11,6 +11,7 @@ import org.testng.annotations.Test import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse +import static org.easymock.EasyMock.createMock import static org.easymock.EasyMock.expect import static org.easymock.EasyMock.isA import static org.easymock.EasyMock.isNull @@ -41,7 +42,7 @@ class CookieAuthenticationResultSaverHttpsWarningTest extends PowerMockTestCase HttpServletRequest request = createMock(HttpServletRequest.class) CookieConfig accessTokenConfig = createMock(CookieConfig.class) CookieConfig refreshTokenConfig = createMock(CookieConfig.class) - + JwtSigningKeyResolver signingKeyResolver = createMock(JwtSigningKeyResolver.class) def resolver = createMock(Resolver.class) expect(accessTokenConfig.isSecure()).andReturn(true) @@ -50,13 +51,13 @@ class CookieAuthenticationResultSaverHttpsWarningTest extends PowerMockTestCase expect(log.warn(isA(String))) replay LoggerFactory.class - replay log, request, accessTokenConfig, refreshTokenConfig, resolver + replay log, request, accessTokenConfig, refreshTokenConfig, resolver, signingKeyResolver - def saver = new CookieAuthenticationResultSaver(accessTokenConfig, refreshTokenConfig, resolver) + def saver = new CookieAuthenticationResultSaver(accessTokenConfig, refreshTokenConfig, resolver, signingKeyResolver) assertFalse saver.isCookieSecure(request, accessTokenConfig) verify LoggerFactory.class - verify log, request, accessTokenConfig, refreshTokenConfig, resolver + verify log, request, accessTokenConfig, refreshTokenConfig, resolver, signingKeyResolver reset LoggerFactory.class } } diff --git a/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/filter/account/CookieAuthenticationResultSaverTest.groovy b/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/filter/account/CookieAuthenticationResultSaverTest.groovy index 0bf93f09f3..9a844f5b03 100644 --- a/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/filter/account/CookieAuthenticationResultSaverTest.groovy +++ b/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/filter/account/CookieAuthenticationResultSaverTest.groovy @@ -43,6 +43,7 @@ class CookieAuthenticationResultSaverTest extends PowerMockTestCase { HttpServletRequest request = createMock(HttpServletRequest.class) CookieConfig accessTokenConfig = createMock(CookieConfig.class) CookieConfig refreshTokenConfig = createMock(CookieConfig.class) + JwtSigningKeyResolver signingKeyResolver = createMock(JwtSigningKeyResolver.class) //Let's make the IsRequestSecureResolver return true def resolver = new Resolver() { @@ -55,13 +56,13 @@ class CookieAuthenticationResultSaverTest extends PowerMockTestCase { expect(accessTokenConfig.isSecure()).andReturn(true) expect(request.getScheme()).andReturn "http" - replay request, accessTokenConfig, refreshTokenConfig + replay request, accessTokenConfig, refreshTokenConfig, signingKeyResolver - def saver = new CookieAuthenticationResultSaver(accessTokenConfig, refreshTokenConfig, resolver) + def saver = new CookieAuthenticationResultSaver(accessTokenConfig, refreshTokenConfig, resolver, signingKeyResolver) assertFalse saver.isCookieSecure(request, accessTokenConfig) - verify request, refreshTokenConfig, refreshTokenConfig + verify request, refreshTokenConfig, refreshTokenConfig, signingKeyResolver } /** @@ -73,6 +74,7 @@ class CookieAuthenticationResultSaverTest extends PowerMockTestCase { HttpServletRequest request = createMock(HttpServletRequest.class) CookieConfig accessTokenConfig = createMock(CookieConfig.class) CookieConfig refreshTokenConfig = createMock(CookieConfig.class) + JwtSigningKeyResolver signingKeyResolver = createMock(JwtSigningKeyResolver.class) def localhost = createMock(Resolver.class) def resolver = new SecureRequiredExceptForLocalhostResolver(localhost) { @@ -85,12 +87,12 @@ class CookieAuthenticationResultSaverTest extends PowerMockTestCase { expect(accessTokenConfig.isSecure()).andReturn(true) expect(request.getScheme()).andReturn "https" - replay request, accessTokenConfig, refreshTokenConfig, localhost + replay request, accessTokenConfig, refreshTokenConfig, localhost, signingKeyResolver - def saver = new CookieAuthenticationResultSaver(accessTokenConfig, refreshTokenConfig, resolver) + def saver = new CookieAuthenticationResultSaver(accessTokenConfig, refreshTokenConfig, resolver, signingKeyResolver) assertFalse saver.isCookieSecure(request, accessTokenConfig) - verify request, refreshTokenConfig, refreshTokenConfig, localhost + verify request, refreshTokenConfig, refreshTokenConfig, localhost, signingKeyResolver } } diff --git a/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/filter/oauth/DefaultAccessTokenRequestAuthorizerTest.groovy b/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/filter/oauth/DefaultAccessOktaTokenRequestAuthorizerTest.groovy similarity index 100% rename from extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/filter/oauth/DefaultAccessTokenRequestAuthorizerTest.groovy rename to extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/filter/oauth/DefaultAccessOktaTokenRequestAuthorizerTest.groovy diff --git a/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/filter/oauth/OriginAccessTokenRequestAuthorizerTest.groovy b/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/filter/oauth/OriginAccessOktaTokenRequestAuthorizerTest.groovy similarity index 100% rename from extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/filter/oauth/OriginAccessTokenRequestAuthorizerTest.groovy rename to extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/filter/oauth/OriginAccessOktaTokenRequestAuthorizerTest.groovy diff --git a/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/mvc/LoginControllerTest.groovy b/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/mvc/LoginControllerTest.groovy index c42e944649..080636c05f 100644 --- a/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/mvc/LoginControllerTest.groovy +++ b/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/mvc/LoginControllerTest.groovy @@ -195,7 +195,7 @@ class LoginControllerTest { List accountStores = new ArrayList() accountStores.add(new DefaultAccountStoreModel(null, new DefaultProviderModel('foo', 'saml'), null)) -// expect(accountStoreModelFactory.getAccountStores(request)).andReturn(accountStores) + expect(accountStoreModelFactory.getAccountStores(request)).andReturn(accountStores) expect(request.getAttribute(UserAgents.USER_AGENT_REQUEST_ATTRIBUTE_NAME)).andReturn new DefaultUserAgent(request) expect(request.getParameter("status")).andReturn null expect(request.getHeader("Accept")).andReturn "text/html" diff --git a/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/mvc/provider/ExternalAccountStoreModelFactoryTest.groovy b/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/mvc/provider/ExternalAccountStoreModelFactoryTest.groovy index 91386b3ffa..d7d3d440e6 100644 --- a/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/mvc/provider/ExternalAccountStoreModelFactoryTest.groovy +++ b/extensions/servlet/src/test/groovy/com/stormpath/sdk/servlet/mvc/provider/ExternalAccountStoreModelFactoryTest.groovy @@ -58,6 +58,7 @@ class ExternalAccountStoreModelFactoryTest { initAccountStores() accountStoreMappings = [] applicationAccountStoreMappingList = createNiceMock(ApplicationAccountStoreMappingList) + expect(applicationAccountStoreMappingList.getHref()).andReturn("http://api.example.com/mappings") expect(applicationAccountStoreMappingList.iterator()).andStubAnswer(new IAnswer>() { @Override Iterator answer() throws Throwable { diff --git a/extensions/spring/boot/pom.xml b/extensions/spring/boot/pom.xml index e0602614d2..0230cfbb5e 100644 --- a/extensions/spring/boot/pom.xml +++ b/extensions/spring/boot/pom.xml @@ -21,7 +21,7 @@ com.stormpath.spring stormpath-spring-parent - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml diff --git a/extensions/spring/boot/stormpath-default-spring-boot-starter/pom.xml b/extensions/spring/boot/stormpath-default-spring-boot-starter/pom.xml index 1f12039f37..45863b1795 100644 --- a/extensions/spring/boot/stormpath-default-spring-boot-starter/pom.xml +++ b/extensions/spring/boot/stormpath-default-spring-boot-starter/pom.xml @@ -19,7 +19,7 @@ com.stormpath.spring stormpath-spring-parent - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../../pom.xml diff --git a/extensions/spring/boot/stormpath-spring-boot-starter/pom.xml b/extensions/spring/boot/stormpath-spring-boot-starter/pom.xml index 2be0847abf..fd86fea91a 100644 --- a/extensions/spring/boot/stormpath-spring-boot-starter/pom.xml +++ b/extensions/spring/boot/stormpath-spring-boot-starter/pom.xml @@ -19,7 +19,7 @@ com.stormpath.spring stormpath-spring-parent - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../../pom.xml diff --git a/extensions/spring/boot/stormpath-spring-security-spring-boot-starter/pom.xml b/extensions/spring/boot/stormpath-spring-security-spring-boot-starter/pom.xml index d996d00c49..dd6d69b799 100644 --- a/extensions/spring/boot/stormpath-spring-security-spring-boot-starter/pom.xml +++ b/extensions/spring/boot/stormpath-spring-security-spring-boot-starter/pom.xml @@ -19,7 +19,7 @@ com.stormpath.spring stormpath-spring-parent - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../../pom.xml diff --git a/extensions/spring/boot/stormpath-spring-security-webmvc-spring-boot-starter/pom.xml b/extensions/spring/boot/stormpath-spring-security-webmvc-spring-boot-starter/pom.xml index 2e30170c47..959be27c0c 100644 --- a/extensions/spring/boot/stormpath-spring-security-webmvc-spring-boot-starter/pom.xml +++ b/extensions/spring/boot/stormpath-spring-security-webmvc-spring-boot-starter/pom.xml @@ -19,7 +19,7 @@ com.stormpath.spring stormpath-spring-parent - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../../pom.xml diff --git a/extensions/spring/boot/stormpath-spring-security-webmvc-spring-boot-starter/src/main/java/com/stormpath/spring/boot/autoconfigure/StormpathWebSecurityAutoConfiguration.java b/extensions/spring/boot/stormpath-spring-security-webmvc-spring-boot-starter/src/main/java/com/stormpath/spring/boot/autoconfigure/StormpathWebSecurityAutoConfiguration.java index 421a3d84ea..12f4160742 100644 --- a/extensions/spring/boot/stormpath-spring-security-webmvc-spring-boot-starter/src/main/java/com/stormpath/spring/boot/autoconfigure/StormpathWebSecurityAutoConfiguration.java +++ b/extensions/spring/boot/stormpath-spring-security-webmvc-spring-boot-starter/src/main/java/com/stormpath/spring/boot/autoconfigure/StormpathWebSecurityAutoConfiguration.java @@ -21,6 +21,7 @@ import com.stormpath.sdk.servlet.filter.account.AccountResolverFilter; import com.stormpath.sdk.servlet.mvc.ErrorModelFactory; import com.stormpath.spring.config.AbstractStormpathWebSecurityConfiguration; +import com.stormpath.spring.config.StormpathLoginSuccessHandler; import com.stormpath.spring.filter.ContentNegotiationSpringSecurityAuthenticationFilter; import com.stormpath.spring.filter.StormpathSecurityContextPersistenceFilter; import com.stormpath.spring.filter.StormpathWrapperFilter; @@ -42,6 +43,7 @@ import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.logout.LogoutHandler; import org.springframework.security.web.csrf.CsrfTokenRepository; +import org.springframework.security.web.savedrequest.NullRequestCache; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.servlet.DispatcherServlet; diff --git a/extensions/spring/boot/stormpath-thymeleaf-spring-boot-starter/pom.xml b/extensions/spring/boot/stormpath-thymeleaf-spring-boot-starter/pom.xml index a6c24f7272..63531ae0ef 100644 --- a/extensions/spring/boot/stormpath-thymeleaf-spring-boot-starter/pom.xml +++ b/extensions/spring/boot/stormpath-thymeleaf-spring-boot-starter/pom.xml @@ -19,7 +19,7 @@ com.stormpath.spring stormpath-spring-parent - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../../pom.xml diff --git a/extensions/spring/boot/stormpath-webmvc-spring-boot-starter/pom.xml b/extensions/spring/boot/stormpath-webmvc-spring-boot-starter/pom.xml index 6fdaaa8975..c8c2636c18 100644 --- a/extensions/spring/boot/stormpath-webmvc-spring-boot-starter/pom.xml +++ b/extensions/spring/boot/stormpath-webmvc-spring-boot-starter/pom.xml @@ -19,7 +19,7 @@ com.stormpath.spring stormpath-spring-parent - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../../pom.xml diff --git a/extensions/spring/cloud/pom.xml b/extensions/spring/cloud/pom.xml index d7ee8ca70f..001b7ee9b3 100644 --- a/extensions/spring/cloud/pom.xml +++ b/extensions/spring/cloud/pom.xml @@ -21,7 +21,7 @@ com.stormpath.spring stormpath-spring-parent - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml diff --git a/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/pom.xml b/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/pom.xml index 9eaed6a2cb..153bb8f742 100644 --- a/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/pom.xml +++ b/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/pom.xml @@ -19,7 +19,7 @@ com.stormpath.spring stormpath-spring-parent - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../../pom.xml diff --git a/extensions/spring/pom.xml b/extensions/spring/pom.xml index af36b03645..fe972db872 100644 --- a/extensions/spring/pom.xml +++ b/extensions/spring/pom.xml @@ -21,7 +21,7 @@ com.stormpath.sdk stormpath-sdk-root - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../../pom.xml diff --git a/extensions/spring/stormpath-spring-security-webmvc/pom.xml b/extensions/spring/stormpath-spring-security-webmvc/pom.xml index 296ba1b065..1302b7c76e 100644 --- a/extensions/spring/stormpath-spring-security-webmvc/pom.xml +++ b/extensions/spring/stormpath-spring-security-webmvc/pom.xml @@ -19,7 +19,7 @@ com.stormpath.spring stormpath-spring-parent - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml diff --git a/extensions/spring/stormpath-spring-security-webmvc/src/main/java/com/stormpath/spring/config/AbstractStormpathWebSecurityConfiguration.java b/extensions/spring/stormpath-spring-security-webmvc/src/main/java/com/stormpath/spring/config/AbstractStormpathWebSecurityConfiguration.java index b1815bc7a2..1401a94ba4 100644 --- a/extensions/spring/stormpath-spring-security-webmvc/src/main/java/com/stormpath/spring/config/AbstractStormpathWebSecurityConfiguration.java +++ b/extensions/spring/stormpath-spring-security-webmvc/src/main/java/com/stormpath/spring/config/AbstractStormpathWebSecurityConfiguration.java @@ -141,6 +141,9 @@ public abstract class AbstractStormpathWebSecurityConfiguration { @Value("#{ @environment['stormpath.web.stormpathFilter.enabled'] ?: true }") protected boolean stormpathFilterEnabled; + @Value("#{ @environment['okta.enabled'] ?: true }") + protected boolean oktaEnabled; + @Autowired List> stormpathAccountResolvers; @@ -162,8 +165,18 @@ public SecurityConfigurerAdapter stormpathSecurityConfigurerAdapter() { } public AuthenticationSuccessHandler stormpathAuthenticationSuccessHandler() { - StormpathLoginSuccessHandler loginSuccessHandler = - new StormpathLoginSuccessHandler(client, authenticationResultSaver, produces); + + StormpathLoginSuccessHandler loginSuccessHandler; + + if (oktaEnabled) { + loginSuccessHandler = + new OktaLoginSuccessHandler(client, authenticationResultSaver, produces); + } + else { + loginSuccessHandler = + new StormpathLoginSuccessHandler(client, authenticationResultSaver, produces); + } + loginSuccessHandler.setDefaultTargetUrl(loginNextUri); loginSuccessHandler.setTargetUrlParameter("next"); loginSuccessHandler.setRequestCache(new NullRequestCache()); diff --git a/extensions/spring/stormpath-spring-security-webmvc/src/main/java/com/stormpath/spring/config/OktaLoginSuccessHandler.java b/extensions/spring/stormpath-spring-security-webmvc/src/main/java/com/stormpath/spring/config/OktaLoginSuccessHandler.java new file mode 100644 index 0000000000..7240ec9ba9 --- /dev/null +++ b/extensions/spring/stormpath-spring-security-webmvc/src/main/java/com/stormpath/spring/config/OktaLoginSuccessHandler.java @@ -0,0 +1,32 @@ +package com.stormpath.spring.config; + +import com.stormpath.sdk.authc.AuthenticationResult; +import com.stormpath.sdk.client.Client; +import com.stormpath.sdk.servlet.http.Saver; +import com.stormpath.spring.security.token.JwtProviderAuthenticationToken; +import org.springframework.security.core.Authentication; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * + */ +public class OktaLoginSuccessHandler extends StormpathLoginSuccessHandler { + + private final Saver authenticationResultSaver; + + public OktaLoginSuccessHandler(Client client, Saver saver, String produces) { + super(client, saver, produces); + this.authenticationResultSaver = saver; + } + + @Override + protected void saveAccount(HttpServletRequest request, HttpServletResponse response, final Authentication authentication) throws IOException, ServletException { + + JwtProviderAuthenticationToken token = (JwtProviderAuthenticationToken) authentication; + authenticationResultSaver.set(request, response, token.getAuthenticationResult()); + } +} diff --git a/extensions/spring/stormpath-spring-security-webmvc/src/main/java/com/stormpath/spring/config/SpringSecurityLoginErrorModelFactory.java b/extensions/spring/stormpath-spring-security-webmvc/src/main/java/com/stormpath/spring/config/SpringSecurityLoginErrorModelFactory.java index 03989da2f7..2a3400499d 100644 --- a/extensions/spring/stormpath-spring-security-webmvc/src/main/java/com/stormpath/spring/config/SpringSecurityLoginErrorModelFactory.java +++ b/extensions/spring/stormpath-spring-security-webmvc/src/main/java/com/stormpath/spring/config/SpringSecurityLoginErrorModelFactory.java @@ -23,7 +23,8 @@ public class SpringSecurityLoginErrorModelFactory extends AbstractErrorModelFact private static final List UNSUCCESSFUL_LOGIN_BACKEND_ERRORS = Arrays.asList( "Login attempt failed", "Invalid username or password", - "Login and password required" + "Login and password required", + "invalid_grant" // for Okta ); @Autowired diff --git a/extensions/spring/stormpath-spring-security/pom.xml b/extensions/spring/stormpath-spring-security/pom.xml index ae92299a6d..17f30c3c38 100644 --- a/extensions/spring/stormpath-spring-security/pom.xml +++ b/extensions/spring/stormpath-spring-security/pom.xml @@ -19,7 +19,7 @@ com.stormpath.spring stormpath-spring-parent - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml diff --git a/extensions/spring/stormpath-spring-security/src/main/java/com/stormpath/spring/config/AbstractStormpathSpringSecurityConfiguration.java b/extensions/spring/stormpath-spring-security/src/main/java/com/stormpath/spring/config/AbstractStormpathSpringSecurityConfiguration.java index 81170b1fd4..e420ad946c 100644 --- a/extensions/spring/stormpath-spring-security/src/main/java/com/stormpath/spring/config/AbstractStormpathSpringSecurityConfiguration.java +++ b/extensions/spring/stormpath-spring-security/src/main/java/com/stormpath/spring/config/AbstractStormpathSpringSecurityConfiguration.java @@ -16,6 +16,8 @@ package com.stormpath.spring.config; import com.stormpath.sdk.application.Application; +import com.stormpath.sdk.authc.OktaAuthNAuthenticator; +import com.stormpath.sdk.client.Client; import com.stormpath.spring.security.provider.AccountCustomDataPermissionResolver; import com.stormpath.spring.security.provider.AccountGrantedAuthorityResolver; import com.stormpath.spring.security.provider.AccountPermissionResolver; @@ -25,9 +27,11 @@ import com.stormpath.spring.security.provider.GroupCustomDataPermissionResolver; import com.stormpath.spring.security.provider.GroupGrantedAuthorityResolver; import com.stormpath.spring.security.provider.GroupPermissionResolver; +import com.stormpath.spring.security.provider.OktaAuthenticationProvider; import com.stormpath.spring.security.provider.StormpathAuthenticationProvider; import com.stormpath.spring.security.provider.UsernamePasswordAuthenticationTokenFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.ProviderManager; @@ -39,9 +43,15 @@ */ public abstract class AbstractStormpathSpringSecurityConfiguration { + @Value("#{ @environment['okta.enabled'] ?: true }") + protected boolean oktaEnabled; + @Autowired private Application application; + @Autowired + private Client client; + public GroupGrantedAuthorityResolver stormpathGroupGrantedAuthorityResolver() { return new DefaultGroupGrantedAuthorityResolver(); } @@ -64,7 +74,15 @@ public AuthenticationTokenFactory stormpathAuthenticationTokenFactory() { public AuthenticationProvider stormpathAuthenticationProvider() { - StormpathAuthenticationProvider provider = new StormpathAuthenticationProvider(application); + StormpathAuthenticationProvider provider; + + if (oktaEnabled) { + provider = new OktaAuthenticationProvider(application, client); + } + else { + provider = new StormpathAuthenticationProvider(application); + } + provider.setGroupGrantedAuthorityResolver(stormpathGroupGrantedAuthorityResolver()); provider.setGroupPermissionResolver(stormpathGroupPermissionResolver()); provider.setAccountGrantedAuthorityResolver(stormpathAccountGrantedAuthorityResolver()); diff --git a/extensions/spring/stormpath-spring-security/src/main/java/com/stormpath/spring/security/provider/OktaAuthenticationProvider.java b/extensions/spring/stormpath-spring-security/src/main/java/com/stormpath/spring/security/provider/OktaAuthenticationProvider.java new file mode 100644 index 0000000000..b485c202b2 --- /dev/null +++ b/extensions/spring/stormpath-spring-security/src/main/java/com/stormpath/spring/security/provider/OktaAuthenticationProvider.java @@ -0,0 +1,85 @@ +package com.stormpath.spring.security.provider; + +import com.stormpath.sdk.account.Account; +import com.stormpath.sdk.application.Application; +import com.stormpath.sdk.authc.AuthenticationRequest; +import com.stormpath.sdk.authc.OktaAuthNAuthenticator; +import com.stormpath.sdk.client.Client; +import com.stormpath.sdk.lang.Strings; +import com.stormpath.sdk.oauth.AccessTokenResult; +import com.stormpath.sdk.resource.ResourceException; +import com.stormpath.spring.security.token.JwtProviderAuthenticationToken; +import com.stormpath.spring.security.token.ProviderAuthenticationToken; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.User; + +/** + * + */ +public class OktaAuthenticationProvider extends StormpathAuthenticationProvider { + + private final Application application; + private final Client client; + + public OktaAuthenticationProvider(Application application, Client client) { + super(application); + this.application = application; + this.client = client; + } + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + Account account; + + Authentication returnToken = null; + + try { + if (authentication instanceof ProviderAuthenticationToken) { + returnToken = authentication; + } + else if (authentication instanceof JwtProviderAuthenticationToken) { + // TODO: add tests around this flow + OktaAuthNAuthenticator authNAuthenticator = client.instantiate(OktaAuthNAuthenticator.class); + authNAuthenticator.assertValidAccessToken(((JwtProviderAuthenticationToken) authentication).getAccessToken()); + return authentication; + } + else { + + AuthenticationRequest request = createAuthenticationRequest(authentication); + + try { + AccessTokenResult result = (AccessTokenResult) application.authenticateAccount(request); + account = result.getAccount(); + + User userDetails = new User(account.getHref(), "", getGrantedAuthorities(account)); + returnToken = new JwtProviderAuthenticationToken(userDetails, result.getTokenResponse().getAccessToken(), result.getTokenResponse().getRefreshToken(), result); + + } finally { + //Clear the request data to prevent later memory access + request.clear(); + } + + } + } catch (ResourceException e) { + String msg = Strings.clean(e.getMessage()); + if (msg == null) { + msg = Strings.clean(e.getDeveloperMessage()); + } + if (msg == null) { + msg = "Invalid login or password."; + } + throw new AuthenticationServiceException(msg, e); + } + + return returnToken; + } + + @Override + public boolean supports(Class authentication) { + if (super.supports(authentication)) return true; + if (JwtProviderAuthenticationToken.class.isAssignableFrom(authentication)) return true; + return false; + } +} diff --git a/extensions/spring/stormpath-spring-security/src/main/java/com/stormpath/spring/security/provider/StormpathAuthenticationProvider.java b/extensions/spring/stormpath-spring-security/src/main/java/com/stormpath/spring/security/provider/StormpathAuthenticationProvider.java index d3d352da41..75aae906ec 100644 --- a/extensions/spring/stormpath-spring-security/src/main/java/com/stormpath/spring/security/provider/StormpathAuthenticationProvider.java +++ b/extensions/spring/stormpath-spring-security/src/main/java/com/stormpath/spring/security/provider/StormpathAuthenticationProvider.java @@ -361,13 +361,15 @@ protected Collection getGrantedAuthorities(Account account) { GroupList groups = account.getGroups(); - for (Group group : groups) { - if (GroupStatus.ENABLED.equals(group.getStatus())) { - Set groupGrantedAuthorities = resolveGrantedAuthorities(group); - grantedAuthorities.addAll(groupGrantedAuthorities); - - Set groupPermissions = resolvePermissions(group); - grantedAuthorities.addAll(groupPermissions); + if (groups != null) { + for (Group group : groups) { + if (GroupStatus.ENABLED.equals(group.getStatus())) { + Set groupGrantedAuthorities = resolveGrantedAuthorities(group); + grantedAuthorities.addAll(groupGrantedAuthorities); + + Set groupPermissions = resolvePermissions(group); + grantedAuthorities.addAll(groupPermissions); + } } } diff --git a/extensions/spring/stormpath-spring-security/src/main/java/com/stormpath/spring/security/token/JwtProviderAuthenticationToken.java b/extensions/spring/stormpath-spring-security/src/main/java/com/stormpath/spring/security/token/JwtProviderAuthenticationToken.java new file mode 100644 index 0000000000..9676ad14fe --- /dev/null +++ b/extensions/spring/stormpath-spring-security/src/main/java/com/stormpath/spring/security/token/JwtProviderAuthenticationToken.java @@ -0,0 +1,48 @@ +package com.stormpath.spring.security.token; + +import com.stormpath.sdk.authc.AuthenticationResult; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; + +/** + * + */ +public class JwtProviderAuthenticationToken extends AbstractAuthenticationToken { + + private final String accessToken; + private final String refreshToken; + private final UserDetails userDetails; + private final AuthenticationResult authenticationResult; + + public JwtProviderAuthenticationToken(User userDetails, String accessToken, String refreshToken, AuthenticationResult authenticationResult) { + + super(userDetails.getAuthorities()); + this.accessToken = accessToken; + this.refreshToken = refreshToken; + this.userDetails = userDetails; + this.authenticationResult = authenticationResult; + } + + @Override + public Object getCredentials() { + return null; + } + + @Override + public Object getPrincipal() { + return userDetails; + } + + public String getAccessToken() { + return accessToken; + } + + public String getRefreshToken() { + return refreshToken; + } + + public AuthenticationResult getAuthenticationResult() { + return authenticationResult; + } +} diff --git a/extensions/spring/stormpath-spring-webmvc/pom.xml b/extensions/spring/stormpath-spring-webmvc/pom.xml index 03986ceb6a..2df02027ee 100644 --- a/extensions/spring/stormpath-spring-webmvc/pom.xml +++ b/extensions/spring/stormpath-spring-webmvc/pom.xml @@ -19,7 +19,7 @@ com.stormpath.spring stormpath-spring-parent - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml diff --git a/extensions/spring/stormpath-spring-webmvc/src/main/java/com/stormpath/spring/config/AbstractStormpathWebMvcConfiguration.java b/extensions/spring/stormpath-spring-webmvc/src/main/java/com/stormpath/spring/config/AbstractStormpathWebMvcConfiguration.java index 0301be0c74..9afd8c927e 100644 --- a/extensions/spring/stormpath-spring-webmvc/src/main/java/com/stormpath/spring/config/AbstractStormpathWebMvcConfiguration.java +++ b/extensions/spring/stormpath-spring-webmvc/src/main/java/com/stormpath/spring/config/AbstractStormpathWebMvcConfiguration.java @@ -22,6 +22,7 @@ import com.stormpath.sdk.cache.Cache; import com.stormpath.sdk.client.Client; import com.stormpath.sdk.idsite.IdSiteResultListener; +import com.stormpath.sdk.impl.application.okta.OktaSigningKeyResolver; import com.stormpath.sdk.lang.Assert; import com.stormpath.sdk.lang.BiPredicate; import com.stormpath.sdk.lang.Collections; @@ -31,6 +32,7 @@ import com.stormpath.sdk.servlet.account.DefaultAccountResolver; import com.stormpath.sdk.servlet.application.ApplicationResolver; import com.stormpath.sdk.servlet.application.DefaultApplicationResolver; +import com.stormpath.sdk.servlet.application.okta.OktaJwtSigningKeyResolver; import com.stormpath.sdk.servlet.authz.RequestAuthorizer; import com.stormpath.sdk.servlet.config.CookieConfig; import com.stormpath.sdk.servlet.config.RegisterEnabledPredicate; @@ -72,6 +74,7 @@ import com.stormpath.sdk.servlet.filter.account.DefaultJwtAccountResolver; import com.stormpath.sdk.servlet.filter.account.JwtAccountResolver; import com.stormpath.sdk.servlet.filter.account.JwtSigningKeyResolver; +import com.stormpath.sdk.servlet.filter.account.OktaJwtAccountResolver; import com.stormpath.sdk.servlet.filter.mvc.ControllerFilter; import com.stormpath.sdk.servlet.filter.oauth.AccessTokenAuthenticationRequestFactory; import com.stormpath.sdk.servlet.filter.oauth.AccessTokenResultFactory; @@ -167,6 +170,8 @@ import com.stormpath.spring.mvc.TemplateLayoutInterceptor; import com.stormpath.spring.mvc.VerifyControllerConfig; import com.stormpath.spring.util.SpringPatternMatcher; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.JwsHeader; import io.jsonwebtoken.SignatureAlgorithm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -210,6 +215,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.security.Key; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -420,6 +426,9 @@ public abstract class AbstractStormpathWebMvcConfiguration { @Value("#{ @environment['stormpath.web.cors.allow.credentials'] ?: true }") protected boolean corsAllowCredentials; + @Value("#{ @environment['okta.enabled'] ?: true }") + protected boolean oktaEnabled; + @Autowired(required = false) protected PathMatcher pathMatcher; @@ -750,7 +759,8 @@ public Saver stormpathCookieAuthenticationResultSaver() { return new CookieAuthenticationResultSaver( stormpathAccessTokenCookieConfig(), stormpathRefreshTokenCookieConfig(), - stormpathSecureResolver() + stormpathSecureResolver(), + stormpathJwtSigningKeyResolver() ); } @@ -784,7 +794,21 @@ public Saver stormpathAuthenticationResultSaver() { } public JwtSigningKeyResolver stormpathJwtSigningKeyResolver() { - return new JwtTokenSigningKeyResolver(); + if (oktaEnabled) { + return new JwtSigningKeyResolver() { + @Override + public Key getSigningKey(HttpServletRequest request, HttpServletResponse response, AuthenticationResult result, SignatureAlgorithm alg) { + throw new UnsupportedOperationException("getSigningKey() is not supported"); + } + + @Override + public Key getSigningKey(HttpServletRequest request, HttpServletResponse response, JwsHeader jwsHeader, Claims claims) { + return client.instantiate(OktaSigningKeyResolver.class).resolveSigningKey(jwsHeader, claims); + } + }; + } else { + return new JwtTokenSigningKeyResolver(); + } } public RequestEventListener stormpathRequestEventListener() { @@ -803,7 +827,12 @@ public String stormpathCsrfTokenSigningKey() { } public JwtAccountResolver stormpathJwtAccountResolver() { - return new DefaultJwtAccountResolver(stormpathJwtSigningKeyResolver()); + if (oktaEnabled) { + return new OktaJwtAccountResolver(new OktaJwtSigningKeyResolver(client.getDataStore())); + } + else { + return new DefaultJwtAccountResolver(stormpathJwtSigningKeyResolver()); + } } public Cache stormpathNonceCache() { @@ -1225,6 +1254,7 @@ public RevokeTokenControllerConfig stormpathRevokeTokenConfig() { public Controller stormpathRevokeTokenController() { RevokeTokenController c = new RevokeTokenController(); c.setApplicationResolver(stormpathApplicationResolver()); + c.setAuthenticationResultSaver(stormpathAuthenticationResultSaver()); return init(c); } diff --git a/extensions/spring/stormpath-spring/pom.xml b/extensions/spring/stormpath-spring/pom.xml index 72d614b1d1..a53cd5e987 100644 --- a/extensions/spring/stormpath-spring/pom.xml +++ b/extensions/spring/stormpath-spring/pom.xml @@ -19,7 +19,7 @@ com.stormpath.spring stormpath-spring-parent - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml diff --git a/extensions/spring/stormpath-spring/src/main/java/com/stormpath/spring/config/AbstractStormpathConfiguration.java b/extensions/spring/stormpath-spring/src/main/java/com/stormpath/spring/config/AbstractStormpathConfiguration.java index 98001b4436..ed0c2203a8 100644 --- a/extensions/spring/stormpath-spring/src/main/java/com/stormpath/spring/config/AbstractStormpathConfiguration.java +++ b/extensions/spring/stormpath-spring/src/main/java/com/stormpath/spring/config/AbstractStormpathConfiguration.java @@ -159,7 +159,7 @@ public void oktaOidcClientApiKey(@Qualifier("stormpathClientApiKey") ApiKey stor Client client = stormpathClient(); - String applicationCredentialsHref = "/internal/apps/" + oktaApplicationId + "/settings/clientcreds"; + String applicationCredentialsHref = "/api/v1/internal/apps/" + oktaApplicationId + "/settings/clientcreds"; ApplicationCredentials applicationCredentials = client.getResource(applicationCredentialsHref, ApplicationCredentials.class); ApiKey secondary = ApiKeys.builder() @@ -258,9 +258,9 @@ public Client stormpathClient() { if (baseUrl.endsWith("/")) { baseUrl = baseUrl.substring(0, baseUrl.length() - 1); } - if (baseUrl.endsWith(".com")) { - append = "/api/v1"; - } +// if (baseUrl.endsWith(".com")) { +// append = "/api/v1"; +// } if (append != null) { baseUrl += append; } diff --git a/extensions/zuul/pom.xml b/extensions/zuul/pom.xml index b0bdf92e01..b1743f859a 100644 --- a/extensions/zuul/pom.xml +++ b/extensions/zuul/pom.xml @@ -21,7 +21,7 @@ com.stormpath.sdk stormpath-sdk-root - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../../pom.xml diff --git a/impl/pom.xml b/impl/pom.xml index 852dd011a0..1e2a97f934 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -21,7 +21,7 @@ com.stormpath.sdk stormpath-sdk-root - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml diff --git a/impl/src/main/java/com/stormpath/sdk/impl/account/DefaultAccount.java b/impl/src/main/java/com/stormpath/sdk/impl/account/DefaultAccount.java index 2d36e66a08..6971172683 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/account/DefaultAccount.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/account/DefaultAccount.java @@ -142,7 +142,8 @@ public DefaultAccount(InternalDataStore dataStore) { } public DefaultAccount(InternalDataStore dataStore, Map properties) { - super(dataStore, new OktaUserAccountConverter().toAccount(properties)); + // if we have a dataStore try to convert, otherwise, just pass in the properties as is. + super(dataStore, dataStore != null ? OktaUserAccountConverter.toAccount(properties, dataStore.getBaseUrl()) : properties ); } @Override diff --git a/impl/src/main/java/com/stormpath/sdk/impl/application/DefaultApplication.java b/impl/src/main/java/com/stormpath/sdk/impl/application/DefaultApplication.java index 6286f87758..abf15548e1 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/application/DefaultApplication.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/application/DefaultApplication.java @@ -33,6 +33,7 @@ import com.stormpath.sdk.application.ApplicationAccountStoreMappingList; import com.stormpath.sdk.application.ApplicationOptions; import com.stormpath.sdk.application.ApplicationStatus; +import com.stormpath.sdk.application.OAuthApplication; import com.stormpath.sdk.application.webconfig.ApplicationWebConfig; import com.stormpath.sdk.authc.AuthenticationRequest; import com.stormpath.sdk.authc.AuthenticationResult; @@ -117,7 +118,7 @@ import static com.stormpath.sdk.impl.api.ApiKeyParameter.ID; /** @since 0.2 */ -public class DefaultApplication extends AbstractExtendableInstanceResource implements Application { +public class DefaultApplication extends AbstractExtendableInstanceResource implements Application, OAuthApplication { private static final String OAUTH_REQUEST_AUTHENTICATOR_FQCN = "com.stormpath.sdk.impl.oauth.authc.DefaultOAuthRequestAuthenticator"; @@ -874,31 +875,37 @@ public Application saveWithResponseOptions(ApplicationOptions responseOptions) { } /* @since 1.0.0 */ + @Override public OAuthClientCredentialsGrantRequestAuthenticator createClientCredentialsGrantAuthenticator() { return new DefaultOAuthClientCredentialsGrantRequestAuthenticator(this, getDataStore()); } /* @since 1.1.0 */ + @Override public OAuthStormpathSocialGrantRequestAuthenticator createStormpathSocialGrantAuthenticator() { return new DefaultOAuthStormpathSocialGrantRequestAuthenticator(this, getDataStore()); } /* @since 1.3.1 */ + @Override public OAuthStormpathFactorChallengeGrantRequestAuthenticator createStormpathFactorChallengeGrantAuthenticator() { return new DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator(this, getDataStore()); } /* @since 1.0.RC7 */ + @Override public OAuthPasswordGrantRequestAuthenticator createPasswordGrantAuthenticator() { return new DefaultOAuthPasswordGrantRequestAuthenticator(this, getDataStore()); } /* @since 1.0.RC7 */ + @Override public OAuthRefreshTokenRequestAuthenticator createRefreshGrantAuthenticator() { return new DefaultOAuthRefreshTokenRequestAuthenticator(this, getDataStore()); } /* @since 1.0.RC7 */ + @Override public OAuthBearerRequestAuthenticator createJwtAuthenticator() { return new DefaultOAuthBearerRequestAuthenticator(this, getDataStore()); } @@ -908,6 +915,7 @@ public IdSiteAuthenticator createIdSiteAuthenticator() { return new DefaultIdSiteAuthenticator(this, getDataStore()); } + @Override public OAuthTokenRevocator createOAuhtTokenRevocator() { return new DefaultOAuthTokenRevocator(this, getDataStore()); } diff --git a/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultAuthNRequest.java b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultAuthNRequest.java new file mode 100644 index 0000000000..4e9410f7d9 --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultAuthNRequest.java @@ -0,0 +1,69 @@ +package com.stormpath.sdk.impl.application.okta; + +import com.stormpath.sdk.application.okta.AuthNRequest; +import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.resource.AbstractInstanceResource; +import com.stormpath.sdk.impl.resource.MapProperty; +import com.stormpath.sdk.impl.resource.Property; +import com.stormpath.sdk.impl.resource.StringProperty; + +import java.util.Map; + +/** + * + */ +public class DefaultAuthNRequest extends AbstractInstanceResource implements AuthNRequest { + + private final static StringProperty USERNAME = new StringProperty("username"); + private final static StringProperty PASSWORD = new StringProperty("password"); + + private final static MapProperty OPTIONS = new MapProperty("options"); + + private static final Map PROPERTY_DESCRIPTORS = createPropertyDescriptorMap(USERNAME, PASSWORD, OPTIONS); + + public DefaultAuthNRequest(InternalDataStore dataStore) { + super(dataStore); + } + + public DefaultAuthNRequest(InternalDataStore dataStore, Map properties) { + super(dataStore, properties); + } + + @Override + public Map getPropertyDescriptors() { + return null; + } + + @Override + public String getUsername() { + return getString(USERNAME); + } + + @Override + public AuthNRequest setUsername(String username) { + setProperty(USERNAME, username); + return this; + } + + @Override + public String getPassword() { + return getString(PASSWORD); + } + + @Override + public AuthNRequest setPassword(String password) { + setProperty(PASSWORD, password); + return this; + } + + @Override + public Map getOptions() { + return getMap(OPTIONS); + } + + @Override + public AuthNRequest setOptions(Map options) { + setProperty(OPTIONS, options); + return this; + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultAuthNResult.java b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultAuthNResult.java new file mode 100644 index 0000000000..35f61ddcd6 --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultAuthNResult.java @@ -0,0 +1,50 @@ +package com.stormpath.sdk.impl.application.okta; + +import com.stormpath.sdk.application.okta.AuthNResult; +import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.resource.AbstractInstanceResource; +import com.stormpath.sdk.impl.resource.MapProperty; +import com.stormpath.sdk.impl.resource.Property; +import com.stormpath.sdk.impl.resource.StringProperty; +import com.stormpath.sdk.lang.Collections; +import com.stormpath.sdk.lang.Objects; + +import java.util.Map; + +/** + * + */ +public class DefaultAuthNResult extends AbstractInstanceResource implements AuthNResult { + + private final static StringProperty SESSION_TOKEN = new StringProperty("sessionToken"); + private final static MapProperty EMBEDDED = new MapProperty("_embedded"); + + private static final Map PROPERTY_DESCRIPTORS = createPropertyDescriptorMap(SESSION_TOKEN, EMBEDDED); + + public DefaultAuthNResult(InternalDataStore dataStore) { + super(dataStore); + } + + public DefaultAuthNResult(InternalDataStore dataStore, Map properties) { + super(dataStore, properties); + } + + @Override + public Map getPropertyDescriptors() { + return PROPERTY_DESCRIPTORS; + } + + @Override + public String getSessionToken() { + return getString(SESSION_TOKEN); + } + + public String getUserId() { + Map embedded = getMap(EMBEDDED); + Map user = (Map) embedded.get("user"); + if (!Collections.isEmpty(user)) { + return (String) user.get("id"); + } + return null; + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOIDCKey.java b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOIDCKey.java new file mode 100644 index 0000000000..0e076e0266 --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOIDCKey.java @@ -0,0 +1,56 @@ +package com.stormpath.sdk.impl.application.okta; + +import com.stormpath.sdk.application.okta.OIDCKey; +import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.resource.AbstractInstanceResource; +import com.stormpath.sdk.impl.resource.Property; +import com.stormpath.sdk.impl.resource.StringProperty; + +import java.util.Map; + +/** + * + */ +public class DefaultOIDCKey extends AbstractInstanceResource implements OIDCKey { + + private static final StringProperty ALGORITHM = new StringProperty("alg"); + private static final StringProperty TYPE = new StringProperty("kty"); + private static final StringProperty USE = new StringProperty("use"); + private static final StringProperty ID = new StringProperty("kid"); + + private static final Map PROPERTY_DESCRIPTORS = createPropertyDescriptorMap(ALGORITHM); + + public DefaultOIDCKey(InternalDataStore dataStore, Map properties) { + super(dataStore, properties); + } + + @Override + public Map getPropertyDescriptors() { + return PROPERTY_DESCRIPTORS; + } + + @Override + public String getAlgorithm() { + return getString(ALGORITHM); + } + + @Override + public String getId() { + return getString(ID); + } + + @Override + public String getType() { + return getString(TYPE); + } + + @Override + public String getUse() { + return getString(USE); + } + + @Override + public String get(String id) { + return getStringProperty(id); + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOIDCKeysList.java b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOIDCKeysList.java new file mode 100644 index 0000000000..bf879e223c --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOIDCKeysList.java @@ -0,0 +1,53 @@ +package com.stormpath.sdk.impl.application.okta; + +import com.stormpath.sdk.application.okta.OIDCKey; +import com.stormpath.sdk.application.okta.OIDCKeysList; +import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.resource.AbstractInstanceResource; +import com.stormpath.sdk.impl.resource.Property; +import com.stormpath.sdk.impl.resource.SetProperty; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * + */ +public class DefaultOIDCKeysList extends AbstractInstanceResource implements OIDCKeysList { + + private static final SetProperty KEYS = new SetProperty("keys", OIDCKey.class); + + private static final Map PROPERTY_DESCRIPTORS = createPropertyDescriptorMap(KEYS); + + public DefaultOIDCKeysList(InternalDataStore dataStore, Map properties) { + super(dataStore, properties); + } + + @Override + public Map getPropertyDescriptors() { + return PROPERTY_DESCRIPTORS; + } + + @Override + public Set getKeys() { + + Set keys = new HashSet<>(); + for (Object item : getSetProperty(KEYS.getName())) { + keys.add(getDataStore().instantiate(OIDCKey.class, (Map) item)); + } + + return keys; + } + + @Override + public OIDCKey getKeyById(String keyId) { + + for (OIDCKey key : getKeys()) { + if (keyId.equals(key.getId())) { + return key; + } + } + return null; + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOktaForgotPasswordRequest.java b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOktaForgotPasswordRequest.java new file mode 100644 index 0000000000..6e4014fc94 --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOktaForgotPasswordRequest.java @@ -0,0 +1,63 @@ +package com.stormpath.sdk.impl.application.okta; + +import com.stormpath.sdk.application.okta.OktaForgotPasswordRequest; +import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.resource.AbstractInstanceResource; +import com.stormpath.sdk.impl.resource.Property; +import com.stormpath.sdk.impl.resource.StringProperty; + +import java.util.Map; + +/** + * + */ +public class DefaultOktaForgotPasswordRequest extends AbstractInstanceResource implements OktaForgotPasswordRequest { + + + private static final StringProperty USERNAME = new StringProperty("username"); + private static final StringProperty FACTOR_TYPE = new StringProperty("factorType"); + private static final StringProperty RELAY_STATE = new StringProperty("relayState"); + + private static final Map PROPERTY_DESCRIPTORS = createPropertyDescriptorMap(USERNAME, FACTOR_TYPE, RELAY_STATE); + + public DefaultOktaForgotPasswordRequest(InternalDataStore dataStore) { + super(dataStore); + } + + @Override + public Map getPropertyDescriptors() { + return PROPERTY_DESCRIPTORS; + } + @Override + public String getUsername() { + return getString(USERNAME); + } + + @Override + public OktaForgotPasswordRequest setUsername(String username) { + setProperty(USERNAME, username); + return this; + } + + @Override + public String getFactorType() { + return getString(FACTOR_TYPE); + } + + @Override + public OktaForgotPasswordRequest setFactorType(String factorType) { + setProperty(FACTOR_TYPE, factorType); + return this; + } + + @Override + public String getRelayState() { + return getString(RELAY_STATE); + } + + @Override + public OktaForgotPasswordRequest setRelayState(String relayState) { + setProperty(RELAY_STATE, relayState); + return this; + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOktaForgotPasswordResult.java b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOktaForgotPasswordResult.java new file mode 100644 index 0000000000..0dec7f9d8b --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOktaForgotPasswordResult.java @@ -0,0 +1,62 @@ +package com.stormpath.sdk.impl.application.okta; + +import com.stormpath.sdk.application.okta.OktaForgotPasswordResult; +import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.resource.AbstractInstanceResource; +import com.stormpath.sdk.impl.resource.Property; +import com.stormpath.sdk.impl.resource.StringProperty; + +import java.util.Map; + +/** + * + */ +public class DefaultOktaForgotPasswordResult extends AbstractInstanceResource implements OktaForgotPasswordResult { + + private static final StringProperty STATUS = new StringProperty("status"); + private static final StringProperty FACTOR_RESULT = new StringProperty("factorResult"); + private static final StringProperty FACTOR_TYPE = new StringProperty("factorType"); + private static final StringProperty RELAY_STATE = new StringProperty("relayState"); + private static final StringProperty RECOVERY_TYPE = new StringProperty("recoveryType"); + + private static final Map PROPERTY_DESCRIPTORS = createPropertyDescriptorMap(STATUS, FACTOR_TYPE, RELAY_STATE, FACTOR_RESULT, RECOVERY_TYPE); + + public DefaultOktaForgotPasswordResult(InternalDataStore dataStore) { + super(dataStore); + } + + public DefaultOktaForgotPasswordResult(InternalDataStore dataStore, Map properties) { + super(dataStore, properties); + } + + + @Override + public Map getPropertyDescriptors() { + return PROPERTY_DESCRIPTORS; + } + + @Override + public String getStatus() { + return getString(STATUS); + } + + @Override + public String getFactorResult() { + return getString(FACTOR_RESULT); + } + + @Override + public String getRelayState() { + return getString(RELAY_STATE); + } + + @Override + public String getFactorType() { + return getString(FACTOR_TYPE); + } + + @Override + public String getRecoveryType() { + return getString(RECOVERY_TYPE); + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOktaSigningKeyResolver.java b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOktaSigningKeyResolver.java new file mode 100644 index 0000000000..acda94ad3e --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOktaSigningKeyResolver.java @@ -0,0 +1,72 @@ +package com.stormpath.sdk.impl.application.okta; + +import com.stormpath.sdk.application.okta.OIDCKey; +import com.stormpath.sdk.application.okta.OIDCKeysList; +import com.stormpath.sdk.client.Client; +import com.stormpath.sdk.ds.DataStore; +import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.util.Base64; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.JwsHeader; +import io.jsonwebtoken.SigningKeyResolver; + +import java.math.BigInteger; +import java.security.Key; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.RSAPublicKeySpec; + +/** + * Resolves Signing keys via Okta's /oauth2/v1/keys endpoint. + * TODO: this could be made more generic by using the auto discovery endpoint. + */ +public class DefaultOktaSigningKeyResolver implements OktaSigningKeyResolver { + + + private final InternalDataStore dataStore; + + public DefaultOktaSigningKeyResolver(InternalDataStore dataStore) { + this.dataStore = dataStore; + } + + @Override + public Key resolveSigningKey(JwsHeader header, Claims claims) { + return getKey(header); + } + + @Override + public Key resolveSigningKey(JwsHeader header, String plaintext) { + return getKey(header); + } + + private Key getKey(JwsHeader header) { + String keyId = header.getKeyId(); + String keyAlgoritm = header.getAlgorithm(); + + if (!"RS256".equals(keyAlgoritm)) { + throw new UnsupportedOperationException("Only 'RS256' key algorithm is supported."); + } + + OIDCKeysList keyList = dataStore.getResource("/oauth2/v1/keys", OIDCKeysList.class); + OIDCKey key = keyList.getKeyById(keyId); + + try { + + BigInteger modulus = new BigInteger(1, Base64.decodeBase64(key.get("n"))); + BigInteger publicExponent = new BigInteger(1, Base64.decodeBase64(key.get("e"))); + return KeyFactory.getInstance("RSA").generatePublic( + new RSAPublicKeySpec(modulus, publicExponent)); + + } catch (NoSuchAlgorithmException e) { + throw new UnsupportedOperationException("Failed to load key Algorithm", e); + } catch (InvalidKeySpecException e) { + throw new UnsupportedOperationException("Failed to load key", e); + } + } + + @Override + public String getHref() { + return null; + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOktaTokenRequest.java b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOktaTokenRequest.java new file mode 100644 index 0000000000..9e328fbc73 --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOktaTokenRequest.java @@ -0,0 +1,87 @@ +package com.stormpath.sdk.impl.application.okta; + +import com.stormpath.sdk.application.okta.OktaTokenRequest; +import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.resource.AbstractInstanceResource; +import com.stormpath.sdk.impl.resource.Property; +import com.stormpath.sdk.impl.resource.StringProperty; + +import java.util.Map; + +/** + * + */ +public class DefaultOktaTokenRequest extends AbstractInstanceResource implements OktaTokenRequest { + + private static final StringProperty USERNAME = new StringProperty("username"); + private static final StringProperty PASSWORD = new StringProperty("password"); + private static final StringProperty REDIRECT_URI = new StringProperty("redirect_uri"); + private static final StringProperty SCOPE = new StringProperty("scope"); + private static final StringProperty GRANT_TYPE = new StringProperty("grant_type"); + + private static final Map PROPERTY_DESCRIPTORS = createPropertyDescriptorMap(USERNAME, PASSWORD, REDIRECT_URI, SCOPE, GRANT_TYPE); + + public DefaultOktaTokenRequest(InternalDataStore dataStore) { + super(dataStore); + } + + @Override + public Map getPropertyDescriptors() { + return null; + } + + @Override + public String getGrantType() { + return getString(GRANT_TYPE); + } + + @Override + public OktaTokenRequest setGrantType(String grantType) { + setProperty(GRANT_TYPE, grantType); + return this; + } + + @Override + public String getUsername() { + return getString(USERNAME); + } + + @Override + public OktaTokenRequest setUsername(String username) { + setProperty(USERNAME, username); + return this; + } + + @Override + public String getPassword() { + return getString(PASSWORD); + } + + @Override + public OktaTokenRequest setPassword(String password) { + setProperty(PASSWORD, password); + return this; + } + + @Override + public String getRedirectUri() { + return getString(REDIRECT_URI); + } + + @Override + public OktaTokenRequest setRedirectUri(String redirectUri) { + setProperty(REDIRECT_URI, redirectUri); + return this; + } + + @Override + public String getScope() { + return getString(SCOPE); + } + + @Override + public OktaTokenRequest setScope(String scope) { + setProperty(SCOPE, scope); + return this; + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOktaTokenResponse.java b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOktaTokenResponse.java new file mode 100644 index 0000000000..8f94a93b7c --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultOktaTokenResponse.java @@ -0,0 +1,79 @@ +package com.stormpath.sdk.impl.application.okta; + +import com.stormpath.sdk.application.okta.OktaTokenResponse; +import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.resource.AbstractInstanceResource; +import com.stormpath.sdk.impl.resource.IntegerProperty; +import com.stormpath.sdk.impl.resource.Property; +import com.stormpath.sdk.impl.resource.StringProperty; + +import java.util.Map; + +/** + * + */ +public class DefaultOktaTokenResponse extends AbstractInstanceResource implements OktaTokenResponse { + + private static final StringProperty ACCESS_TOKEN = new StringProperty("access_token"); + private static final StringProperty TOKEN_TYPE = new StringProperty("token_type"); + private static final StringProperty EXPIRES_IN = new StringProperty("expires_in"); + private static final StringProperty SCOPE = new StringProperty("scope"); + private static final StringProperty REFRESH_TOKEN = new StringProperty("refresh_token"); + + private static final Map PROPERTY_DESCRIPTORS = createPropertyDescriptorMap(ACCESS_TOKEN, TOKEN_TYPE, EXPIRES_IN, SCOPE, REFRESH_TOKEN); + + public DefaultOktaTokenResponse(InternalDataStore dataStore) { + super(dataStore); + } + + public DefaultOktaTokenResponse(InternalDataStore dataStore, Map properties) { + super(dataStore, properties); + } + + @Override + public Map getPropertyDescriptors() { + return PROPERTY_DESCRIPTORS; + } + + @Override + public String getAccessToken() { + return getString(ACCESS_TOKEN); + } + + @Override + public String getTokenType() { + return getString(TOKEN_TYPE); + } + + @Override + public String getExpiresIn() { + return getString(EXPIRES_IN); + } + + @Override + public String getScope() { + return getString(SCOPE); + } + + @Override + public String getRefreshToken() { + return getString(REFRESH_TOKEN); + } + + @Override + public String getIdToken() { + return null; + } + + @Override + public String toJson() { + return null; + } + + @Override + public String getApplicationHref() { + return null; + } + + +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultTokenIntrospectRequest.java b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultTokenIntrospectRequest.java new file mode 100644 index 0000000000..1683b4514e --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultTokenIntrospectRequest.java @@ -0,0 +1,52 @@ +package com.stormpath.sdk.impl.application.okta; + +import com.stormpath.sdk.application.okta.TokenIntrospectRequest; +import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.resource.AbstractInstanceResource; +import com.stormpath.sdk.impl.resource.Property; +import com.stormpath.sdk.impl.resource.StringProperty; + +import java.util.Map; + +/** + * + */ +public class DefaultTokenIntrospectRequest extends AbstractInstanceResource implements TokenIntrospectRequest { + + private static final StringProperty TOKEN = new StringProperty("token"); + private static final StringProperty TOKEN_TYPE_HINT = new StringProperty("token_type_hint"); + + private static final Map PROPERTY_DESCRIPTORS = createPropertyDescriptorMap(TOKEN, TOKEN_TYPE_HINT); + + + public DefaultTokenIntrospectRequest(InternalDataStore dataStore) { + super(dataStore); + } + + @Override + public Map getPropertyDescriptors() { + return PROPERTY_DESCRIPTORS; + } + + @Override + public String getToken() { + return getString(TOKEN); + } + + @Override + public TokenIntrospectRequest setToken(String token) { + setProperty(TOKEN, token); + return this; + } + + @Override + public String getTokenTypeHint() { + return getString(TOKEN_TYPE_HINT); + } + + @Override + public TokenIntrospectRequest setTokenTypeHint(String tokenTypeHint) { + setProperty(TOKEN_TYPE_HINT, tokenTypeHint); + return this; + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultTokenIntrospectResponse.java b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultTokenIntrospectResponse.java new file mode 100644 index 0000000000..87f2815d83 --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/DefaultTokenIntrospectResponse.java @@ -0,0 +1,103 @@ +package com.stormpath.sdk.impl.application.okta; + +import com.stormpath.sdk.application.okta.TokenIntrospectResponse; +import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.resource.AbstractInstanceResource; +import com.stormpath.sdk.impl.resource.BooleanProperty; +import com.stormpath.sdk.impl.resource.DateProperty; +import com.stormpath.sdk.impl.resource.Property; +import com.stormpath.sdk.impl.resource.StringProperty; + +import java.util.Date; +import java.util.Map; + +/** + * + */ +public class DefaultTokenIntrospectResponse extends AbstractInstanceResource implements TokenIntrospectResponse { + + private static final BooleanProperty ACTIVE = new BooleanProperty("active"); + private static final StringProperty SCOPE = new StringProperty("scope"); + private static final StringProperty USERNAME = new StringProperty("username"); + private static final StringProperty SUBJECT = new StringProperty("sub"); + private static final StringProperty AUDIENCE = new StringProperty("aud"); + private static final StringProperty ISSUER = new StringProperty("iss"); + private static final StringProperty JWT_ID = new StringProperty("jti"); + private static final StringProperty CLIENT_ID = new StringProperty("client_id"); + private static final StringProperty TOKEN_TYPE = new StringProperty("token_type"); + private static final StringProperty UID = new StringProperty("uid"); + + private static final DateProperty EXPIRES_AT = new DateProperty("exp"); + private static final DateProperty ISSUED_AT = new DateProperty("iat"); + + private static final Map PROPERTY_DESCRIPTORS = createPropertyDescriptorMap(SCOPE, USERNAME, SUBJECT, AUDIENCE, ISSUER, JWT_ID, CLIENT_ID, TOKEN_TYPE, UID, EXPIRES_AT, ISSUED_AT); + + public DefaultTokenIntrospectResponse(InternalDataStore dataStore, Map properties) { + super(dataStore, properties); + } + + @Override + public Map getPropertyDescriptors() { + return PROPERTY_DESCRIPTORS; + } + + @Override + public boolean isActive() { + return getBoolean(ACTIVE); + } + + @Override + public String getScope() { + return getString(SCOPE); + } + + @Override + public String getUsername() { + return getString(USERNAME); + } + + @Override + public Date getExpiresAt() { + return getDateProperty(EXPIRES_AT); + } + + @Override + public Date getIssuedAt() { + return getDateProperty(ISSUED_AT); + } + + @Override + public String getSubject() { + return getString(SUBJECT); + } + + @Override + public String getAudience() { + return getString(AUDIENCE); + } + + @Override + public String getIssuer() { + return getString(ISSUER); + } + + @Override + public String getJwtId() { + return getString(JWT_ID); + } + + @Override + public String getTokenType() { + return getString(TOKEN_TYPE); + } + + @Override + public String getClientId() { + return getString(CLIENT_ID); + } + + @Override + public String getUid() { + return getString(UID); + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/application/LocalApplication.java b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/OktaApplication.java similarity index 73% rename from impl/src/main/java/com/stormpath/sdk/impl/application/LocalApplication.java rename to impl/src/main/java/com/stormpath/sdk/impl/application/okta/OktaApplication.java index b5e83bb792..c94210ed58 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/application/LocalApplication.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/OktaApplication.java @@ -1,4 +1,4 @@ -package com.stormpath.sdk.impl.application; +package com.stormpath.sdk.impl.application.okta; import com.stormpath.sdk.account.Account; import com.stormpath.sdk.account.AccountCriteria; @@ -15,6 +15,9 @@ import com.stormpath.sdk.application.ApplicationAccountStoreMappingList; import com.stormpath.sdk.application.ApplicationOptions; import com.stormpath.sdk.application.ApplicationStatus; +import com.stormpath.sdk.application.OAuthApplication; +import com.stormpath.sdk.application.okta.OktaForgotPasswordRequest; +import com.stormpath.sdk.application.okta.OktaForgotPasswordResult; import com.stormpath.sdk.application.webconfig.ApplicationWebConfig; import com.stormpath.sdk.application.webconfig.ApplicationWebConfigStatus; import com.stormpath.sdk.application.webconfig.ChangePasswordConfig; @@ -36,13 +39,33 @@ import com.stormpath.sdk.group.GroupList; import com.stormpath.sdk.idsite.IdSiteCallbackHandler; import com.stormpath.sdk.idsite.IdSiteUrlBuilder; -import com.stormpath.sdk.impl.application.webconfig.DefaultApplicationWebConfig; +import com.stormpath.sdk.impl.application.DefaultApplicationAccountStoreMapping; +import com.stormpath.sdk.impl.application.DefaultApplicationAccountStoreMappingList; +import com.stormpath.sdk.impl.authc.DefaultUsernamePasswordRequest; +import com.stormpath.sdk.impl.authc.DefaultOktaAuthNAuthenticator; import com.stormpath.sdk.impl.directory.OktaDirectory; import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.oauth.DefaultIdSiteAuthenticator; +import com.stormpath.sdk.impl.oauth.DefaultOAuthBearerRequestAuthenticator; +import com.stormpath.sdk.impl.oauth.DefaultOAuthClientCredentialsGrantRequestAuthenticator; +import com.stormpath.sdk.impl.oauth.DefaultOAuthPasswordGrantRequestAuthenticator; +import com.stormpath.sdk.impl.oauth.DefaultOAuthRefreshTokenRequestAuthenticator; +import com.stormpath.sdk.impl.oauth.DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator; +import com.stormpath.sdk.impl.oauth.DefaultOAuthStormpathSocialGrantRequestAuthenticator; +import com.stormpath.sdk.impl.oauth.DefaultOAuthTokenRevocator; +import com.stormpath.sdk.impl.okta.OktaApiPaths; import com.stormpath.sdk.impl.resource.AbstractCollectionResource; import com.stormpath.sdk.impl.resource.AbstractResource; import com.stormpath.sdk.impl.resource.Property; +import com.stormpath.sdk.oauth.IdSiteAuthenticator; +import com.stormpath.sdk.oauth.OAuthBearerRequestAuthenticator; +import com.stormpath.sdk.oauth.OAuthClientCredentialsGrantRequestAuthenticator; +import com.stormpath.sdk.oauth.OAuthPasswordGrantRequestAuthenticator; import com.stormpath.sdk.oauth.OAuthPolicy; +import com.stormpath.sdk.oauth.OAuthRefreshTokenRequestAuthenticator; +import com.stormpath.sdk.oauth.OAuthStormpathFactorChallengeGrantRequestAuthenticator; +import com.stormpath.sdk.oauth.OAuthStormpathSocialGrantRequestAuthenticator; +import com.stormpath.sdk.oauth.OAuthTokenRevocator; import com.stormpath.sdk.organization.OrganizationCriteria; import com.stormpath.sdk.provider.ProviderAccountRequest; import com.stormpath.sdk.provider.ProviderAccountResult; @@ -52,24 +75,27 @@ import com.stormpath.sdk.saml.SamlPolicy; import com.stormpath.sdk.tenant.Tenant; +import java.util.Collection; +import java.util.Collections; import java.util.Date; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -public class LocalApplication extends AbstractResource implements Application { +public class OktaApplication extends AbstractResource implements Application, OAuthApplication { private final Directory OKTA_TENANT_DIR; - private final ApplicationAccountStoreMappingList applicationAccountStoreMappingList; + private final ApplicationAccountStoreMappingCollectionBackedList applicationAccountStoreMappingList; private String name; - public LocalApplication(InternalDataStore dataStore) { + public OktaApplication(InternalDataStore dataStore) { this(dataStore, new LinkedHashMap()); } - public LocalApplication(InternalDataStore dataStore, Map properties) { + public OktaApplication(InternalDataStore dataStore, Map properties) { super(dataStore, properties); this.OKTA_TENANT_DIR = new OktaDirectory(dataStore); @@ -78,9 +104,14 @@ public LocalApplication(InternalDataStore dataStore, Map propert mappingProperties.put("application", this); mappingProperties.put("accountStore", OKTA_TENANT_DIR); - ApplicationAccountStoreMapping mapping = new DefaultApplicationAccountStoreMapping(dataStore, mappingProperties); - - applicationAccountStoreMappingList = AbstractCollectionResource.singletonCollectionResource(dataStore, DefaultApplicationAccountStoreMappingList.class, mapping); + ApplicationAccountStoreMapping mapping = new DefaultApplicationAccountStoreMapping(dataStore, mappingProperties) { + @Override + public AccountStore getAccountStore() { + return OKTA_TENANT_DIR; + } + }; + mapping.setAccountStore(OKTA_TENANT_DIR); + applicationAccountStoreMappingList = new ApplicationAccountStoreMappingCollectionBackedList(Collections.singletonList(mapping)); } @Override @@ -211,7 +242,16 @@ public Tenant getTenant() { @Override public PasswordResetToken sendPasswordResetEmail(String email) throws ResourceException { - throw new UnsupportedOperationException("sendPasswordResetEmail() method hasn't been implemented."); + + OktaForgotPasswordRequest request = getDataStore().instantiate(OktaForgotPasswordRequest.class); + request.setUsername(email); + request.setFactorType("EMAIL"); + request.setRelayState("/"); + + OktaForgotPasswordResult result = getDataStore().create(OktaApiPaths.PASSWORD_RECOVERY, request, OktaForgotPasswordResult.class); + + return null; + } @Override @@ -231,7 +271,8 @@ public Account resetPassword(String passwordResetToken, String newPassword) { @Override public AuthenticationResult authenticateAccount(AuthenticationRequest request) throws ResourceException { - throw new UnsupportedOperationException("authenticateAccount() method hasn't been implemented."); + + return new DefaultOktaAuthNAuthenticator(getDataStore()).authenticate((DefaultUsernamePasswordRequest)request); } @Override @@ -239,28 +280,19 @@ public ProviderAccountResult getAccount(ProviderAccountRequest request) { throw new UnsupportedOperationException("getAccount() method hasn't been implemented."); } - /** - * @since 0.9 - */ @Override public ApplicationAccountStoreMappingList getAccountStoreMappings() { return applicationAccountStoreMappingList; } - /** - * @since 0.9 - */ @Override public ApplicationAccountStoreMappingList getAccountStoreMappings(Map queryParams) { - return applicationAccountStoreMappingList; + return getAccountStoreMappings(); } - /** - * @since 1.0.RC9 - */ @Override public ApplicationAccountStoreMappingList getAccountStoreMappings(ApplicationAccountStoreMappingCriteria criteria) { - return applicationAccountStoreMappingList; + return getAccountStoreMappings(); } @@ -346,7 +378,6 @@ public SamlPolicy getSamlPolicy() { @Override public ApplicationWebConfig getWebConfig() { -// return null; return new ApplicationWebConfig() { @Override @@ -371,7 +402,7 @@ public Date getModifiedAt() { @Override public String getDomainName() { - return "hack"; + return "OktaDirectory-DomainName-Not-Used"; } @Override @@ -514,4 +545,92 @@ public AccountStore getDefaultGroupStore() { public void setDefaultGroupStore(AccountStore accountStore) { throw new UnsupportedOperationException("setDefaultGroupStore() method hasn't been implemented."); } + + + @Override + public OAuthClientCredentialsGrantRequestAuthenticator createClientCredentialsGrantAuthenticator() { + return new DefaultOAuthClientCredentialsGrantRequestAuthenticator(this, getDataStore(), "/oauth2/v1/token"); + } + + @Override + public OAuthStormpathSocialGrantRequestAuthenticator createStormpathSocialGrantAuthenticator() { + return new DefaultOAuthStormpathSocialGrantRequestAuthenticator(this, getDataStore(), "/oauth2/v1/token"); + } + + @Override + public OAuthStormpathFactorChallengeGrantRequestAuthenticator createStormpathFactorChallengeGrantAuthenticator() { + return new DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator(this, getDataStore(), "/oauth2/v1/token"); + } + + @Override + public OAuthPasswordGrantRequestAuthenticator createPasswordGrantAuthenticator() { + return new DefaultOAuthPasswordGrantRequestAuthenticator(this, getDataStore(), "/oauth2/v1/token"); + } + + @Override + public OAuthRefreshTokenRequestAuthenticator createRefreshGrantAuthenticator() { + return new DefaultOAuthRefreshTokenRequestAuthenticator(this, getDataStore(), "/oauth2/v1/token"); + } + + @Override + public OAuthBearerRequestAuthenticator createJwtAuthenticator() { + return new DefaultOAuthBearerRequestAuthenticator(this, getDataStore()); + } + + public IdSiteAuthenticator createIdSiteAuthenticator() { + return new DefaultIdSiteAuthenticator(this, getDataStore(), "/oauth2/v1/token"); + } + + @Override + public OAuthTokenRevocator createOAuhtTokenRevocator() { + return new DefaultOAuthTokenRevocator(this, getDataStore(), "/oauth2/v1/token"); + } + + + public static class ApplicationAccountStoreMappingCollectionBackedList implements ApplicationAccountStoreMappingList { + + final private Collection accountStoreMappings; + + public ApplicationAccountStoreMappingCollectionBackedList(Collection accountStoreMappings) { + this.accountStoreMappings = accountStoreMappings; + } + + @Override + public String getHref() { + return null; + } + + @Override + public int getOffset() { + return 0; + } + + @Override + public int getLimit() { + return getSize(); + } + + @Override + public int getSize() { + return accountStoreMappings.size(); + } + + @Override + public ApplicationAccountStoreMapping single() { + Iterator iterator = iterator(); + if (!iterator.hasNext()) { + throw new IllegalStateException("This list is empty while it was expected to contain one (and only one) element."); + } + ApplicationAccountStoreMapping itemToReturn = iterator.next(); + if (iterator.hasNext()) { + throw new IllegalStateException("Only a single resource was expected, but this list contains more than one item."); + } + return itemToReturn; + } + + @Override + public Iterator iterator() { + return accountStoreMappings.iterator(); + } + } } diff --git a/impl/src/main/java/com/stormpath/sdk/impl/application/okta/OktaSigningKeyResolver.java b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/OktaSigningKeyResolver.java new file mode 100644 index 0000000000..703eb6ba96 --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/application/okta/OktaSigningKeyResolver.java @@ -0,0 +1,10 @@ +package com.stormpath.sdk.impl.application.okta; + +import com.stormpath.sdk.resource.Resource; +import io.jsonwebtoken.SigningKeyResolver; + +/** + * + */ +public interface OktaSigningKeyResolver extends SigningKeyResolver, Resource { +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/authc/DefaultOktaAuthNAuthenticator.java b/impl/src/main/java/com/stormpath/sdk/impl/authc/DefaultOktaAuthNAuthenticator.java new file mode 100644 index 0000000000..3ec6023e9b --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/authc/DefaultOktaAuthNAuthenticator.java @@ -0,0 +1,172 @@ +package com.stormpath.sdk.impl.authc; + +import com.stormpath.sdk.account.Account; +import com.stormpath.sdk.api.ApiKey; +import com.stormpath.sdk.application.okta.OktaTokenResponse; +import com.stormpath.sdk.application.okta.OktaTokenRequest; +import com.stormpath.sdk.authc.AuthenticationRequest; +import com.stormpath.sdk.authc.AuthenticationResult; +import com.stormpath.sdk.authc.AuthenticationResultVisitor; +import com.stormpath.sdk.authc.OktaAuthNAuthenticator; +import com.stormpath.sdk.error.Error; +import com.stormpath.sdk.impl.application.okta.OktaSigningKeyResolver; +import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.http.HttpHeaders; +import com.stormpath.sdk.impl.http.MediaType; +import com.stormpath.sdk.lang.Assert; +import com.stormpath.sdk.oauth.AccessTokenResult; +import com.stormpath.sdk.resource.ResourceException; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Header; +import io.jsonwebtoken.Jwt; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SigningKeyResolver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +/** + * Uses Okta's /api/v1/authn endpoint to authenticate users. + */ +public class DefaultOktaAuthNAuthenticator implements OktaAuthNAuthenticator { + + private static final Logger log = LoggerFactory.getLogger(DefaultOktaAuthNAuthenticator.class); + + private final InternalDataStore dataStore; + + public DefaultOktaAuthNAuthenticator(InternalDataStore dataStore) { + Assert.notNull(dataStore); + this.dataStore = dataStore; + } + + @Override + public AuthenticationResult authenticate(AuthenticationRequest request) { + + Assert.isInstanceOf(DefaultUsernamePasswordRequest.class, request, "Only 'DefaultUsernamePasswordRequest' requests are supported."); + DefaultUsernamePasswordRequest usernamePasswordRequest = (DefaultUsernamePasswordRequest) request; + + final OktaTokenResponse oktaTokenResponse = doAuthRequest(usernamePasswordRequest); + + // validate the key we just received + final Jwt jwt = parseJwt(oktaTokenResponse.getAccessToken()); + String userId = jwt.getBody().get("uid", String.class); + + final String userHref = "/api/v1/users/" + userId; + + Map authMap = new LinkedHashMap<>(); + Map accountMap = new LinkedHashMap<>(); + authMap.put("account", accountMap); + accountMap.put("href", userHref); + + final Set scopes = new HashSet<>(jwt.getBody().get("scp", ArrayList.class)); + + return new AccessTokenResult() { + @Override + public com.stormpath.sdk.oauth.TokenResponse getTokenResponse() { + return oktaTokenResponse; + } + + @Override + public Set getScope() { + return scopes; + } + + @Override + public ApiKey getApiKey() { + return null; + } + + @Override + public Account getAccount() { + return dataStore.getResource(userHref, Account.class); + } + + @Override + public void accept(AuthenticationResultVisitor visitor) { + visitor.visit(this); + } + + @Override + public String getHref() { + return null; + } + }; + } + + @Override + public void assertValidAccessToken(String accessToken) { + parseJwt(accessToken); + } + + private Jwt parseJwt(String accessToken) { + + SigningKeyResolver keyResolver = dataStore.instantiate(OktaSigningKeyResolver.class); + return Jwts.parser() + .setSigningKeyResolver(keyResolver) + .parse(accessToken); + } + + private OktaTokenResponse doAuthRequest(DefaultUsernamePasswordRequest request) { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + // hit the oauth token endpoint + OktaTokenRequest tokenRequest = this.dataStore.instantiate(OktaTokenRequest.class) + .setPassword(new String(request.getCredentials())) + .setUsername(request.getPrincipals()) + .setGrantType("password") + .setScope("offline_access"); + + try { + + return this.dataStore.create("/oauth2/v1/token", tokenRequest, OktaTokenResponse.class, httpHeaders); + } + catch (final ResourceException e) { + + log.debug("Exception thrown while requesting token, assuming this is an Invalid username or password", e); + + throw new ResourceException(new Error() { + @Override + public int getStatus() { + return e.getStatus(); + } + + @Override + public int getCode() { + return 0; + } + + @Override + public String getMessage() { + // TODO: i18n, configure error handler for this type of message? + return "Invalid username or password."; + } + + @Override + public String getDeveloperMessage() { + return e.getDeveloperMessage(); + } + + @Override + public String getMoreInfo() { + return e.getMoreInfo(); + } + + @Override + public String getRequestId() { + return e.getRequestId(); + } + }); + } + } + + @Override + public String getHref() { + return null; + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/client/DefaultClientBuilder.java b/impl/src/main/java/com/stormpath/sdk/impl/client/DefaultClientBuilder.java index a56b9719ca..b8c6e19a1a 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/client/DefaultClientBuilder.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/client/DefaultClientBuilder.java @@ -16,6 +16,10 @@ package com.stormpath.sdk.impl.client; import com.stormpath.sdk.api.ApiKey; +import com.stormpath.sdk.api.ApiKeys; +import com.stormpath.sdk.application.okta.ApplicationCredentials; +import com.stormpath.sdk.client.DefaultPairedApiKey; +import com.stormpath.sdk.client.PairedApiKey; import com.stormpath.sdk.impl.api.ApiKeyResolver; import com.stormpath.sdk.impl.api.DefaultApiKeyResolver; import com.stormpath.sdk.impl.authc.credentials.ClientCredentials; @@ -87,6 +91,8 @@ public class DefaultClientBuilder implements ClientBuilder { USER_HOME + STORMPATH_PROPERTIES }; + final private boolean oktaEnabled; + private ClientConfiguration clientConfig = new ClientConfiguration(); public DefaultClientBuilder() { @@ -197,6 +203,8 @@ public DefaultClientBuilder() { if (props.get(DEFAULT_CLIENT_PROXY_PASSWORD_PROPERTY_NAME) != null) { clientConfig.setProxyPassword(props.get(DEFAULT_CLIENT_PROXY_PASSWORD_PROPERTY_NAME)); } + + this.oktaEnabled = Boolean.valueOf(props.get(DEFAULT_OKTA_ENABLED_PROPERTY_NAME)); } @Override @@ -303,25 +311,35 @@ public Client build() { this.clientConfig.getProxyUsername(), this.clientConfig.getProxyPassword()); } - ClientCredentials clientCredentials; + ApiKeyResolver apiKeyResolver = this.clientConfig.getApiKeyResolver(); - if (this.clientCredentials != null) { - clientCredentials = this.clientCredentials; - } else if (this.apiKey != null) { - clientCredentials = new ApiKeyCredentials(this.apiKey); - } else { - ClientCredentialsProvider clientCredentialsProvider = new DefaultClientCredentialsProviderChain(clientConfig); - clientCredentials = clientCredentialsProvider.getClientCredentials(); + if (oktaEnabled) { + ApiKey apiKey = new DefaultPairedApiKey(this.apiKey); + clientCredentials = new ApiKeyCredentials(apiKey); + if (apiKeyResolver == null) { + apiKeyResolver = new DefaultApiKeyResolver(apiKey); + } } + else { + ClientCredentials clientCredentials; + + if (this.clientCredentials != null) { + clientCredentials = this.clientCredentials; + } else if (this.apiKey != null) { + clientCredentials = new ApiKeyCredentials(this.apiKey); + } else { + ClientCredentialsProvider clientCredentialsProvider = new DefaultClientCredentialsProviderChain(clientConfig); + clientCredentials = clientCredentialsProvider.getClientCredentials(); + } - ApiKeyResolver apiKeyResolver = this.clientConfig.getApiKeyResolver(); + this.clientCredentials = clientCredentials; - if (apiKeyResolver == null) { - Assert.isInstanceOf(ApiKeyCredentials.class, clientCredentials, "An ApiKeyResolver must be configured for ClientCredentials other than ApiKeyCredentials."); - apiKeyResolver = new DefaultApiKeyResolver(((ApiKeyCredentials) clientCredentials).getApiKey()); + if (apiKeyResolver == null) { + Assert.isInstanceOf(ApiKeyCredentials.class, clientCredentials, "An ApiKeyResolver must be configured for ClientCredentials other than ApiKeyCredentials."); + apiKeyResolver = new DefaultApiKeyResolver(((ApiKeyCredentials) clientCredentials).getApiKey()); + } } - - BaseUrlResolver baseUrlResolver = this.clientConfig.getBaseUrlResolver(); + BaseUrlResolver baseUrlResolver = this.clientConfig.getBaseUrlResolver(); if (baseUrlResolver == null) { Assert.notNull(this.clientConfig.getBaseUrl(), "Stormpath base url must not be null."); diff --git a/impl/src/main/java/com/stormpath/sdk/impl/directory/DefaultOktaPasswordPolicy.java b/impl/src/main/java/com/stormpath/sdk/impl/directory/DefaultOktaPasswordPolicy.java new file mode 100644 index 0000000000..d9580af526 --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/directory/DefaultOktaPasswordPolicy.java @@ -0,0 +1,117 @@ +package com.stormpath.sdk.impl.directory; + +import com.stormpath.sdk.directory.OktaPasswordPolicy; +import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.resource.AbstractInstanceResource; +import com.stormpath.sdk.impl.resource.BooleanProperty; +import com.stormpath.sdk.impl.resource.DateProperty; +import com.stormpath.sdk.impl.resource.IntegerProperty; +import com.stormpath.sdk.impl.resource.MapProperty; +import com.stormpath.sdk.impl.resource.Property; +import com.stormpath.sdk.impl.resource.StringProperty; + +import java.util.Date; +import java.util.Map; + +public class DefaultOktaPasswordPolicy extends AbstractInstanceResource implements OktaPasswordPolicy { + + // SIMPLE PROPERTIES + static final StringProperty TYPE = new StringProperty("type"); + static final StringProperty ID = new StringProperty("id"); + static final StringProperty STATUS = new StringProperty("status"); + static final StringProperty NAME = new StringProperty("name"); + static final StringProperty DESCRIPTION = new StringProperty("description"); + static final IntegerProperty PRIORITY = new IntegerProperty("priority"); + static final BooleanProperty SYSTEM = new BooleanProperty("system"); + static final DateProperty CREATED = new DateProperty("created"); + static final DateProperty LAST_UPDATED = new DateProperty("lastUpdated"); + + // MAP Properties + static final MapProperty CONDITIONS = new MapProperty("conditions"); + static final MapProperty SETTINGS = new MapProperty("settings"); + static final MapProperty DELEGATION = new MapProperty("delegation"); + static final MapProperty RULES = new MapProperty("rules"); + + private static final Map PROPERTY_DESCRIPTORS = createPropertyDescriptorMap( + TYPE, ID, STATUS, NAME, DESCRIPTION, PRIORITY, SYSTEM, CREATED, LAST_UPDATED, CONDITIONS, SETTINGS, DELEGATION, RULES + ); + + public DefaultOktaPasswordPolicy(InternalDataStore dataStore) { + super(dataStore); + } + + public DefaultOktaPasswordPolicy(InternalDataStore dataStore, Map properties) { + super(dataStore, properties); + } + + @Override + public Map getPropertyDescriptors() { + return PROPERTY_DESCRIPTORS; + } + + + @Override + public String getType() { + return getString(TYPE); + } + + @Override + public String getId() { + return getString(ID); + } + + @Override + public String getStatus() { + return getString(STATUS); + } + + @Override + public String getName() { + return getString(NAME); + } + + @Override + public String getDescription() { + return getString(DESCRIPTION); + } + + @Override + public int getPriority() { + return getInt(PRIORITY); + } + + @Override + public boolean getSystem() { + return getBoolean(SYSTEM); + } + + @Override + public Map getConditions() { + return getMap(CONDITIONS); + } + + @Override + public Date getCreated() { + return getDateProperty(CREATED); + } + + @Override + public Date getLastUpdated() { + return getDateProperty(LAST_UPDATED); + } + + @Override + public Map getSettings() { + return getMap(SETTINGS); + } + + @Override + public Map getDelegation() { + return getMap(DELEGATION); + } + + @Override + public Map getRules() { + return getMap(RULES); + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/directory/DefaultOktaPasswordPolicyList.java b/impl/src/main/java/com/stormpath/sdk/impl/directory/DefaultOktaPasswordPolicyList.java new file mode 100644 index 0000000000..a0f18e639c --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/directory/DefaultOktaPasswordPolicyList.java @@ -0,0 +1,39 @@ +package com.stormpath.sdk.impl.directory; + +import com.stormpath.sdk.directory.OktaPasswordPolicy; +import com.stormpath.sdk.directory.OktaPasswordPolicyList; +import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.resource.AbstractCollectionResource; +import com.stormpath.sdk.impl.resource.ArrayProperty; +import com.stormpath.sdk.impl.resource.Property; + +import java.util.Map; + +public class DefaultOktaPasswordPolicyList extends AbstractCollectionResource implements OktaPasswordPolicyList { + + private static final ArrayProperty ITEMS = new ArrayProperty<>("items", OktaPasswordPolicy.class); + + private static final Map PROPERTY_DESCRIPTORS = createPropertyDescriptorMap(OFFSET, LIMIT, ITEMS); + + public DefaultOktaPasswordPolicyList(InternalDataStore dataStore) { + super(dataStore); + } + + public DefaultOktaPasswordPolicyList(InternalDataStore dataStore, Map properties) { + super(dataStore, properties); + } + + public DefaultOktaPasswordPolicyList(InternalDataStore dataStore, Map properties, Map queryParams) { + super(dataStore, properties, queryParams); + } + + @Override + public Map getPropertyDescriptors() { + return PROPERTY_DESCRIPTORS; + } + + @Override + protected Class getItemType() { + return OktaPasswordPolicy.class; + } +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/directory/OktaDirectory.java b/impl/src/main/java/com/stormpath/sdk/impl/directory/OktaDirectory.java index 4f171cf81b..4d864fd7b2 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/directory/OktaDirectory.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/directory/OktaDirectory.java @@ -11,16 +11,23 @@ import com.stormpath.sdk.directory.Directory; import com.stormpath.sdk.directory.DirectoryOptions; import com.stormpath.sdk.directory.DirectoryStatus; +import com.stormpath.sdk.directory.OktaPasswordPolicy; import com.stormpath.sdk.directory.PasswordPolicy; +import com.stormpath.sdk.directory.OktaPasswordPolicyList; +import com.stormpath.sdk.directory.PasswordStrength; import com.stormpath.sdk.group.CreateGroupRequest; import com.stormpath.sdk.group.Group; import com.stormpath.sdk.group.GroupCriteria; import com.stormpath.sdk.group.GroupList; import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.okta.OktaApiPaths; import com.stormpath.sdk.impl.provider.OktaProvider; import com.stormpath.sdk.impl.resource.AbstractResource; import com.stormpath.sdk.impl.resource.Property; import com.stormpath.sdk.lang.Assert; +import com.stormpath.sdk.mail.EmailStatus; +import com.stormpath.sdk.mail.ModeledEmailTemplateList; +import com.stormpath.sdk.mail.UnmodeledEmailTemplateList; import com.stormpath.sdk.organization.OrganizationAccountStoreMappingCriteria; import com.stormpath.sdk.organization.OrganizationAccountStoreMappingList; import com.stormpath.sdk.organization.OrganizationCriteria; @@ -130,7 +137,7 @@ public Account createAccount(Account account, boolean registrationWorkflowEnable @Override public Account createAccount(CreateAccountRequest request) { - String usersHref = getHref() + "/users"; + String usersHref = getHref() + OktaApiPaths.USERS; final Account account = request.getAccount(); return getDataStore().create(usersHref, account); } @@ -187,7 +194,10 @@ public Provider getProvider() { @Override public PasswordPolicy getPasswordPolicy() { - throw new UnsupportedOperationException("Not implemented."); + String passwordPolicyHref = getHref() + OktaApiPaths.API_V1 + "policies?type=PASSWORD"; + OktaPasswordPolicyList policies = getDataStore().getResource(passwordPolicyHref, OktaPasswordPolicyList.class); + OktaPasswordPolicy oktaPasswordPolicy = policies.single(); + return transformOktaPasswordPolicy(oktaPasswordPolicy); } @Override @@ -234,4 +244,79 @@ public OrganizationAccountStoreMappingList getOrganizationAccountStoreMappings(O public Schema getAccountSchema() { throw new UnsupportedOperationException("Not implemented."); } + + @SuppressWarnings("unchecked") + private PasswordPolicy transformOktaPasswordPolicy(OktaPasswordPolicy oktaPasswordPolicy) { + // ref: http://developer.okta.com/docs/api/resources/policy.html#GroupPasswordPolicy + final Map strengthMap = (Map) + ((Map)oktaPasswordPolicy.getSettings().get("password")).get("complexity"); + PasswordPolicy ret = new PasswordPolicy() { + @Override + public int getResetTokenTtlHours() { + return 0; + } + + @Override + public PasswordPolicy setResetTokenTtlHours(int resetTokenTtl) { + return null; + } + + @Override + public EmailStatus getResetEmailStatus() { + return null; + } + + @Override + public PasswordPolicy setResetEmailStatus(EmailStatus status) { + return null; + } + + @Override + public EmailStatus getResetSuccessEmailStatus() { + return null; + } + + @Override + public PasswordPolicy setResetSuccessEmailStatus(EmailStatus status) { + return null; + } + + @Override + public PasswordStrength getStrength() { + PasswordStrength p = new DefaultPasswordStrength(getDataStore()); + + p.setMinLength((Integer) strengthMap.get("minLength")); + p.setMinLowerCase((Integer) strengthMap.get("minLowerCase")); + p.setMinUpperCase((Integer) strengthMap.get("minUpperCase")); + p.setMinNumeric((Integer) strengthMap.get("minNumber")); + p.setMinSymbol((Integer) strengthMap.get("minSymbol")); + p.setMaxLength(1024); + p.setMinDiacritic(0); + p.setPreventReuse(0); + + return p; + } + + @Override + public ModeledEmailTemplateList getResetEmailTemplates() { + return null; + } + + @Override + public UnmodeledEmailTemplateList getResetSuccessEmailTemplates() { + return null; + } + + @Override + public String getHref() { + return "local"; + } + + @Override + public void save() { + + } + }; + return ret; + } } diff --git a/impl/src/main/java/com/stormpath/sdk/impl/ds/DefaultDataStore.java b/impl/src/main/java/com/stormpath/sdk/impl/ds/DefaultDataStore.java index 771616ebab..e3621c5caf 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/ds/DefaultDataStore.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/ds/DefaultDataStore.java @@ -20,7 +20,7 @@ import com.stormpath.sdk.cache.CacheManager; import com.stormpath.sdk.http.HttpMethod; import com.stormpath.sdk.impl.api.ApiKeyResolver; -import com.stormpath.sdk.impl.application.LocalApplication; +import com.stormpath.sdk.impl.application.okta.OktaApplication; import com.stormpath.sdk.impl.authc.credentials.ApiKeyCredentials; import com.stormpath.sdk.impl.authc.credentials.ClientCredentials; import com.stormpath.sdk.impl.cache.DisabledCacheManager; @@ -31,6 +31,7 @@ import com.stormpath.sdk.impl.ds.cache.ReadCacheFilter; import com.stormpath.sdk.impl.ds.cache.WriteCacheFilter; import com.stormpath.sdk.impl.error.DefaultError; +import com.stormpath.sdk.impl.error.OktaError; import com.stormpath.sdk.impl.http.CanonicalUri; import com.stormpath.sdk.impl.http.HttpHeaders; import com.stormpath.sdk.impl.http.HttpHeadersHolder; @@ -105,6 +106,8 @@ public class DefaultDataStore implements InternalDataStore { private static final boolean COLLECTION_CACHING_ENABLED = false; //EXPERIMENTAL - set to true only while developing. + private static boolean oktaEnabled; + private final RequestExecutor requestExecutor; private final ResourceFactory resourceFactory; private final MapMarshaller mapMarshaller; @@ -160,6 +163,12 @@ public DefaultDataStore(RequestExecutor requestExecutor, BaseUrlResolver baseUrl this.cacheResolver = new DefaultCacheResolver(this.cacheManager, new DefaultCacheRegionNameResolver()); this.apiKeyResolver = apiKeyResolver; + if (baseUrlResolver.getBaseUrl().toLowerCase().contains("okta")) { + oktaEnabled = true; + } else { + oktaEnabled = false; + } + ReferenceFactory referenceFactory = new ReferenceFactory(); this.resourceConverter = new DefaultResourceConverter(referenceFactory); @@ -241,7 +250,7 @@ public T instantiate(Class clazz, Map pr public T getResource(String href, Class clazz) { if ("local".equalsIgnoreCase(href) && clazz.isAssignableFrom(Application.class)) { - return (T) new LocalApplication(this); + return (T) new OktaApplication(this); } return getResource(href, clazz, (Map) null); @@ -600,7 +609,15 @@ private Response execute(Request request) throws ResourceException { body.put(DefaultError.REQUEST_ID.getName(), requestId); } - DefaultError error = new DefaultError(body); + com.stormpath.sdk.error.Error error; + if (oktaEnabled) { + OktaError oktaError = new OktaError(body); + // Okta Error response doesn't have status + oktaError.setProperty(OktaError.STATUS.getName(), response.getHttpStatus()); + error = oktaError; + } else { + error = new DefaultError(body); + } throw new ResourceException(error); } diff --git a/impl/src/main/java/com/stormpath/sdk/impl/ds/DefaultResourceConverter.java b/impl/src/main/java/com/stormpath/sdk/impl/ds/DefaultResourceConverter.java index 3506c13cc0..0d479087c9 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/ds/DefaultResourceConverter.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/ds/DefaultResourceConverter.java @@ -74,7 +74,7 @@ private LinkedHashMap toMap(final AbstractResource resource, boo // Convert Stormpath Account to Okta User map if (resource instanceof Account) { - props = new LinkedHashMap<>(new OktaUserAccountConverter().toUser(props)); + props = new LinkedHashMap<>(OktaUserAccountConverter.toUser(props)); } return props; diff --git a/impl/src/main/java/com/stormpath/sdk/impl/ds/JacksonMapMarshaller.java b/impl/src/main/java/com/stormpath/sdk/impl/ds/JacksonMapMarshaller.java index d7e026c0d7..3f5da23aae 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/ds/JacksonMapMarshaller.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/ds/JacksonMapMarshaller.java @@ -20,9 +20,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; /** @@ -74,11 +76,25 @@ public Map unmarshal(String marshalled) { } } + @SuppressWarnings("unchecked") @Override public Map unmarshall(InputStream marshalled) { try { - TypeReference> typeRef = new TypeReference>(){}; - return this.objectMapper.readValue(marshalled, typeRef); + Object resolvedObj = this.objectMapper.readValue(marshalled, Object.class); + if (resolvedObj instanceof Map) { + return (Map) resolvedObj; + } else if (resolvedObj instanceof List) { + List list = (List) resolvedObj; + Map ret = new LinkedHashMap<>(); + ret.put("items", list); + ret.put("offset", 0); + ret.put("limit", 100); + ret.put("size", list.size()); + ret.put("href", "local"); + return ret; + } + throw new MarshalingException("Unable to convert InputStream String to Map. " + + "Resolved Object is neither a Map or a List: " + resolvedObj.getClass()); } catch (IOException e) { throw new MarshalingException("Unable to convert InputStream String to Map.", e); } diff --git a/impl/src/main/java/com/stormpath/sdk/impl/error/OktaError.java b/impl/src/main/java/com/stormpath/sdk/impl/error/OktaError.java new file mode 100644 index 0000000000..0172b70cb5 --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/error/OktaError.java @@ -0,0 +1,97 @@ +/* + * Copyright 2013 Stormpath, 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.stormpath.sdk.impl.error; + +import com.stormpath.sdk.error.Error; +import com.stormpath.sdk.impl.resource.AbstractResource; +import com.stormpath.sdk.impl.resource.IntegerProperty; +import com.stormpath.sdk.impl.resource.ListProperty; +import com.stormpath.sdk.impl.resource.Property; +import com.stormpath.sdk.impl.resource.StringProperty; +import com.stormpath.sdk.lang.Collections; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +/** + * @since 0.1 + */ +public class OktaError extends AbstractResource implements Error, Serializable { + + static final long serialVersionUID = 42L; + + public static final IntegerProperty STATUS = new IntegerProperty("status"); + static final StringProperty ERROR_CODE = new StringProperty("errorCode"); + static final StringProperty ERROR_SUMMARY = new StringProperty("errorSummary"); + static final ListProperty ERROR_CAUSES = new ListProperty("errorCauses"); + static final StringProperty ERROR_ID = new StringProperty("errorId"); + static final StringProperty ERROR = new StringProperty("error"); + + private static final Map PROPERTY_DESCRIPTORS = createPropertyDescriptorMap( + STATUS, ERROR_CODE, ERROR_SUMMARY, ERROR_CAUSES, ERROR_ID, ERROR + ); + + public OktaError(Map body) { + super(null, body); + } + + // Needed for this class to be serializable + public OktaError() { + super(null, null); + } + + @Override + public Map getPropertyDescriptors() { + return PROPERTY_DESCRIPTORS; + } + + @Override + public int getStatus() { + return getInt(STATUS); + } + + @Override + public int getCode() { + return 0; + } + + @Override + @SuppressWarnings("unchecked") + public String getMessage() { + return getString(ERROR); + } + + @Override + public String getDeveloperMessage() { + return getString(ERROR); + } + + @Override + public String getMoreInfo() { + List causes = getListProperty(ERROR_CAUSES.getName()); + if (!Collections.isEmpty(causes)) { + return ((Map) causes.get(0)).get("errorSummary"); + } + return getDeveloperMessage(); + } + + @Override + public String getRequestId() { + return getString(ERROR_ID); + } + +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/group/DefaultGroup.java b/impl/src/main/java/com/stormpath/sdk/impl/group/DefaultGroup.java index ff3a45c9f8..b97709873f 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/group/DefaultGroup.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/group/DefaultGroup.java @@ -23,6 +23,7 @@ import com.stormpath.sdk.directory.Directory; import com.stormpath.sdk.group.*; import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.okta.OktaUserAccountConverter; import com.stormpath.sdk.impl.resource.AbstractExtendableInstanceResource; import com.stormpath.sdk.impl.resource.CollectionReference; import com.stormpath.sdk.impl.resource.Property; @@ -64,7 +65,7 @@ public DefaultGroup(InternalDataStore dataStore) { } public DefaultGroup(InternalDataStore dataStore, Map properties) { - super(dataStore, properties); + super(dataStore, OktaUserAccountConverter.toStormpathGroup(properties)); } @Override diff --git a/impl/src/main/java/com/stormpath/sdk/impl/http/authc/SswsAuthenticator.java b/impl/src/main/java/com/stormpath/sdk/impl/http/authc/SswsAuthenticator.java index 4348f7e51d..e85082e021 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/http/authc/SswsAuthenticator.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/http/authc/SswsAuthenticator.java @@ -42,7 +42,7 @@ public void authenticate(Request request) throws RequestAuthenticationException String base64 = Base64.encodeBase64String(basicCreds.getBytes(UTF8)); - request.getHeaders().set(AUTHORIZATION_HEADER, "BASIC " + base64); + request.getHeaders().set(AUTHORIZATION_HEADER, "Basic " + base64); return; } diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultIdSiteAuthenticator.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultIdSiteAuthenticator.java index 8af1bcc2fb..d7e59cf256 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultIdSiteAuthenticator.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultIdSiteAuthenticator.java @@ -33,8 +33,15 @@ public class DefaultIdSiteAuthenticator extends AbstractOAuthRequestAuthenticato final static String OAUTH_TOKEN_PATH = "/oauth/token"; + private final String oauthTokenPath; + public DefaultIdSiteAuthenticator(Application application, DataStore dataStore){ + this(application, dataStore, OAUTH_TOKEN_PATH); + } + + public DefaultIdSiteAuthenticator(Application application, DataStore dataStore, String oauthTokenPath) { super(application, dataStore); + this.oauthTokenPath = oauthTokenPath; } @Override @@ -50,7 +57,7 @@ public OAuthGrantRequestAuthenticationResult authenticate(OAuthRequestAuthentica HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - GrantAuthenticationToken grantResult = dataStore.create(application.getHref() + OAUTH_TOKEN_PATH, attempt, GrantAuthenticationToken.class, httpHeaders); + GrantAuthenticationToken grantResult = dataStore.create(application.getHref() + oauthTokenPath, attempt, GrantAuthenticationToken.class, httpHeaders); OAuthGrantRequestAuthenticationResultBuilder builder = new DefaultOAuthGrantRequestAuthenticationResultBuilder(grantResult); return builder.build(); diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultIdSiteAuthenticatorFactory.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultIdSiteAuthenticatorFactory.java index 932964cf59..5f873c0110 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultIdSiteAuthenticatorFactory.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultIdSiteAuthenticatorFactory.java @@ -16,10 +16,15 @@ package com.stormpath.sdk.impl.oauth; import com.stormpath.sdk.application.Application; -import com.stormpath.sdk.impl.application.DefaultApplication; +import com.stormpath.sdk.application.OAuthApplication; +import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.resource.AbstractResource; import com.stormpath.sdk.oauth.IdSiteAuthenticator; import com.stormpath.sdk.oauth.IdSiteAuthenticatorFactory; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + /** * @since 1.0.RC8.2 */ @@ -27,6 +32,6 @@ public class DefaultIdSiteAuthenticatorFactory implements IdSiteAuthenticatorFac @Override public IdSiteAuthenticator forApplication(Application application) { - return ((DefaultApplication) application).createIdSiteAuthenticator(); + return ((OAuthApplication) application).createIdSiteAuthenticator(); } } \ No newline at end of file diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthBearerRequestAuthenticatorFactory.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthBearerRequestAuthenticatorFactory.java index 8882523062..f9a59d2288 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthBearerRequestAuthenticatorFactory.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthBearerRequestAuthenticatorFactory.java @@ -16,7 +16,7 @@ package com.stormpath.sdk.impl.oauth; import com.stormpath.sdk.application.Application; -import com.stormpath.sdk.impl.application.DefaultApplication; +import com.stormpath.sdk.application.OAuthApplication; import com.stormpath.sdk.oauth.OAuthBearerRequestAuthenticator; import com.stormpath.sdk.oauth.OAuthBearerRequestAuthenticatorFactory; @@ -27,6 +27,6 @@ public class DefaultOAuthBearerRequestAuthenticatorFactory implements OAuthBeare @Override public OAuthBearerRequestAuthenticator forApplication(Application application) { - return ((DefaultApplication) application).createJwtAuthenticator(); + return ((OAuthApplication) application).createJwtAuthenticator(); } } diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthClientCredentialsGrantRequestAuthenticator.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthClientCredentialsGrantRequestAuthenticator.java index 753bf6f8a7..2a4320ccf2 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthClientCredentialsGrantRequestAuthenticator.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthClientCredentialsGrantRequestAuthenticator.java @@ -14,8 +14,15 @@ public class DefaultOAuthClientCredentialsGrantRequestAuthenticator extends Abst private final static String OAUTH_TOKEN_PATH = "/oauth/token"; + private final String oauthTokenPath; + public DefaultOAuthClientCredentialsGrantRequestAuthenticator(Application application, DataStore dataStore) { + this(application, dataStore, OAUTH_TOKEN_PATH); + } + + public DefaultOAuthClientCredentialsGrantRequestAuthenticator(Application application, DataStore dataStore, String oauthTokenPath) { super(application, dataStore); + this.oauthTokenPath = oauthTokenPath; } @Override @@ -32,7 +39,7 @@ public OAuthGrantRequestAuthenticationResult authenticate(OAuthRequestAuthentica HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - GrantAuthenticationToken grantResult = dataStore.create(application.getHref() + OAUTH_TOKEN_PATH, oAuthClientCredentialsGrantAuthenticationAttempt, GrantAuthenticationToken.class, httpHeaders); + GrantAuthenticationToken grantResult = dataStore.create(application.getHref() + oauthTokenPath, oAuthClientCredentialsGrantAuthenticationAttempt, GrantAuthenticationToken.class, httpHeaders); OAuthGrantRequestAuthenticationResultBuilder builder = new DefaultOAuthClientCredentialsGrantRequestAuthenticationResultBuilder(grantResult); diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthClientCredentialsRequestAuthenticatorFactory.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthClientCredentialsRequestAuthenticatorFactory.java index 031be71115..7ae0b09bc8 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthClientCredentialsRequestAuthenticatorFactory.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthClientCredentialsRequestAuthenticatorFactory.java @@ -1,7 +1,7 @@ package com.stormpath.sdk.impl.oauth; import com.stormpath.sdk.application.Application; -import com.stormpath.sdk.impl.application.DefaultApplication; +import com.stormpath.sdk.application.OAuthApplication; import com.stormpath.sdk.oauth.OAuthClientCredentialsGrantRequestAuthenticator; import com.stormpath.sdk.oauth.OAuthClientCredentialsRequestAuthenticatorFactory; @@ -11,6 +11,6 @@ public class DefaultOAuthClientCredentialsRequestAuthenticatorFactory implements OAuthClientCredentialsRequestAuthenticatorFactory { @Override public OAuthClientCredentialsGrantRequestAuthenticator forApplication(Application application) { - return ((DefaultApplication) application).createClientCredentialsGrantAuthenticator(); + return ((OAuthApplication) application).createClientCredentialsGrantAuthenticator(); } } diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthPasswordGrantRequestAuthenticator.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthPasswordGrantRequestAuthenticator.java index 2a2779989d..1bddda9cc1 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthPasswordGrantRequestAuthenticator.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthPasswordGrantRequestAuthenticator.java @@ -29,8 +29,15 @@ public class DefaultOAuthPasswordGrantRequestAuthenticator extends AbstractOAuth final static String OAUTH_TOKEN_PATH = "/oauth/token"; + private final String oauthTokenPath; + public DefaultOAuthPasswordGrantRequestAuthenticator(Application application, DataStore dataStore) { + this(application, dataStore, OAUTH_TOKEN_PATH); + } + + public DefaultOAuthPasswordGrantRequestAuthenticator(Application application, DataStore dataStore, String oauthTokenPath) { super(application, dataStore); + this.oauthTokenPath = oauthTokenPath; } @Override @@ -39,17 +46,19 @@ public OAuthGrantRequestAuthenticationResult authenticate(OAuthRequestAuthentica Assert.isInstanceOf(OAuthPasswordGrantRequestAuthentication.class, authenticationRequest, "authenticationRequest must be an instance of PasswordGrantRequest."); OAuthPasswordGrantRequestAuthentication oauthPasswordGrantRequestAuthentication = (OAuthPasswordGrantRequestAuthentication) authenticationRequest; - OAuthPasswordGrantAuthenticationAttempt oauthPasswordGrantAuthenticationAttempt = new DefaultOAuthPasswordGrantAuthenticationAttempt(dataStore); + DefaultOAuthPasswordGrantAuthenticationAttempt oauthPasswordGrantAuthenticationAttempt = new DefaultOAuthPasswordGrantAuthenticationAttempt(dataStore); oauthPasswordGrantAuthenticationAttempt.setLogin(oauthPasswordGrantRequestAuthentication.getLogin()); oauthPasswordGrantAuthenticationAttempt.setPassword(oauthPasswordGrantRequestAuthentication.getPassword()); oauthPasswordGrantAuthenticationAttempt.setGrantType(oauthPasswordGrantRequestAuthentication.getGrantType()); if (oauthPasswordGrantRequestAuthentication.getAccountStore() != null){ oauthPasswordGrantAuthenticationAttempt.setAccountStore(oauthPasswordGrantRequestAuthentication.getAccountStore()); } + oauthPasswordGrantAuthenticationAttempt.setProperty("scope", "offline_access"); + HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - GrantAuthenticationToken grantResult = dataStore.create(application.getHref() + OAUTH_TOKEN_PATH, oauthPasswordGrantAuthenticationAttempt, GrantAuthenticationToken.class, httpHeaders); + GrantAuthenticationToken grantResult = dataStore.create(application.getHref() + oauthTokenPath, oauthPasswordGrantAuthenticationAttempt, GrantAuthenticationToken.class, httpHeaders); OAuthGrantRequestAuthenticationResultBuilder builder = new DefaultOAuthGrantRequestAuthenticationResultBuilder(grantResult); return builder.build(); diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthPasswordRequestAuthenticatorFactory.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthPasswordRequestAuthenticatorFactory.java index e3d50f7629..5a850530a5 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthPasswordRequestAuthenticatorFactory.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthPasswordRequestAuthenticatorFactory.java @@ -16,10 +16,15 @@ package com.stormpath.sdk.impl.oauth; import com.stormpath.sdk.application.Application; -import com.stormpath.sdk.impl.application.DefaultApplication; +import com.stormpath.sdk.application.OAuthApplication; +import com.stormpath.sdk.impl.ds.InternalDataStore; +import com.stormpath.sdk.impl.resource.AbstractResource; import com.stormpath.sdk.oauth.OAuthPasswordGrantRequestAuthenticator; import com.stormpath.sdk.oauth.OAuthPasswordRequestAuthenticatorFactory; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + /** * @since 1.0.RC7 */ @@ -27,6 +32,6 @@ public class DefaultOAuthPasswordRequestAuthenticatorFactory implements OAuthPas @Override public OAuthPasswordGrantRequestAuthenticator forApplication(Application application) { - return ((DefaultApplication) application).createPasswordGrantAuthenticator(); + return ((OAuthApplication) application).createPasswordGrantAuthenticator(); } } diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthPolicy.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthPolicy.java index 4bec6c57c1..386055ada6 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthPolicy.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthPolicy.java @@ -17,12 +17,12 @@ import com.stormpath.sdk.application.Application; import com.stormpath.sdk.impl.ds.InternalDataStore; -import com.stormpath.sdk.impl.resource.*; +import com.stormpath.sdk.impl.resource.AbstractInstanceResource; +import com.stormpath.sdk.impl.resource.Property; +import com.stormpath.sdk.impl.resource.ResourceReference; +import com.stormpath.sdk.impl.resource.StringProperty; import com.stormpath.sdk.lang.Assert; import com.stormpath.sdk.oauth.OAuthPolicy; -import com.stormpath.sdk.oauth.openidconnect.Scope; -import com.stormpath.sdk.oauth.openidconnect.ScopeList; -import com.stormpath.sdk.resource.ResourceException; import com.stormpath.sdk.tenant.Tenant; import java.util.Map; @@ -35,23 +35,16 @@ public class DefaultOAuthPolicy extends AbstractInstanceResource implements OAut // SIMPLE PROPERTIES static final StringProperty ACCESS_TOKEN_TTL = new StringProperty("accessTokenTtl"); static final StringProperty REFRESH_TOKEN_TTL = new StringProperty("refreshTokenTtl"); - static final StringProperty ID_TOKEN_TTL = new StringProperty("idTokenTtl"); static final StringProperty TOKEN_ENDPOINT = new StringProperty("tokenEndpoint"); static final StringProperty REVOCATION_ENDPOINT = new StringProperty("revocationEndpoint"); - static final CollectionReference SCOPES = - new CollectionReference<>("scopes", ScopeList.class, Scope.class); - - static final MapProperty ACCESS_TOKEN_ATTRIBUTE_MAPPINGS = new MapProperty("accessTokenAttributeMappings"); - static final MapProperty ID_TOKEN_ATTRIBUTE_MAPPINGS = new MapProperty("idTokenAttributeMappings"); - // INSTANCE RESOURCE REFERENCES: static final ResourceReference APPLICATION = new ResourceReference("application", Application.class); static final ResourceReference TENANT = new ResourceReference("tenant", Tenant.class); private static final Map PROPERTY_DESCRIPTORS = createPropertyDescriptorMap( - ACCESS_TOKEN_TTL, REFRESH_TOKEN_TTL, ID_TOKEN_TTL, TOKEN_ENDPOINT, SCOPES, ACCESS_TOKEN_ATTRIBUTE_MAPPINGS, ID_TOKEN_ATTRIBUTE_MAPPINGS, APPLICATION, TENANT); + ACCESS_TOKEN_TTL, REFRESH_TOKEN_TTL, TOKEN_ENDPOINT, APPLICATION, TENANT); @Override public Map getPropertyDescriptors() { @@ -76,9 +69,6 @@ public String getRefreshTokenTtl() { return getString(REFRESH_TOKEN_TTL); } - @Override - public String getIdTokenTtl() { return getString(ID_TOKEN_TTL); } - @Override public String getTokenEndpoint() { return getString(TOKEN_ENDPOINT); @@ -103,46 +93,6 @@ public OAuthPolicy setRefreshTokenTtl(String refreshTokenTtl) { return this; } - @Override - public OAuthPolicy setIdTokenTtl(String idTokenTtl) { - Assert.notNull(idTokenTtl, "idTokenTtl cannot be null."); - setProperty(ID_TOKEN_TTL, idTokenTtl); - return this; - } - - @Override - public Scope createScope(Scope scope) throws ResourceException { - Assert.notNull(scope, "Scope instance cannot be null."); - return getDataStore().create(getScopes().getHref(), scope); - } - - @Override - public ScopeList getScopes() { - return getResourceProperty(SCOPES); - } - - @Override - public Map getAccessTokenAttributeMap() { - return getMap(ACCESS_TOKEN_ATTRIBUTE_MAPPINGS); - } - - @Override - public OAuthPolicy setAccessTokenAttributeMap(Map accessTokenAttributeMap) { - setProperty(ACCESS_TOKEN_ATTRIBUTE_MAPPINGS, accessTokenAttributeMap); - return this; - } - - @Override - public Map getIdTokenAttributeMap() { - return getMap(ID_TOKEN_ATTRIBUTE_MAPPINGS); - } - - @Override - public OAuthPolicy setIdTokenAttributeMap(Map idTokenAttributeMap) { - setProperty(ID_TOKEN_ATTRIBUTE_MAPPINGS, idTokenAttributeMap); - return this; - } - @Override public Application getApplication() { return getResourceProperty(APPLICATION); diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthPolicyOptions.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthPolicyOptions.java deleted file mode 100644 index a54acfd031..0000000000 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthPolicyOptions.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Stormpath, 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.stormpath.sdk.impl.oauth; - -import com.stormpath.sdk.impl.query.DefaultOptions; -import com.stormpath.sdk.oauth.OAuthPolicyOptions; - -/** - * @since 1.6.0 - */ -public class DefaultOAuthPolicyOptions extends DefaultOptions implements OAuthPolicyOptions{ - @Override - public OAuthPolicyOptions withScopes() { - return expand(DefaultOAuthPolicy.SCOPES); - } - - @Override - public OAuthPolicyOptions withScopes(int limit) { - return expand(DefaultOAuthPolicy.SCOPES, limit); - } - - @Override - public OAuthPolicyOptions withScopes(int limit, int offset) { - return expand(DefaultOAuthPolicy.SCOPES, limit, offset); - } -} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthRefreshTokenRequestAuthenticator.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthRefreshTokenRequestAuthenticator.java index 7e6403462f..3e75b8da61 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthRefreshTokenRequestAuthenticator.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthRefreshTokenRequestAuthenticator.java @@ -29,8 +29,15 @@ public class DefaultOAuthRefreshTokenRequestAuthenticator extends AbstractOAuthR final static String OAUTH_TOKEN_PATH = "/oauth/token"; + private final String oauthTokenPath; + public DefaultOAuthRefreshTokenRequestAuthenticator(Application application, DataStore dataStore){ + this(application, dataStore, OAUTH_TOKEN_PATH); + } + + public DefaultOAuthRefreshTokenRequestAuthenticator(Application application, DataStore dataStore, String oauthTokenPath) { super(application, dataStore); + this.oauthTokenPath = oauthTokenPath; } @Override @@ -45,7 +52,7 @@ public OAuthGrantRequestAuthenticationResult authenticate(OAuthRequestAuthentica HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - GrantAuthenticationToken grantResult = dataStore.create(application.getHref() + OAUTH_TOKEN_PATH, attempt, GrantAuthenticationToken.class, httpHeaders); + GrantAuthenticationToken grantResult = dataStore.create(application.getHref() + oauthTokenPath, attempt, GrantAuthenticationToken.class, httpHeaders); OAuthGrantRequestAuthenticationResultBuilder builder = new DefaultOAuthGrantRequestAuthenticationResultBuilder(grantResult); return builder.build(); diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthRefreshTokenRequestAuthenticatorFactory.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthRefreshTokenRequestAuthenticatorFactory.java index b17e69131d..3629285f0b 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthRefreshTokenRequestAuthenticatorFactory.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthRefreshTokenRequestAuthenticatorFactory.java @@ -16,7 +16,7 @@ package com.stormpath.sdk.impl.oauth; import com.stormpath.sdk.application.Application; -import com.stormpath.sdk.impl.application.DefaultApplication; +import com.stormpath.sdk.application.OAuthApplication; import com.stormpath.sdk.oauth.OAuthRefreshTokenRequestAuthenticator; import com.stormpath.sdk.oauth.OAuthRefreshTokenRequestAuthenticatorFactory; @@ -27,6 +27,6 @@ public class DefaultOAuthRefreshTokenRequestAuthenticatorFactory implements OAut @Override public OAuthRefreshTokenRequestAuthenticator forApplication(Application application) { - return ((DefaultApplication) application).createRefreshGrantAuthenticator(); + return ((OAuthApplication) application).createRefreshGrantAuthenticator(); } } diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator.java index 77660f8ae2..e86422741a 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator.java @@ -33,8 +33,15 @@ public class DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator exten private final static String OAUTH_TOKEN_PATH = "/oauth/token"; + private final String oauthTokenPath; + public DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator(Application application, DataStore dataStore) { + this(application, dataStore, OAUTH_TOKEN_PATH); + } + + public DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticator(Application application, DataStore dataStore, String oauthTokenPath) { super(application, dataStore); + this.oauthTokenPath = oauthTokenPath; } @Override @@ -52,7 +59,7 @@ public OAuthGrantRequestAuthenticationResult authenticate(OAuthRequestAuthentica HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - GrantAuthenticationToken grantResult = dataStore.create(application.getHref() + OAUTH_TOKEN_PATH, authenticationAttempt, GrantAuthenticationToken.class, httpHeaders); + GrantAuthenticationToken grantResult = dataStore.create(application.getHref() + oauthTokenPath, authenticationAttempt, GrantAuthenticationToken.class, httpHeaders); OAuthGrantRequestAuthenticationResultBuilder builder = new DefaultOAuthGrantRequestAuthenticationResultBuilder(grantResult); diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java index 890412929b..b35f5d215c 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory.java @@ -16,7 +16,7 @@ package com.stormpath.sdk.impl.oauth; import com.stormpath.sdk.application.Application; -import com.stormpath.sdk.impl.application.DefaultApplication; +import com.stormpath.sdk.application.OAuthApplication; import com.stormpath.sdk.oauth.OAuthStormpathFactorChallengeGrantRequestAuthenticator; import com.stormpath.sdk.oauth.OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory; @@ -26,6 +26,6 @@ public class DefaultOAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory implements OAuthStormpathFactorChallengeGrantRequestAuthenticatorFactory { @Override public OAuthStormpathFactorChallengeGrantRequestAuthenticator forApplication(Application application) { - return ((DefaultApplication) application).createStormpathFactorChallengeGrantAuthenticator(); + return ((OAuthApplication) application).createStormpathFactorChallengeGrantAuthenticator(); } } diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathSocialGrantRequestAuthenticator.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathSocialGrantRequestAuthenticator.java index 2ed748b6c0..3d5c51db8f 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathSocialGrantRequestAuthenticator.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathSocialGrantRequestAuthenticator.java @@ -18,8 +18,15 @@ public class DefaultOAuthStormpathSocialGrantRequestAuthenticator extends Abstra private final static String OAUTH_TOKEN_PATH = "/oauth/token"; + private final String oauthTokenPath; + public DefaultOAuthStormpathSocialGrantRequestAuthenticator(Application application, DataStore dataStore) { + this(application, dataStore, OAUTH_TOKEN_PATH); + } + + public DefaultOAuthStormpathSocialGrantRequestAuthenticator(Application application, DataStore dataStore, String oauthTokenPath) { super(application, dataStore); + this.oauthTokenPath = oauthTokenPath; } @Override @@ -42,7 +49,7 @@ public OAuthGrantRequestAuthenticationResult authenticate(OAuthRequestAuthentica HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - GrantAuthenticationToken grantResult = dataStore.create(application.getHref() + OAUTH_TOKEN_PATH, authenticationAttempt, GrantAuthenticationToken.class, httpHeaders); + GrantAuthenticationToken grantResult = dataStore.create(application.getHref() + oauthTokenPath, authenticationAttempt, GrantAuthenticationToken.class, httpHeaders); OAuthGrantRequestAuthenticationResultBuilder builder = new DefaultOAuthGrantRequestAuthenticationResultBuilder(grantResult); diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathSocialRequestAuthenticatorFactory.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathSocialRequestAuthenticatorFactory.java index 2987903fe3..747e05eaa9 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathSocialRequestAuthenticatorFactory.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthStormpathSocialRequestAuthenticatorFactory.java @@ -1,7 +1,7 @@ package com.stormpath.sdk.impl.oauth; import com.stormpath.sdk.application.Application; -import com.stormpath.sdk.impl.application.DefaultApplication; +import com.stormpath.sdk.application.OAuthApplication; import com.stormpath.sdk.oauth.OAuthStormpathSocialGrantRequestAuthenticator; import com.stormpath.sdk.oauth.OAuthStormpathSocialRequestAuthenticatorFactory; @@ -11,6 +11,6 @@ public class DefaultOAuthStormpathSocialRequestAuthenticatorFactory implements OAuthStormpathSocialRequestAuthenticatorFactory { @Override public OAuthStormpathSocialGrantRequestAuthenticator forApplication(Application application) { - return ((DefaultApplication) application).createStormpathSocialGrantAuthenticator(); + return ((OAuthApplication) application).createStormpathSocialGrantAuthenticator(); } } diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthTokenRevocator.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthTokenRevocator.java index afe716108d..14e109cab7 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthTokenRevocator.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthTokenRevocator.java @@ -31,15 +31,22 @@ public class DefaultOAuthTokenRevocator implements OAuthTokenRevocator { private final static String OAUTH_REVOKE_PATH = "/oauth/revoke"; + private final String oauthRevokePath; + protected final Application application; protected final InternalDataStore dataStore; public DefaultOAuthTokenRevocator(Application application, InternalDataStore dataStore) { + this(application, dataStore, OAUTH_REVOKE_PATH); + } + + public DefaultOAuthTokenRevocator(Application application, InternalDataStore dataStore, String oauthRevokePath) { Assert.notNull(application, "application cannot be null."); Assert.notNull(dataStore, "dataStore cannot be null."); this.application = application; this.dataStore = dataStore; + this.oauthRevokePath = oauthRevokePath; } @Override @@ -57,7 +64,7 @@ public void revoke(OAuthRevocationRequest request) { HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - dataStore.create(application.getHref() + OAUTH_REVOKE_PATH, attempt, OAuthTokenRevoked.class, httpHeaders); + dataStore.create(application.getHref() + oauthRevokePath, attempt, OAuthTokenRevoked.class, httpHeaders); } } diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthTokenRevocatorFactory.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthTokenRevocatorFactory.java index 9dc7836630..0ee4382ee8 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthTokenRevocatorFactory.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/oauth/DefaultOAuthTokenRevocatorFactory.java @@ -16,7 +16,7 @@ package com.stormpath.sdk.impl.oauth; import com.stormpath.sdk.application.Application; -import com.stormpath.sdk.impl.application.DefaultApplication; +import com.stormpath.sdk.application.OAuthApplication; import com.stormpath.sdk.oauth.OAuthTokenRevocator; import com.stormpath.sdk.oauth.OAuthTokenRevocatorFactory; @@ -26,6 +26,6 @@ public class DefaultOAuthTokenRevocatorFactory implements OAuthTokenRevocatorFactory { public OAuthTokenRevocator forApplication(Application application) { - return ((DefaultApplication) application).createOAuhtTokenRevocator(); + return ((OAuthApplication) application).createOAuhtTokenRevocator(); } } diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/openidconnect/DefaultScope.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/openidconnect/DefaultScope.java deleted file mode 100644 index 31af164e66..0000000000 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/openidconnect/DefaultScope.java +++ /dev/null @@ -1,124 +0,0 @@ -/* -* Copyright 2017 Stormpath, 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.stormpath.sdk.impl.oauth.openidconnect; - -import com.stormpath.sdk.impl.ds.InternalDataStore; -import com.stormpath.sdk.impl.resource.*; -import com.stormpath.sdk.oauth.OAuthPolicy; -import com.stormpath.sdk.oauth.openidconnect.Scope; - -import java.util.Date; -import java.util.Map; - -/** - * @since 1.6.0 - */ -public class DefaultScope extends AbstractInstanceResource implements Scope { - - static final StringProperty NAME = new StringProperty("name"); - static final StringProperty FRIENDLY_NAME = new StringProperty("friendlyName"); - static final StringProperty DESCRIPTION = new StringProperty("description"); - static final MapProperty ATTRIBUTE_MAPPINGS = new MapProperty("attributeMappings"); - public static final DateProperty CREATED_AT = new DateProperty("createdAt"); - public static final DateProperty MODIFIED_AT = new DateProperty("modifiedAt"); - - static final ResourceReference O_AUTH_POLICY = new ResourceReference<>("oAuthPolicy", OAuthPolicy.class); - - static final Map PROPERTY_DESCRIPTORS = createPropertyDescriptorMap(NAME, FRIENDLY_NAME, DESCRIPTION, ATTRIBUTE_MAPPINGS, O_AUTH_POLICY, CREATED_AT, MODIFIED_AT); - - public DefaultScope(InternalDataStore dataStore) { - super(dataStore); - } - - public DefaultScope(InternalDataStore dataStore, Map properties) { - super(dataStore, properties); - } - - @Override - public Map getPropertyDescriptors() { - return PROPERTY_DESCRIPTORS; - } - - @Override - public String getName() { - return getString(NAME); - } - - @Override - public Scope setName(String name) { - setProperty(NAME, name); - return this; - } - - @Override - public String getFriendlyName() { - return getString(FRIENDLY_NAME); - } - - @Override - public Scope setFriendlyName(String friendlyName) { - setProperty(FRIENDLY_NAME, friendlyName); - return this; - } - - @Override - public String getDescription() { - return getString(DESCRIPTION); - } - - @Override - public Scope setDescription(String description) { - setProperty(DESCRIPTION, description); - return this; - } - - @Override - public Map getAttributeMappings() { - return getMap(ATTRIBUTE_MAPPINGS); - } - - @Override - public Scope setAttributeMappings(Map attributeMappings) { - setProperty(ATTRIBUTE_MAPPINGS, attributeMappings); - return this; - } - - @Override - public OAuthPolicy getOAuthPolicy() { - return getResourceProperty(O_AUTH_POLICY); - } - - @Override - public Scope setOAuthPolicy(OAuthPolicy oAuthPolicy) { - setMaterializableResourceProperty(O_AUTH_POLICY, oAuthPolicy); - return this; - } - - @Override - public void delete() { - getDataStore().delete(this); - } - - @Override - public Date getCreatedAt() { - return getDateProperty(CREATED_AT); - } - - @Override - public Date getModifiedAt() { - return getDateProperty(MODIFIED_AT); - } -} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/oauth/openidconnect/DefaultScopeList.java b/impl/src/main/java/com/stormpath/sdk/impl/oauth/openidconnect/DefaultScopeList.java deleted file mode 100644 index 79b5150cb1..0000000000 --- a/impl/src/main/java/com/stormpath/sdk/impl/oauth/openidconnect/DefaultScopeList.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2017 Stormpath, 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.stormpath.sdk.impl.oauth.openidconnect; - -import com.stormpath.sdk.impl.ds.InternalDataStore; -import com.stormpath.sdk.impl.resource.AbstractCollectionResource; -import com.stormpath.sdk.impl.resource.ArrayProperty; -import com.stormpath.sdk.impl.resource.Property; -import com.stormpath.sdk.oauth.openidconnect.Scope; -import com.stormpath.sdk.oauth.openidconnect.ScopeList; - -import java.util.Map; - -/** - * @since 1.6.0 - */ -public class DefaultScopeList extends AbstractCollectionResource implements ScopeList { - - private static final ArrayProperty ITEMS = new ArrayProperty<>("items", Scope.class); - - private static final Map PROPERTY_DESCRIPTORS = createPropertyDescriptorMap(OFFSET, LIMIT, ITEMS); - - public DefaultScopeList(InternalDataStore dataStore) { - super(dataStore); - } - - public DefaultScopeList(InternalDataStore dataStore, Map properties) { - super(dataStore, properties); - } - - public DefaultScopeList(InternalDataStore dataStore, Map properties, Map queryParams) { - super(dataStore, properties, queryParams); - } - - @Override - protected Class getItemType() { - return Scope.class; - } - - @Override - public Map getPropertyDescriptors() { - return PROPERTY_DESCRIPTORS; - } -} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/okta/OktaApiPaths.java b/impl/src/main/java/com/stormpath/sdk/impl/okta/OktaApiPaths.java new file mode 100644 index 0000000000..ee083792c3 --- /dev/null +++ b/impl/src/main/java/com/stormpath/sdk/impl/okta/OktaApiPaths.java @@ -0,0 +1,18 @@ +package com.stormpath.sdk.impl.okta; + +/** + * Static path properties in one place. + */ +public final class OktaApiPaths { + + public static final String API_V1 = "/api/v1/"; + + public static final String USERS = API_V1 + "users/"; + + public static final String PASSWORD_RECOVERY = API_V1 + "authn/recovery/password"; + + public static String apiPath(String resource) { + return API_V1 + resource; + } + +} diff --git a/impl/src/main/java/com/stormpath/sdk/impl/okta/OktaUserAccountConverter.java b/impl/src/main/java/com/stormpath/sdk/impl/okta/OktaUserAccountConverter.java index 8a52c024f3..b948558283 100644 --- a/impl/src/main/java/com/stormpath/sdk/impl/okta/OktaUserAccountConverter.java +++ b/impl/src/main/java/com/stormpath/sdk/impl/okta/OktaUserAccountConverter.java @@ -1,6 +1,7 @@ package com.stormpath.sdk.impl.okta; import com.stormpath.sdk.account.AccountStatus; +import com.stormpath.sdk.group.Group; import com.stormpath.sdk.lang.Collections; import com.stormpath.sdk.lang.Objects; import com.stormpath.sdk.lang.Strings; @@ -12,7 +13,7 @@ /** * Converts between Okta User map and Stormpath Account Map. */ -public class OktaUserAccountConverter { +public final class OktaUserAccountConverter { // https://docs.stormpath.com/rest/product-guide/latest/reference.html#account @@ -68,7 +69,44 @@ public class OktaUserAccountConverter { */ - public Map toAccount(Map userMap) { + + private static final String STORMPATH_EMAIL = "email"; + private static final String STORMPATH_HREF = "href"; + private static final String STORMPATH_USERNAME = "username"; + private static final String STORMPATH_PASSWORD = "password"; + private static final String STORMPATH_GIVEN_NAME = "givenName"; + private static final String STORMPATH_MIDDLE_NAME = "middleName"; + private static final String STORMPATH_SURNAME = "surname"; + private static final String STORMPATH_FULL_NAME = "fullName"; + private static final String STORMPATH_CREATED_AT = "createdAt"; + private static final String STORMPATH_MODIFIED_AT = "modifiedAt"; + private static final String STORMPATH_PASSWORD_MODIFIED_AT = "passwordModifiedAt"; + private static final String STORMPATH_STATUS = "status"; + + private static final String STORMPATH_CUSTOM_DATA = "customData"; + + private static final String OKTA_ID = "id"; + private static final String OKTA_PROFILE = "profile"; + private static final String OKTA_LOGIN = "login"; + private static final String OKTA_EMAIL = "email"; + private static final String OKTA_FIRST_NAME = "firstName"; + private static final String OKTA_MIDDLE_NAME = "middleName"; + private static final String OKTA_LAST_NAME = "lastName"; + private static final String OKTA_CREATED = "created"; + private static final String OKTA_LAST_UPDATED = "lastUpdated"; + private static final String OKTA_PASSEWORD_CHANGED = "passwordChanged"; + private static final String OKTA_STATUS = "status"; + private static final String OKTA_LINKS = "_links"; + private static final String OKTA_SELF = "self"; + private static final String OKTA_HREF = "href"; + + private static final String OKTA_CREDENTIALS = "credentials"; + private static final String OKTA_PASSWORD = "password"; + private static final String OKTA_VALUE = "value"; + + private OktaUserAccountConverter() {} + + public static Map toAccount(Map userMap, String baseUrl) { // quick hack to make existing tests work. // if the UserMap does NOT contain an 'id' field, just assume the map is already an account. @@ -77,98 +115,108 @@ public Map toAccount(Map userMap) { } Map accountMap = new LinkedHashMap<>(); - Map profileMap = (Map) userMap.get("profile"); + nullSafePut(accountMap, STORMPATH_HREF, baseUrl + OktaApiPaths.USERS + userMap.get(OKTA_ID)); + Map profileMap = getPropertyMap(userMap, OKTA_PROFILE); if (!Collections.isEmpty(profileMap)) { - nullSafePut(accountMap, "username", profileMap.get("login")); - nullSafePut(accountMap, "email", profileMap.get("email")); - - nullSafePut(accountMap, "givenName", profileMap.get("firstName")); - nullSafePut(accountMap, "middleName", profileMap.get("middleName")); - nullSafePut(accountMap, "surname", profileMap.get("lastName")); - nullSafePut(accountMap, "fullName", profileMap.get("firstName") + " " + profileMap.get("lastName")); // TODO: add null/format checks needed here - - nullSafePut(accountMap, "customData", trimMap(profileMap, "login", "email", "firstName", "middleName", "lastName")); + nullSafePut(accountMap, STORMPATH_USERNAME, profileMap.get(OKTA_LOGIN)); + nullSafePut(accountMap, STORMPATH_EMAIL, profileMap.get(OKTA_EMAIL)); + nullSafePut(accountMap, STORMPATH_GIVEN_NAME, profileMap.get(OKTA_FIRST_NAME)); + nullSafePut(accountMap, STORMPATH_MIDDLE_NAME, profileMap.get(OKTA_MIDDLE_NAME)); + nullSafePut(accountMap, STORMPATH_SURNAME, profileMap.get(OKTA_LAST_NAME)); + // build full name + nullSafePut(accountMap, STORMPATH_FULL_NAME, buildFullName(profileMap.get(OKTA_FIRST_NAME), profileMap.get(OKTA_LAST_NAME))); + // everything not in this lis is considered customData + nullSafePut(accountMap, STORMPATH_CUSTOM_DATA, trimMap(profileMap, OKTA_LOGIN, OKTA_EMAIL, OKTA_FIRST_NAME, OKTA_MIDDLE_NAME, OKTA_LAST_NAME)); } - nullSafePut(accountMap, "createdAt", userMap.get("created")); - nullSafePut(accountMap, "modifiedAt", userMap.get("lastUpdated")); - nullSafePut(accountMap, "passwordModifiedAt", profileMap.get("passwordChanged")); + nullSafePut(accountMap, STORMPATH_CREATED_AT, userMap.get(OKTA_CREATED)); + nullSafePut(accountMap, STORMPATH_MODIFIED_AT, userMap.get(OKTA_LAST_UPDATED)); + nullSafePut(accountMap, STORMPATH_PASSWORD_MODIFIED_AT, profileMap.get(OKTA_PASSEWORD_CHANGED)); // UserStatus -> AccountStatus enum conversion - if (userMap.containsKey("status")) { - accountMap.put("status", fromUserStatus(userMap.get("status"))); + if (userMap.containsKey(OKTA_STATUS)) { + accountMap.put(STORMPATH_STATUS, fromUserStatus(userMap.get(OKTA_STATUS))); } // _links.self.href -> href - Map linksMap = (Map) userMap.get("_links"); - if (!Collections.isEmpty(linksMap)) { - Map self = (Map) linksMap.get("self"); - if (!Collections.isEmpty(self)) { - nullSafePut(accountMap, "href", self.get("href")); - } - } + nullSafePut(accountMap, STORMPATH_HREF, getOktaHref(userMap)); - // pretty sure this logic is NOT needed when mapping User -> Account -// Map credentialsMap = (Map) userMap.get("credentials"); -// if (!Collections.isEmpty(profileMap)) { -// Map passwordMap = (Map) userMap.get("password"); -// if (!Collections.isEmpty(passwordMap) ) { -// nullSafePut(accountMap, "password", profileMap.get("value")); -// } -// } + Map groupsMap = new LinkedHashMap<>(); + groupsMap.put(STORMPATH_HREF, accountMap.get(STORMPATH_HREF) + "/groups"); + accountMap.put("groups", groupsMap); return accountMap; } - public Map toUser(Map accountMap) { + public static Map toUser(Map accountMap) { Map userMap = new LinkedHashMap<>(); Map profileMap = new LinkedHashMap<>(); - userMap.put("profile", profileMap); + userMap.put(OKTA_PROFILE, profileMap); - String username = (String) accountMap.get("username"); + String username = (String) accountMap.get(STORMPATH_USERNAME); if (!Strings.hasText(username)) { - username = (String) accountMap.get("email"); + username = (String) accountMap.get(STORMPATH_EMAIL); } - nullSafePut(profileMap, "login", username); - nullSafePut(profileMap, "email", accountMap.get("email")); + nullSafePut(profileMap, OKTA_LOGIN, username); + nullSafePut(profileMap, OKTA_EMAIL, accountMap.get(STORMPATH_EMAIL)); + nullSafePut(profileMap, OKTA_FIRST_NAME, accountMap.get(STORMPATH_GIVEN_NAME)); + nullSafePut(profileMap, OKTA_MIDDLE_NAME, accountMap.get(STORMPATH_MIDDLE_NAME)); + nullSafePut(profileMap, OKTA_LAST_NAME, accountMap.get(STORMPATH_SURNAME)); - nullSafePut(profileMap, "firstName", accountMap.get("givenName")); - nullSafePut(profileMap, "middleName", accountMap.get("middleName")); - nullSafePut(profileMap, "lastName", accountMap.get("surname")); -// nullSafePut(userMap, "displayName", accountMap.get("fullName")); // generated field from Stormpath - nullSafePut(userMap, "created", accountMap.get("createdAt")); - nullSafePut(userMap, "lastUpdated", accountMap.get("modifiedAt")); - nullSafePut(userMap, "passwordChanged", accountMap.get("passwordModifiedAt")); + nullSafePut(userMap, OKTA_CREATED, accountMap.get(STORMPATH_CREATED_AT)); + nullSafePut(userMap, OKTA_LAST_UPDATED, accountMap.get(STORMPATH_MODIFIED_AT)); + nullSafePut(userMap, OKTA_PASSEWORD_CHANGED, accountMap.get(STORMPATH_PASSWORD_MODIFIED_AT)); // credentials - if (accountMap.containsKey("password")) { + if (accountMap.containsKey(STORMPATH_PASSWORD)) { Map credentialsMap = new LinkedHashMap<>(); Map passwordMap = new LinkedHashMap<>(); - userMap.put("credentials", credentialsMap); - credentialsMap.put("password", passwordMap); - passwordMap.put("value", accountMap.get("password")); + userMap.put(OKTA_CREDENTIALS, credentialsMap); + credentialsMap.put(OKTA_PASSWORD, passwordMap); + passwordMap.put(OKTA_VALUE, accountMap.get(STORMPATH_PASSWORD)); } // custom data, just drop it in profile map - if (accountMap.containsKey("customData")) { - profileMap.putAll((Map) accountMap.get("customData")); + if (accountMap.containsKey(STORMPATH_CUSTOM_DATA)) { + profileMap.putAll(getPropertyMap(accountMap, STORMPATH_CUSTOM_DATA)); } return userMap; } - private void nullSafePut(Map map, String key, Object value) { + private static String getOktaHref(Map properties) { + + // _links.self.href -> href + Map linksMap = getPropertyMap(properties,OKTA_LINKS); + if (!Collections.isEmpty(linksMap)) { + Map self = getPropertyMap(linksMap, OKTA_SELF); + if (!Collections.isEmpty(self)) { + return (String) self.get(OKTA_HREF); + } + } + return null; + } + + private static String buildFullName(Object firstName, Object lastName) { + return (Objects.getDisplayString(firstName) + " " + Objects.getDisplayString(lastName)).trim(); + } + + private static Map getPropertyMap(Map map, String key) { + return (Map) map.get(key); + } + + private static void nullSafePut(Map map, String key, Object value) { if (value != null) { map.put(key, value); } } - private String fromUserStatus(Object userStatusRaw) { + private static String fromUserStatus(Object userStatusRaw) { UserStatus userStatus = UserStatus.valueOf(Objects.nullSafeToString(userStatusRaw)); AccountStatus accountStatus; @@ -213,7 +261,7 @@ private String fromUserStatus(Object userStatusRaw) { * @param keys keys to remove from original map * @return new map based on previous with all keys removed. */ - private Map trimMap(Map map, String... keys) { + private static Map trimMap(Map map, String... keys) { Map result = new LinkedHashMap<>(map); for (String key : keys) { @@ -222,4 +270,32 @@ private Map trimMap(Map map, String... keys) { return result; } + + public static Map toStormpathGroup(Map oktaGroup) { + + if (Collections.isEmpty(oktaGroup) || !oktaGroup.containsKey(OKTA_PROFILE)) { + return oktaGroup; + } + + Map stormpathGroup = new LinkedHashMap<>(); + + nullSafePut(stormpathGroup, STORMPATH_CREATED_AT, oktaGroup.get(OKTA_CREATED)); + nullSafePut(stormpathGroup, STORMPATH_MODIFIED_AT, oktaGroup.get(OKTA_LAST_UPDATED)); + + stormpathGroup.put(STORMPATH_STATUS, "ENABLED"); + + Map profile = getPropertyMap(oktaGroup, OKTA_PROFILE); + if (!Collections.isEmpty(profile)) { + nullSafePut(stormpathGroup, "name", profile.get("name")); + nullSafePut(stormpathGroup, "description", profile.get("description")); + } + + // _links.self.href -> href + nullSafePut(stormpathGroup, STORMPATH_HREF, "/api/v1/groups/" + oktaGroup.get("id")); + + stormpathGroup.put(STORMPATH_CUSTOM_DATA, new LinkedHashMap()); + + return stormpathGroup; + } + } diff --git a/impl/src/test/groovy/com/stormpath/sdk/impl/account/DefaultAccountLinkTest.groovy b/impl/src/test/groovy/com/stormpath/sdk/impl/account/DefaultAccountLinkTest.groovy index fb88ebd338..31f11db6bf 100644 --- a/impl/src/test/groovy/com/stormpath/sdk/impl/account/DefaultAccountLinkTest.groovy +++ b/impl/src/test/groovy/com/stormpath/sdk/impl/account/DefaultAccountLinkTest.groovy @@ -21,6 +21,7 @@ import com.stormpath.sdk.account.AccountLink import com.stormpath.sdk.impl.ds.InternalDataStore import com.stormpath.sdk.impl.resource.DateProperty import com.stormpath.sdk.impl.resource.ResourceReference +import org.easymock.IAnswer import org.testng.annotations.Test import java.text.DateFormat @@ -60,11 +61,21 @@ public class DefaultAccountLinkTest { AccountLink accountLink = new DefaultAccountLink(internalDataStore, properties) - expect(internalDataStore.instantiate(Account, properties.leftAccount)). - andReturn(new DefaultAccount(internalDataStore, properties.leftAccount)) - - expect(internalDataStore.instantiate(Account, properties.rightAccount)). - andReturn(new DefaultAccount(internalDataStore, properties.rightAccount)) + expect(internalDataStore.instantiate(Account, properties.leftAccount)).andAnswer( new IAnswer() { + @Override + Account answer() throws Throwable { + new DefaultAccount(internalDataStore, properties.leftAccount) + } + }) + expect(internalDataStore.getBaseUrl()).andReturn("https://api.stormpath.com/v1") + + expect(internalDataStore.instantiate(Account, properties.rightAccount)).andAnswer( new IAnswer() { + @Override + Account answer() throws Throwable { + new DefaultAccount(internalDataStore, properties.rightAccount) + } + }) + expect(internalDataStore.getBaseUrl()).andReturn("https://api.stormpath.com/v1") replay internalDataStore diff --git a/impl/src/test/groovy/com/stormpath/sdk/impl/account/DefaultAccountTest.groovy b/impl/src/test/groovy/com/stormpath/sdk/impl/account/DefaultAccountTest.groovy index fb97f15205..6ac17b4ae1 100644 --- a/impl/src/test/groovy/com/stormpath/sdk/impl/account/DefaultAccountTest.groovy +++ b/impl/src/test/groovy/com/stormpath/sdk/impl/account/DefaultAccountTest.groovy @@ -44,6 +44,7 @@ import com.stormpath.sdk.oauth.RefreshTokenList import com.stormpath.sdk.phone.PhoneList import com.stormpath.sdk.provider.ProviderData import com.stormpath.sdk.tenant.Tenant +import org.easymock.Capture import org.testng.annotations.Test import java.text.DateFormat @@ -112,30 +113,9 @@ class DefaultAccountTest { modifiedAt: "2015-02-01T12:00:00Z"] def internalDataStore = createStrictMock(InternalDataStore) - def defaultAccount = new DefaultAccount(internalDataStore, properties) - - assertFalse(defaultAccount.isPrintableProperty("password")) - assertNull(defaultAccount.getStatus()) - - defaultAccount = defaultAccount.setUsername("pacoman") - .setEmail("some@testmail.stormpath.com") - .setSurname("Smuk") - .setMiddleName("Ben") - .setGivenName("Mel") - .setStatus(AccountStatus.DISABLED) - .setPassword("superPass0rd") + def accountCapture = new Capture() - DateFormat df = new ISO8601DateFormat(); - - assertEquals(defaultAccount.getUsername(), "pacoman") - assertEquals(defaultAccount.getEmail(), "some@testmail.stormpath.com") - assertEquals(defaultAccount.getSurname(), "Smuk") - assertEquals(defaultAccount.getMiddleName(), "Ben") - assertEquals(defaultAccount.getGivenName(), "Mel") - assertEquals(defaultAccount.getStatus(), AccountStatus.DISABLED) - assertEquals(defaultAccount.getFullName(), "Mel Ben Smuk") - assertEquals(df.format(defaultAccount.getCreatedAt()), "2015-01-01T00:00:00Z") - assertEquals(df.format(defaultAccount.getModifiedAt()), "2015-02-01T12:00:00Z") + expect(internalDataStore.getBaseUrl()).andReturn("https://api.stormpath.com/v1") expect(internalDataStore.instantiate(EmailVerificationToken, properties.emailVerificationToken)). andReturn(new DefaultEmailVerificationToken(internalDataStore, properties.emailVerificationToken)) @@ -183,7 +163,7 @@ class DefaultAccountTest { expect(internalDataStore.getResource(properties.providerData.href, ProviderData.class, "providerId", IdentityProviderType.IDENTITY_PROVIDERDATA_CLASS_MAP)) .andReturn(new DefaultProviderData(internalDataStore, properties.providerData)) - expect(internalDataStore.delete(defaultAccount)) + expect(internalDataStore.delete(capture(accountCapture))) def groupMembership = new DefaultGroupMembership(internalDataStore) def group = createStrictMock(Group) @@ -199,6 +179,31 @@ class DefaultAccountTest { replay internalDataStore, groupCriteria, group, linkedAccountsCriteria, otherAccount, accountLinksCriteria + def defaultAccount = new DefaultAccount(internalDataStore, properties) + + assertFalse(defaultAccount.isPrintableProperty("password")) + assertNull(defaultAccount.getStatus()) + + defaultAccount = defaultAccount.setUsername("pacoman") + .setEmail("some@testmail.stormpath.com") + .setSurname("Smuk") + .setMiddleName("Ben") + .setGivenName("Mel") + .setStatus(AccountStatus.DISABLED) + .setPassword("superPass0rd") + + DateFormat df = new ISO8601DateFormat(); + + assertEquals(defaultAccount.getUsername(), "pacoman") + assertEquals(defaultAccount.getEmail(), "some@testmail.stormpath.com") + assertEquals(defaultAccount.getSurname(), "Smuk") + assertEquals(defaultAccount.getMiddleName(), "Ben") + assertEquals(defaultAccount.getGivenName(), "Mel") + assertEquals(defaultAccount.getStatus(), AccountStatus.DISABLED) + assertEquals(defaultAccount.getFullName(), "Mel Ben Smuk") + assertEquals(df.format(defaultAccount.getCreatedAt()), "2015-01-01T00:00:00Z") + assertEquals(df.format(defaultAccount.getModifiedAt()), "2015-02-01T12:00:00Z") + def resource = defaultAccount.getEmailVerificationToken() assertTrue(resource instanceof DefaultEmailVerificationToken && resource.getHref().equals(properties.emailVerificationToken.href)) @@ -258,6 +263,7 @@ class DefaultAccountTest { defaultAccount.link(otherAccount) verify internalDataStore, groupCriteria, group, linkedAccountsCriteria, otherAccount + assertEquals accountCapture.value, defaultAccount } @Test @@ -279,10 +285,11 @@ class DefaultAccountTest { ] - def internalDataStore = createStrictMock(InternalDataStore) - def defaultAccount = new DefaultAccount(internalDataStore, properties) + def internalDataStore = createMock(InternalDataStore) def mockGroup = createStrictMock(Group) + expect(internalDataStore.getBaseUrl()).andReturn("https://api.stormpath.com/v1") + expect(internalDataStore.instantiate(GroupList, properties.groups)).andReturn(new DefaultGroupList(internalDataStore, properties.groups)) expect(internalDataStore.instantiate(Group, groupValues)).andReturn(mockGroup) expect(mockGroup.getName()).andReturn(properties.groups.items.get(0).name) @@ -337,6 +344,9 @@ class DefaultAccountTest { replay internalDataStore, mockGroup + + def defaultAccount = new DefaultAccount(internalDataStore, properties) + assertFalse(defaultAccount.isMemberOfGroup(groupName.substring(0, groupName.length()-2) + "*")) //fooNa* assertFalse(defaultAccount.isMemberOfGroup("*" + groupName.toUpperCase() + "*")) //*FOONAME* assertFalse(defaultAccount.isMemberOfGroup("*" + groupName.substring(2, groupName.length()).toLowerCase())) //*oname @@ -442,6 +452,8 @@ class DefaultAccountTest { def map = createStrictMock(Map) def applicationCriteria = createStrictMock(ApplicationCriteria) + expect(internalDataStore.getBaseUrl()).andReturn("https://api.stormpath.com/v1").any() + //getApplications() expect(internalDataStore.instantiate(ApplicationList, properties.applications)).andReturn(applicationList) expect(applicationList.getHref()).andReturn(properties.applications.href) diff --git a/impl/src/test/groovy/com/stormpath/sdk/impl/account/DefaultPasswordResetTokenTest.groovy b/impl/src/test/groovy/com/stormpath/sdk/impl/account/DefaultPasswordResetTokenTest.groovy index c13c41ecc3..f1ca21bf6f 100644 --- a/impl/src/test/groovy/com/stormpath/sdk/impl/account/DefaultPasswordResetTokenTest.groovy +++ b/impl/src/test/groovy/com/stormpath/sdk/impl/account/DefaultPasswordResetTokenTest.groovy @@ -21,6 +21,8 @@ import com.stormpath.sdk.directory.Directory import com.stormpath.sdk.impl.ds.InternalDataStore import com.stormpath.sdk.impl.resource.ResourceReference import com.stormpath.sdk.impl.resource.StringProperty +import org.easymock.Capture +import org.easymock.IAnswer import org.testng.annotations.Test import static org.easymock.EasyMock.* @@ -34,12 +36,13 @@ class DefaultPasswordResetTokenTest { @Test void testAll() { - def internalDataStore = createStrictMock(InternalDataStore) + def internalDataStore = createMock(InternalDataStore) def resourceWithDS = new DefaultPasswordResetToken(internalDataStore) def resourceWithProps = new DefaultPasswordResetToken(internalDataStore, [href: "https://api.stormpath.com/v1/applications/WpM9nyZ2Tb67hbRvLk9KA/passwordResetTokens/j6HqguWPo98YXM2xmcOUShw", account: [href: "https://api.stormpath.com/v1/accounts/nfoweurj9824urnou"]]) + def accountCapture = new Capture() assertTrue(resourceWithDS instanceof DefaultPasswordResetToken && resourceWithProps instanceof DefaultPasswordResetToken) def pd = resourceWithProps.getPropertyDescriptors() @@ -54,9 +57,18 @@ class DefaultPasswordResetTokenTest { resourceWithDS.setEmail("some@testmail.stormpath.com") assertEquals(resourceWithDS.getEmail(), "some@testmail.stormpath.com") + expect(internalDataStore.getBaseUrl()).andReturn("https://api.stormpath.com/v1") + def innerProperties = [href: "https://api.stormpath.com/v1/accounts/nfoweurj9824urnou"] - def account = new DefaultAccount(internalDataStore, innerProperties) - expect(internalDataStore.instantiate(Account, innerProperties)).andReturn(account) + + expect(internalDataStore.instantiate(Account, innerProperties)).andAnswer(new IAnswer() { + @Override + Account answer() throws Throwable { + def account = new DefaultAccount(internalDataStore, innerProperties) + accountCapture.setValue(account) + return account + } + }) def accountStore = createStrictMock(Directory) def accountStoreHref = 'https://api.stormpath.com/v1/directories/dir123' @@ -70,7 +82,7 @@ class DefaultPasswordResetTokenTest { resourceWithDS.setAccountStore(accountStore) assertEquals(resourceWithDS.dirtyProperties.accountStore.href, accountStoreHref) - assertSame(resourceWithProps.account, account) + assertSame(resourceWithProps.account, accountCapture.value) verify internalDataStore, accountStore } diff --git a/impl/src/test/groovy/com/stormpath/sdk/impl/application/okta/DefaultAuthNResultTest.groovy b/impl/src/test/groovy/com/stormpath/sdk/impl/application/okta/DefaultAuthNResultTest.groovy new file mode 100644 index 0000000000..83bbed9e9a --- /dev/null +++ b/impl/src/test/groovy/com/stormpath/sdk/impl/application/okta/DefaultAuthNResultTest.groovy @@ -0,0 +1,36 @@ +package com.stormpath.sdk.impl.application.okta + + +import static org.easymock.EasyMock.* +import static org.hamcrest.Matchers.* +import static org.hamcrest.MatcherAssert.* + +import org.testng.annotations.Test + +/** + * Tests for {@link DefaultAuthNResult}. + */ +class DefaultAuthNResultTest { + + @Test + void basicParseTest() { + + def sessionToken = "a_session_token" + def userId = "a_user_id" + + Map map = [ + sessionToken: sessionToken, + "_embedded": [ + user: [ + id: userId + ] + ] + ] + + def underTest = new DefaultAuthNResult(null, map) + + assertThat underTest.sessionToken, equalTo(sessionToken) + assertThat underTest.userId, equalTo(userId) + + } +} diff --git a/impl/src/test/groovy/com/stormpath/sdk/impl/authc/OktaAuthNAuthenticatorTest.groovy b/impl/src/test/groovy/com/stormpath/sdk/impl/authc/OktaAuthNAuthenticatorTest.groovy new file mode 100644 index 0000000000..69d7e9dd9e --- /dev/null +++ b/impl/src/test/groovy/com/stormpath/sdk/impl/authc/OktaAuthNAuthenticatorTest.groovy @@ -0,0 +1,85 @@ +package com.stormpath.sdk.impl.authc + +import com.stormpath.sdk.account.Account +import com.stormpath.sdk.application.okta.OktaTokenRequest +import com.stormpath.sdk.application.okta.OktaTokenResponse +import com.stormpath.sdk.impl.application.okta.OktaSigningKeyResolver +import com.stormpath.sdk.impl.ds.InternalDataStore +import com.stormpath.sdk.impl.http.HttpHeaders +import com.stormpath.sdk.impl.http.MediaType +import io.jsonwebtoken.Claims +import io.jsonwebtoken.JwsHeader +import io.jsonwebtoken.Jwts +import io.jsonwebtoken.SignatureAlgorithm +import io.jsonwebtoken.impl.DefaultClaims +import io.jsonwebtoken.impl.TextCodec +import org.testng.annotations.Test + +import javax.crypto.spec.SecretKeySpec +import java.security.Key + +import static org.easymock.EasyMock.* +import static org.hamcrest.Matchers.* +import static org.hamcrest.MatcherAssert.* + +/** + * Tests for {@link DefaultOktaAuthNAuthenticator}. + */ +class OktaAuthNAuthenticatorTest { + + @Test + void authenticationTest() { + + def username = "joe.coder@example.com" + def password = "test_password" + def authRequest = new DefaultUsernamePasswordRequest(username, password.chars) + def key = "somekeyblablabla" + + def accessToken = Jwts.builder() + .setClaims(new DefaultClaims([ + scp: ["one", "two"], + uid: "011110" + ]) + .setSubject(username)) + .signWith(SignatureAlgorithm.HS512, key) + .compact() + + def mockDataStore = createMock(InternalDataStore) + def oktaTokenRequest = createMock(OktaTokenRequest) + def oktaTokenResponse = createMock(OktaTokenResponse) + def signingKeyResolver = createMock(OktaSigningKeyResolver) + def account = createMock(Account) + + expect(mockDataStore.instantiate(OktaTokenRequest)).andReturn(oktaTokenRequest) + expect(oktaTokenRequest.setPassword(password)).andReturn(oktaTokenRequest) + expect(oktaTokenRequest.setUsername(username)).andReturn(oktaTokenRequest) + expect(oktaTokenRequest.setGrantType("password")).andReturn(oktaTokenRequest) + expect(oktaTokenRequest.setScope("offline_access")).andReturn(oktaTokenRequest) + + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + expect(mockDataStore.create("/oauth2/v1/token", oktaTokenRequest, OktaTokenResponse, httpHeaders)).andReturn(oktaTokenResponse) + expect(oktaTokenResponse.getAccessToken()).andReturn(accessToken) + + // TODO: pull this out into own class + expect(mockDataStore.instantiate(OktaSigningKeyResolver)).andReturn(signingKeyResolver) + expect(signingKeyResolver.resolveSigningKey(anyObject(JwsHeader), anyObject(Claims))).andReturn(getKey(key)) + expect(mockDataStore.getResource("/api/v1/users/011110", Account)).andReturn(account) + + + replay mockDataStore, oktaTokenRequest, oktaTokenResponse, signingKeyResolver + + def authResult = new DefaultOktaAuthNAuthenticator(mockDataStore).authenticate(authRequest) + assertThat authResult.getHref(), nullValue() + assertThat authResult.getAccount(), sameInstance(account) + + verify mockDataStore, oktaTokenRequest, oktaTokenResponse, signingKeyResolver + } + + + private Key getKey(String key, SignatureAlgorithm algorithm = SignatureAlgorithm.HS512) { + return new SecretKeySpec(TextCodec.BASE64.decode(key), algorithm.name()) + } + +} + diff --git a/impl/src/test/groovy/com/stormpath/sdk/impl/ds/DefaultDataStoreTest.groovy b/impl/src/test/groovy/com/stormpath/sdk/impl/ds/DefaultDataStoreTest.groovy index 19c9aa8a51..af79876eb0 100644 --- a/impl/src/test/groovy/com/stormpath/sdk/impl/ds/DefaultDataStoreTest.groovy +++ b/impl/src/test/groovy/com/stormpath/sdk/impl/ds/DefaultDataStoreTest.groovy @@ -343,7 +343,7 @@ class DefaultDataStoreTest { def response = createStrictMock(Response) def apiKeyCredentials = createStrictMock(ApiKeyCredentials) def apiKeyResolver = createStrictMock(ApiKeyResolver) - def baseUrlResolver = createStrictMock(BaseUrlResolver) + def baseUrlResolver = createMock(BaseUrlResolver) def providerResponseMap = [href: "https://api.stormpath.com/v1/directories/5fgF3o89Ph5nbJzY6EVSct/provider", createdAt: "2014-04-01T22:05:25.661Z", @@ -381,6 +381,7 @@ class DefaultDataStoreTest { def childIdProperty = "providerId" def map = IdentityProviderType.IDENTITY_PROVIDER_CLASS_MAP + expect(baseUrlResolver.getBaseUrl()).andReturn("https://api.stormpath.com").times(2) expect(requestExecutor.executeRequest(anyObject(DefaultRequest))).andReturn(response) expect(response.isError()).andReturn(false) expect(response.hasBody()).andReturn(true) @@ -391,7 +392,7 @@ class DefaultDataStoreTest { expect(response.getBody()).andReturn(providerAccountResponseIS) expect(response.getHttpStatus()).andReturn(201) - replay(requestExecutor, response) + replay(requestExecutor, response, baseUrlResolver) def cache = Caches.newCacheManager() .withDefaultTimeToIdle(1, TimeUnit.HOURS) @@ -410,7 +411,7 @@ class DefaultDataStoreTest { assertEquals(defaultDataStore.cacheManager.caches.size(), 1) - verify(requestExecutor, response) + verify(requestExecutor, response, baseUrlResolver) } //@since 1.0.0 diff --git a/impl/src/test/groovy/com/stormpath/sdk/impl/ds/JacksonMapMarshallerTest.groovy b/impl/src/test/groovy/com/stormpath/sdk/impl/ds/JacksonMapMarshallerTest.groovy index b6622ec123..a460fa3f45 100644 --- a/impl/src/test/groovy/com/stormpath/sdk/impl/ds/JacksonMapMarshallerTest.groovy +++ b/impl/src/test/groovy/com/stormpath/sdk/impl/ds/JacksonMapMarshallerTest.groovy @@ -99,7 +99,7 @@ class JacksonMapMarshallerTest { def objectMapper = createMock(ObjectMapper) def inputStream = createMock(InputStream) - expect(objectMapper.readValue((InputStream)eq(inputStream), (TypeReference)isA(TypeReference))) + expect(objectMapper.readValue((InputStream)eq(inputStream), (Class)eq(Object.class))) .andAnswer(new IAnswer() { @Override Map answer() throws Throwable { diff --git a/impl/src/test/groovy/com/stormpath/sdk/impl/oauth/DefaultAccessTokenTest.groovy b/impl/src/test/groovy/com/stormpath/sdk/impl/oauth/DefaultAccessTokenTest.groovy index ea3b4389d1..6845ac08d4 100644 --- a/impl/src/test/groovy/com/stormpath/sdk/impl/oauth/DefaultAccessTokenTest.groovy +++ b/impl/src/test/groovy/com/stormpath/sdk/impl/oauth/DefaultAccessTokenTest.groovy @@ -30,6 +30,7 @@ import com.stormpath.sdk.oauth.AccessToken import com.stormpath.sdk.tenant.Tenant import io.jsonwebtoken.Jwts import io.jsonwebtoken.SignatureAlgorithm +import org.easymock.IAnswer import org.testng.annotations.Test import java.text.DateFormat @@ -123,13 +124,22 @@ class DefaultAccessTokenTest { expect(apiKey.getSecret()).andReturn(secret) - expect(internalDataStore.getApiKey()).andReturn(apiKey) + expect(internalDataStore.getApiKey()).andReturn(apiKey) expect(internalDataStore.instantiate(Tenant, properties.tenant)).andReturn(new DefaultTenant(internalDataStore, properties.tenant)) - expect(internalDataStore.instantiate(Account, properties.account)).andReturn(new DefaultAccount(internalDataStore, properties.account)) + expect(internalDataStore.instantiate(Account, properties.account)).andAnswer( new IAnswer() { + @Override + Account answer() throws Throwable { + new DefaultAccount(internalDataStore, properties.account) + } + }) + expect(internalDataStore.getBaseUrl()).andReturn("https://api.stormpath.com/v1") + expect(internalDataStore.instantiate(Application, properties.application)).andReturn(new DefaultApplication(internalDataStore, properties.application)) replay internalDataStore, apiKey + + def defaultAccessToken = new DefaultAccessToken(internalDataStore, properties) assertEquals(defaultAccessToken.getHref(), properties.href) diff --git a/impl/src/test/groovy/com/stormpath/sdk/impl/oauth/DefaultOAuthPolicyTest.groovy b/impl/src/test/groovy/com/stormpath/sdk/impl/oauth/DefaultOAuthPolicyTest.groovy index 786afe5635..2c0e1df531 100644 --- a/impl/src/test/groovy/com/stormpath/sdk/impl/oauth/DefaultOAuthPolicyTest.groovy +++ b/impl/src/test/groovy/com/stormpath/sdk/impl/oauth/DefaultOAuthPolicyTest.groovy @@ -40,7 +40,7 @@ class DefaultOAuthPolicyTest { OAuthPolicy passwordPolicy = new DefaultOAuthPolicy(createStrictMock(InternalDataStore)) def propertyDescriptors = passwordPolicy.getPropertyDescriptors() - assertEquals(propertyDescriptors.size(), 9) + assertEquals(propertyDescriptors.size(), 5) assertTrue(propertyDescriptors.get("accessTokenTtl") instanceof StringProperty) assertTrue(propertyDescriptors.get("refreshTokenTtl") instanceof StringProperty) diff --git a/impl/src/test/groovy/com/stormpath/sdk/impl/oauth/DefaultOAuthRefreshTokenRequestAuthenticationBuilderTest.groovy b/impl/src/test/groovy/com/stormpath/sdk/impl/oauth/DefaultOAuthRefreshOktaTokenRequestAuthenticationBuilderTest.groovy similarity index 95% rename from impl/src/test/groovy/com/stormpath/sdk/impl/oauth/DefaultOAuthRefreshTokenRequestAuthenticationBuilderTest.groovy rename to impl/src/test/groovy/com/stormpath/sdk/impl/oauth/DefaultOAuthRefreshOktaTokenRequestAuthenticationBuilderTest.groovy index ac034af879..8a21855970 100644 --- a/impl/src/test/groovy/com/stormpath/sdk/impl/oauth/DefaultOAuthRefreshTokenRequestAuthenticationBuilderTest.groovy +++ b/impl/src/test/groovy/com/stormpath/sdk/impl/oauth/DefaultOAuthRefreshOktaTokenRequestAuthenticationBuilderTest.groovy @@ -26,7 +26,7 @@ import static org.testng.Assert.fail * * @since 1.0.RC7 */ -class DefaultOAuthRefreshTokenRequestAuthenticationBuilderTest { +class DefaultOAuthRefreshOktaTokenRequestAuthenticationBuilderTest { @Test void testError(){ diff --git a/impl/src/test/groovy/com/stormpath/sdk/impl/oauth/DefaultRefreshTokenTest.groovy b/impl/src/test/groovy/com/stormpath/sdk/impl/oauth/DefaultRefreshTokenTest.groovy index 1077a2780d..4db991f146 100644 --- a/impl/src/test/groovy/com/stormpath/sdk/impl/oauth/DefaultRefreshTokenTest.groovy +++ b/impl/src/test/groovy/com/stormpath/sdk/impl/oauth/DefaultRefreshTokenTest.groovy @@ -26,6 +26,7 @@ import com.stormpath.sdk.impl.resource.ResourceReference import com.stormpath.sdk.impl.resource.StringProperty import com.stormpath.sdk.impl.tenant.DefaultTenant import com.stormpath.sdk.tenant.Tenant +import org.easymock.IAnswer import org.testng.annotations.Test import java.text.DateFormat @@ -78,7 +79,13 @@ class DefaultRefreshTokenTest { assertEquals(df.format(defaultRefreshToken.getCreatedAt()), "2015-01-01T00:00:00Z", properties.created_at) expect(internalDataStore.instantiate(Tenant, properties.tenant)).andReturn(new DefaultTenant(internalDataStore, properties.tenant)) - expect(internalDataStore.instantiate(Account, properties.account)).andReturn(new DefaultAccount(internalDataStore, properties.account)) + expect(internalDataStore.instantiate(Account, properties.account)).andAnswer(new IAnswer() { + @Override + Account answer() throws Throwable { + return new DefaultAccount(internalDataStore, properties.account) + } + }) + expect(internalDataStore.getBaseUrl()).andReturn("https://api.stormpath.com/v1") expect(internalDataStore.instantiate(Application, properties.application)).andReturn(new DefaultApplication(internalDataStore, properties.application)) replay internalDataStore diff --git a/impl/src/test/groovy/com/stormpath/sdk/impl/okta/OktaUserAccountConverterTest.groovy b/impl/src/test/groovy/com/stormpath/sdk/impl/okta/OktaUserAccountConverterTest.groovy index 25e91732c8..80310bc567 100644 --- a/impl/src/test/groovy/com/stormpath/sdk/impl/okta/OktaUserAccountConverterTest.groovy +++ b/impl/src/test/groovy/com/stormpath/sdk/impl/okta/OktaUserAccountConverterTest.groovy @@ -71,6 +71,7 @@ class OktaUserAccountConverterTest { // create Account object def internalDataStore = createStrictMock(InternalDataStore) def customDataPropsCapture = new Capture() + expect(internalDataStore.getBaseUrl()).andReturn("https://api.example.com") expect(internalDataStore.instantiate(anyObject(CustomData), capture(customDataPropsCapture))).andAnswer(new IAnswer() { @Override CustomData answer() throws Throwable { diff --git a/pom.xml b/pom.xml index 1d0d6a37ee..59fc403732 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ com.stormpath.sdk stormpath-sdk-root - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT pom Stormpath Java SDK diff --git a/tck.sh b/tck.sh index c8ebee1325..804d8c23d7 100755 --- a/tck.sh +++ b/tck.sh @@ -44,6 +44,8 @@ case "$OPTION" in ;; run) SCRIPT_DIR=$(cd "$(dirname "$0")"; pwd) + CI_DIR_CONTENTS=`ls $SCRIPT_DIR/ci` + echo "CI DIR Contents:" if [ -e "$SCRIPT_DIR/ci/stormpath_env.sh" ]; then source ${SCRIPT_DIR}/ci/stormpath_env.sh export STORMPATH_APPLICATION_HREF=$STORMPATH_TEST_APPLICATION_HREF diff --git a/tutorials/pom.xml b/tutorials/pom.xml index 4094c0cae3..1bf1a11ea9 100644 --- a/tutorials/pom.xml +++ b/tutorials/pom.xml @@ -19,7 +19,7 @@ com.stormpath.sdk stormpath-sdk-root - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml diff --git a/tutorials/spring-boot/00-the-basics/pom.xml b/tutorials/spring-boot/00-the-basics/pom.xml index a05e85d889..8a0604cfed 100644 --- a/tutorials/spring-boot/00-the-basics/pom.xml +++ b/tutorials/spring-boot/00-the-basics/pom.xml @@ -21,14 +21,14 @@ com.stormpath.sdk stormpath-sdk-tutorials-spring-boot - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-tutorials-spring-boot-default-the-basics - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Tutorials :: Spring Boot Default :: The Basics A simple Spring Boot Web MVC application with out-of-the-box login and self-service screens! diff --git a/tutorials/spring-boot/01-some-access-controls/pom.xml b/tutorials/spring-boot/01-some-access-controls/pom.xml index 74f882bc0a..10b1396bc0 100644 --- a/tutorials/spring-boot/01-some-access-controls/pom.xml +++ b/tutorials/spring-boot/01-some-access-controls/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-tutorials-spring-boot - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-tutorials-spring-boot-default-some-access-controls - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Tutorials :: Spring Boot Default :: Some Access Controls A simple Spring Boot Web MVC application with out-of-the-box login and self-service screens! diff --git a/tutorials/spring-boot/02-spring-security-ftw/pom.xml b/tutorials/spring-boot/02-spring-security-ftw/pom.xml index f6096dd71e..39be1835eb 100644 --- a/tutorials/spring-boot/02-spring-security-ftw/pom.xml +++ b/tutorials/spring-boot/02-spring-security-ftw/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-tutorials-spring-boot - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-tutorials-spring-boot-default-spring-security-ftw - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Tutorials :: Spring Boot Default :: Spring Security FTW! A simple Spring Boot Web MVC application with out-of-the-box login and self-service screens! diff --git a/tutorials/spring-boot/03-spring-security-refined/pom.xml b/tutorials/spring-boot/03-spring-security-refined/pom.xml index eb156f4c7a..1e67a18ea7 100644 --- a/tutorials/spring-boot/03-spring-security-refined/pom.xml +++ b/tutorials/spring-boot/03-spring-security-refined/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-tutorials-spring-boot - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-tutorials-spring-boot-default-spring-security-refined - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Tutorials :: Spring Boot Default :: Spring Security Refined A simple Spring Boot Web MVC application with out-of-the-box login and self-service screens! diff --git a/tutorials/spring-boot/04-a-finer-grain-of-control/pom.xml b/tutorials/spring-boot/04-a-finer-grain-of-control/pom.xml index c36c417766..02c0c184d9 100644 --- a/tutorials/spring-boot/04-a-finer-grain-of-control/pom.xml +++ b/tutorials/spring-boot/04-a-finer-grain-of-control/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-tutorials-spring-boot - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-tutorials-spring-boot-default-a-finer-grain-of-control - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Tutorials :: Spring Boot Default :: A Finer Grain of Control A simple Spring Boot Web MVC application with out-of-the-box login and self-service screens! diff --git a/tutorials/spring-boot/05-token-management/pom.xml b/tutorials/spring-boot/05-token-management/pom.xml index a4d040763c..2f42def2c1 100644 --- a/tutorials/spring-boot/05-token-management/pom.xml +++ b/tutorials/spring-boot/05-token-management/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-tutorials-spring-boot - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-tutorials-spring-boot-default-token-management - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Tutorials :: Spring Boot Default :: Token Management A simple Spring Boot Web MVC application with out-of-the-box login and self-service screens! diff --git a/tutorials/spring-boot/pom.xml b/tutorials/spring-boot/pom.xml index 788816b9d9..0488267b52 100644 --- a/tutorials/spring-boot/pom.xml +++ b/tutorials/spring-boot/pom.xml @@ -19,7 +19,7 @@ com.stormpath.sdk stormpath-sdk-tutorials - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml diff --git a/tutorials/spring/00-the-basics/pom.xml b/tutorials/spring/00-the-basics/pom.xml index b09db91e6f..d09fe629f7 100644 --- a/tutorials/spring/00-the-basics/pom.xml +++ b/tutorials/spring/00-the-basics/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-tutorials-spring - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-tutorials-spring-security-webmvc-the-basics - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Tutorials :: Spring Security WebMVC :: The Basics A simple Spring Security Web MVC application with out-of-the-box login and self-service screens! diff --git a/tutorials/spring/01-some-access-controls/pom.xml b/tutorials/spring/01-some-access-controls/pom.xml index be4eea6158..ccbc52d0a7 100644 --- a/tutorials/spring/01-some-access-controls/pom.xml +++ b/tutorials/spring/01-some-access-controls/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-tutorials-spring - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-tutorials-spring-security-webmvc-some-access-controls - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Tutorials :: Spring Security WebMVC :: Some Access Controls A simple Spring Security Web MVC application with out-of-the-box login and self-service screens! diff --git a/tutorials/spring/02-spring-security-ftw/pom.xml b/tutorials/spring/02-spring-security-ftw/pom.xml index b62558d698..d3a4b4ed01 100644 --- a/tutorials/spring/02-spring-security-ftw/pom.xml +++ b/tutorials/spring/02-spring-security-ftw/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-tutorials-spring - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-tutorials-spring-security-webmvc-spring-security-ftw - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Tutorials :: Spring Security WebMVC :: Spring Security For The Win A simple Spring Security Web MVC application with out-of-the-box login and self-service screens! diff --git a/tutorials/spring/03-spring-security-refined/pom.xml b/tutorials/spring/03-spring-security-refined/pom.xml index 184fa5362b..de7e49fcd9 100644 --- a/tutorials/spring/03-spring-security-refined/pom.xml +++ b/tutorials/spring/03-spring-security-refined/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-tutorials-spring - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-tutorials-spring-security-webmvc-spring-security-refined - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Tutorials :: Spring Security WebMVC :: Spring Security Refined A simple Spring Security Web MVC application with out-of-the-box login and self-service screens! diff --git a/tutorials/spring/04-a-finer-grain-of-control/pom.xml b/tutorials/spring/04-a-finer-grain-of-control/pom.xml index 1be63d1200..530c6a75fb 100644 --- a/tutorials/spring/04-a-finer-grain-of-control/pom.xml +++ b/tutorials/spring/04-a-finer-grain-of-control/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-tutorials-spring - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-tutorials-spring-security-webmvc-a-finer-grain-of-control - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Tutorials :: Spring Security WebMVC :: A Finer Grain Of Control A simple Spring Security Web MVC application with out-of-the-box login and self-service screens! diff --git a/tutorials/spring/05-token-management/pom.xml b/tutorials/spring/05-token-management/pom.xml index 5012174988..0cfe34298d 100644 --- a/tutorials/spring/05-token-management/pom.xml +++ b/tutorials/spring/05-token-management/pom.xml @@ -21,13 +21,13 @@ com.stormpath.sdk stormpath-sdk-tutorials-spring - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml com.stormpath.spring stormpath-sdk-tutorials-spring-security-webmvc-token-management - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT Stormpath Java SDK :: Tutorials :: Spring Security WebMVC :: Token Management A simple Spring Security Web MVC application with out-of-the-box login and self-service screens! diff --git a/tutorials/spring/pom.xml b/tutorials/spring/pom.xml index ff01559707..73420b2423 100644 --- a/tutorials/spring/pom.xml +++ b/tutorials/spring/pom.xml @@ -19,7 +19,7 @@ com.stormpath.sdk stormpath-sdk-tutorials - 1.6.0-SNAPSHOT + 2.0.0-okta-alpha-SNAPSHOT ../pom.xml