From 1c53776506d1556a3ea157261fd15396084e81d4 Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 8 Mar 2019 14:44:59 +0100 Subject: [PATCH 01/23] Update gradle and android gradle plugin --- build.gradle | 4 +++- gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 5966013..a9f27ab 100644 --- a/build.gradle +++ b/build.gradle @@ -3,9 +3,10 @@ buildscript { repositories { jcenter() + google() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.3' + classpath 'com.android.tools.build:gradle:3.3.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -15,5 +16,6 @@ buildscript { allprojects { repositories { jcenter() + google() } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2778cb3..a82e409 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip From b574e8a03a91d3081fdf5c769cb6553ab0f4a6ea Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 8 Mar 2019 14:46:19 +0100 Subject: [PATCH 02/23] From compile to implementation gradle --- library/build.gradle | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/build.gradle b/library/build.gradle index c5beafc..f97ae07 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -24,15 +24,15 @@ repositories { dependencies { // Rx - compile 'io.reactivex:rxjava:1.1.1' + implementation 'io.reactivex:rxjava:1.1.1' // GSON - compile 'com.google.code.gson:gson:2.8.0' + implementation 'com.google.code.gson:gson:2.8.0' // Testing - testCompile "cglib:cglib:2.2" - testCompile 'junit:junit:4.12' - testCompile('org.spockframework:spock-core:1.0-groovy-2.4') { + testImplementation "cglib:cglib:2.2" + testImplementation 'junit:junit:4.12' + testImplementation('org.spockframework:spock-core:1.0-groovy-2.4') { exclude group: 'junit' } } \ No newline at end of file From c0b30be68a589c4ee510fe039f40a749ff03c1b2 Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 8 Mar 2019 14:46:32 +0100 Subject: [PATCH 03/23] Add rxjava 2 to dependencies --- library/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/library/build.gradle b/library/build.gradle index f97ae07..70e93b9 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -25,6 +25,7 @@ dependencies { // Rx implementation 'io.reactivex:rxjava:1.1.1' + implementation 'io.reactivex.rxjava2:rxjava:2.2.7' // GSON implementation 'com.google.code.gson:gson:2.8.0' From 384ad29672723f91d80492e928b58354b7a328e6 Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 8 Mar 2019 16:22:00 +0100 Subject: [PATCH 04/23] Update files to rxjava2 --- .../heimdall2/OAuth2AccessToken.java | 90 ++++++++++++ .../heimdall2/OAuth2AccessTokenManager.java | 107 ++++++++++++++ .../heimdall2/OAuth2AccessTokenStorage.java | 41 ++++++ .../grants/OAuth2AuthorizationCodeGrant.java | 138 ++++++++++++++++++ .../grants/OAuth2ClientCredentialsGrant.java | 27 ++++ .../heimdall2/grants/OAuth2Grant.java | 21 +++ .../heimdall2/grants/OAuth2ImplicitGrant.java | 49 +++++++ .../grants/OAuth2RefreshAccessTokenGrant.java | 34 +++++ ...ResourceOwnerPasswordCredentialsGrant.java | 40 +++++ 9 files changed, 547 insertions(+) create mode 100644 library/src/main/java/de/rheinfabrik/heimdall2/OAuth2AccessToken.java create mode 100644 library/src/main/java/de/rheinfabrik/heimdall2/OAuth2AccessTokenManager.java create mode 100644 library/src/main/java/de/rheinfabrik/heimdall2/OAuth2AccessTokenStorage.java create mode 100644 library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2AuthorizationCodeGrant.java create mode 100644 library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2ClientCredentialsGrant.java create mode 100644 library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2Grant.java create mode 100644 library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2ImplicitGrant.java create mode 100644 library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2RefreshAccessTokenGrant.java create mode 100644 library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2ResourceOwnerPasswordCredentialsGrant.java diff --git a/library/src/main/java/de/rheinfabrik/heimdall2/OAuth2AccessToken.java b/library/src/main/java/de/rheinfabrik/heimdall2/OAuth2AccessToken.java new file mode 100644 index 0000000..cfa9ec2 --- /dev/null +++ b/library/src/main/java/de/rheinfabrik/heimdall2/OAuth2AccessToken.java @@ -0,0 +1,90 @@ +package de.rheinfabrik.heimdall2; + +import com.google.gson.annotations.SerializedName; +import java.io.Serializable; +import java.util.Calendar; + +/** + * OAuth2AccessToken represents an OAuth2AccessToken response as described in https://tools.ietf.org/html/rfc6749#section-5.1. + */ +public class OAuth2AccessToken implements Serializable { + + // Properties + + /** + * REQUIRED + * The type of the token issued as described in https://tools.ietf.org/html/rfc6749#section-7.1. + * Value is case insensitive. + */ + @SerializedName("token_type") + public String tokenType; + + /** + * REQUIRED + * The access token issued by the authorization server. + */ + @SerializedName("access_token") + public String accessToken; + + /** + * OPTIONAL + * The refresh token, which can be used to obtain new + * access tokens using the same authorization grant as described + * in https://tools.ietf.org/html/rfc6749#section-6. + */ + @SerializedName("refresh_token") + public String refreshToken; + + /** + * RECOMMENDED + * The lifetime in seconds of the access token. For + * example, the value "3600" denotes that the access token will + * expire in one hour from the time the response was generated. + * If omitted, the authorization server SHOULD provide the + * expiration time via other means or document the default value. + */ + @SerializedName("expires_in") + public Integer expiresIn; + + /** + * The expiration date used by Heimdall. + */ + @SerializedName("heimdall_expiration_date") + public Calendar expirationDate; + + // Public Api + + /** + * This method returns whether the access token is expired or not. + * + * @return True if expired. Otherwise false. + */ + public boolean isExpired() { + return expirationDate != null && Calendar.getInstance().after(expirationDate); + } + + // Object + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (!(other instanceof OAuth2AccessToken)) { + return false; + } + + OAuth2AccessToken otherToken = (OAuth2AccessToken) other; + + return accessToken.equals(otherToken.accessToken) && tokenType.equals(otherToken.tokenType); + } + + @Override + public int hashCode() { + int result = tokenType.hashCode(); + result = 31 * result + accessToken.hashCode(); + + return result; + } +} diff --git a/library/src/main/java/de/rheinfabrik/heimdall2/OAuth2AccessTokenManager.java b/library/src/main/java/de/rheinfabrik/heimdall2/OAuth2AccessTokenManager.java new file mode 100644 index 0000000..144da0a --- /dev/null +++ b/library/src/main/java/de/rheinfabrik/heimdall2/OAuth2AccessTokenManager.java @@ -0,0 +1,107 @@ +package de.rheinfabrik.heimdall2; + +import de.rheinfabrik.heimdall2.grants.OAuth2Grant; +import de.rheinfabrik.heimdall2.grants.OAuth2RefreshAccessTokenGrant; +import io.reactivex.Single; +import java.util.Calendar; + +/** + * The all-seeing and all-hearing guardian sentry of your application who + * stands on the rainbow bridge to handle all your access tokens needs! + * + * @param The token type. + */ +public class OAuth2AccessTokenManager { + + // Members + + private final OAuth2AccessTokenStorage mStorage; + + // Constructor + + /** + * The designated constructor. + * + * @param storage The OAuth2AccessTokenStorage used to store and retrieve the access token. + */ + public OAuth2AccessTokenManager(OAuth2AccessTokenStorage storage) { + super(); + + if (storage == null) { + throw new IllegalArgumentException("Storage MUST NOT be null."); + } + + mStorage = storage; + } + + // Public API + + /** + * Returns the underlying storage. + * + * @return - An OAuth2AccessTokenStorage. + */ + public OAuth2AccessTokenStorage getStorage() { + return mStorage; + } + + /** + * Grants a new access token using the given OAuth2 grant. + * + * @param grant A class implementing the OAuth2Grant interface. + * @return - An observable emitting the granted access token. + */ + public Single grantNewAccessToken(OAuth2Grant grant) { + return grantNewAccessToken(grant, Calendar.getInstance()); + } + + /** + * Grants a new access token using the given OAuth2 grant. + * + * @param grant A class implementing the OAuth2Grant interface. + * @param calendar A calendar instance used to calculate the expiration date of the token. + * @return - An observable emitting the granted access token. + */ + public Single grantNewAccessToken(OAuth2Grant grant, Calendar calendar) { + if (grant == null) { + throw new IllegalArgumentException("Grant MUST NOT be null."); + } + + return grant.grantNewAccessToken() + .doOnSuccess(accessToken -> { + if (accessToken.expiresIn != null) { + Calendar expirationDate = (Calendar) calendar.clone(); + expirationDate.add(Calendar.SECOND, accessToken.expiresIn); + accessToken.expirationDate = expirationDate; + } + mStorage.storeAccessToken(accessToken); + }).cache(); + } + + /** + * Returns an Observable emitting an unexpired access token. + * NOTE: In order to work, Heimdall needs an access token which has a refresh_token and an + * expires_in field. + * + * @param refreshAccessTokenGrant The refresh grant that will be used if the access token is expired. + * @return - An Observable emitting an unexpired access token. + */ + public Single getValidAccessToken(final OAuth2RefreshAccessTokenGrant refreshAccessTokenGrant) { + if (refreshAccessTokenGrant == null) { + throw new IllegalArgumentException("RefreshAccessTokenGrant MUST NOT be null."); + } + + return mStorage.getStoredAccessToken() + .flatMap(accessToken -> { + if (accessToken == null) { + return Single.error(new IllegalStateException("No access token found.")); + } else if (accessToken.isExpired()) { + refreshAccessTokenGrant.refreshToken = accessToken.refreshToken; + + return grantNewAccessToken(refreshAccessTokenGrant); + } else { + return Single.just(accessToken); + } + }); + } +} diff --git a/library/src/main/java/de/rheinfabrik/heimdall2/OAuth2AccessTokenStorage.java b/library/src/main/java/de/rheinfabrik/heimdall2/OAuth2AccessTokenStorage.java new file mode 100644 index 0000000..670d149 --- /dev/null +++ b/library/src/main/java/de/rheinfabrik/heimdall2/OAuth2AccessTokenStorage.java @@ -0,0 +1,41 @@ +package de.rheinfabrik.heimdall2; + + +import io.reactivex.Single; + +/** + * Interface used to define how to store and retrieve a stored access token. + * + * @param The access token type. + */ +public interface OAuth2AccessTokenStorage { + + // Public API + + /** + * Queries the stored access token. + * + * @return - An Observable emitting the stored access token. + */ + Single getStoredAccessToken(); + + /** + * Stores the given access token. + * + * @param token The access token which will be stored. + */ + void storeAccessToken(TAccessToken token); + + /** + * Checks whether there is or is not an access token + * + * @return - An Observable emitting true or false based on whether there is or is not an + * access token. + */ + Single hasAccessToken(); + + /** + * Removes the stored access token. + */ + void removeAccessToken(); +} diff --git a/library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2AuthorizationCodeGrant.java b/library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2AuthorizationCodeGrant.java new file mode 100644 index 0000000..0df0196 --- /dev/null +++ b/library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2AuthorizationCodeGrant.java @@ -0,0 +1,138 @@ +package de.rheinfabrik.heimdall2.grants; + +import de.rheinfabrik.heimdall2.OAuth2AccessToken; +import io.reactivex.Observable; +import io.reactivex.Single; +import io.reactivex.subjects.BehaviorSubject; +import io.reactivex.subjects.PublishSubject; +import java.net.URL; +import java.net.URLDecoder; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * Class representing the Authorization Code Grant as described in https://tools.ietf.org/html/rfc6749#section-4.1. + * + * @param The access token type. + */ +public abstract class OAuth2AuthorizationCodeGrant implements OAuth2Grant { + + // Constants + + /** + * REQUIRED + * The "response_type" which MUST be "code". + */ + public final static String RESPONSE_TYPE = "code"; + + /** + * REQUIRED + * The "grant_type" which MUST be "authorization_code". + */ + public final static String GRANT_TYPE = "authorization_code"; + + // Properties + + /** + * REQUIRED + * The client identifier as described in https://tools.ietf.org/html/rfc6749#section-2.2. + */ + public String clientId; + + /** + * OPTIONAL + * As described in https://tools.ietf.org/html/rfc6749#section-3.1.2. + */ + public String redirectUri; + + /** + * OPTIONAL + * The scope of the access request as described in https://tools.ietf.org/html/rfc6749#section-3.3. + */ + public String scope; + + /** + * RECOMMENDED + * An opaque value used by the client to maintain + * state between the request and callback. The authorization + * server includes this value when redirecting the user-agent back + * to the client. The parameter SHOULD be used for preventing + * cross-site request forgery as described in https://tools.ietf.org/html/rfc6749#section-10.12. + */ + public String state; + + // Public Api + + /** + * Observable emitting the authorization Uri. + */ + public final Observable authorizationUri() { + return mAuthorizationUrlSubject; + } + + /** + * Command you should send a value to whenever an url in e.g. your web view has been loaded. + */ + public final PublishSubject onUrlLoadedCommand = PublishSubject.create(); + + // Abstract Api + + /** + * Called when the grant needs the authorization url. + */ + public abstract URL buildAuthorizationUrl(); + + /** + * Called when the grant was able to grab the code and it wants to exchange it for an access token. + */ + public abstract Observable exchangeTokenUsingCode(String code); + + // Members + + private final BehaviorSubject mAuthorizationUrlSubject = BehaviorSubject.create(); + + // OAuth2AccessToken + + @Override + public Single grantNewAccessToken() { + mAuthorizationUrlSubject.onNext(buildAuthorizationUrl()); + + return onUrlLoadedCommand + .map(uri -> { + List values = getQueryParameters(uri).get(RESPONSE_TYPE); + if (values != null && values.size() >= 1) { + return values.get(0); + } + + return null; + }) + .filter(code -> code != null) + .take(1) + .retry() + .concatMap(this::exchangeTokenUsingCode) + .singleOrError(); + } + + // Private + + private static Map> getQueryParameters(URL url) { + final Map> query_pairs = new LinkedHashMap<>(); + final String[] pairs = url.getQuery().split("&"); + for (String pair : pairs) { + final int idx = pair.indexOf("="); + + try { + final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair; + if (!query_pairs.containsKey(key)) { + query_pairs.put(key, new LinkedList<>()); + } + final String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null; + query_pairs.get(key).add(value); + } catch (Exception ignored) {} + } + + return query_pairs; + } +} diff --git a/library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2ClientCredentialsGrant.java b/library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2ClientCredentialsGrant.java new file mode 100644 index 0000000..d0a0a6c --- /dev/null +++ b/library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2ClientCredentialsGrant.java @@ -0,0 +1,27 @@ +package de.rheinfabrik.heimdall2.grants; + +import de.rheinfabrik.heimdall2.OAuth2AccessToken; + +/** + * Class representing the Client Credentials Grant as described in https://tools.ietf.org/html/rfc6749#section-4.4. + * + * @param The access token type. + */ +public abstract class OAuth2ClientCredentialsGrant implements OAuth2Grant { + + // Constants + + /** + * REQUIRED + * The OAuth2 "grant_type". + */ + public static final String GRANT_TYPE = "client_credentials"; + + // Properties + + /** + * OPTIONAL + * The scope of the access request as described in https://tools.ietf.org/html/rfc6749#section-3.3. + */ + public String scope; +} diff --git a/library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2Grant.java b/library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2Grant.java new file mode 100644 index 0000000..62a3a7b --- /dev/null +++ b/library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2Grant.java @@ -0,0 +1,21 @@ +package de.rheinfabrik.heimdall2.grants; + +import de.rheinfabrik.heimdall2.OAuth2AccessToken; +import io.reactivex.Single; + +/** + * Interface describing an OAuth2 Grant as described in https://tools.ietf.org/html/rfc6749#page-23. + * + * @param The access token type. + */ +public interface OAuth2Grant { + + // Abstract Api + + /** + * Performs the actual request to grant a new access token. + * + * @return - An Observable emitting the granted access token. + */ + Single grantNewAccessToken(); +} diff --git a/library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2ImplicitGrant.java b/library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2ImplicitGrant.java new file mode 100644 index 0000000..b3fc6b4 --- /dev/null +++ b/library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2ImplicitGrant.java @@ -0,0 +1,49 @@ +package de.rheinfabrik.heimdall2.grants; + +import de.rheinfabrik.heimdall2.OAuth2AccessToken; + +/** + * Class representing the Implicit Grant as described in https://tools.ietf.org/html/rfc6749#section-4.2. + * + * @param The access token type. + */ +public abstract class OAuth2ImplicitGrant implements OAuth2Grant { + + // Constants + + /** + * REQUIRED + * The "response_type" which MUST be "token". + */ + public final static String RESPONSE_TYPE = "token"; + + // Properties + + /** + * REQUIRED + * The client identifier as described in https://tools.ietf.org/html/rfc6749#section-2.2. + */ + public String clientId; + + /** + * OPTIONAL + * As described in https://tools.ietf.org/html/rfc6749#section-3.1.2. + */ + public String redirectUri; + + /** + * OPTIONAL + * The scope of the access request as described in https://tools.ietf.org/html/rfc6749#section-3.3. + */ + public String scope; + + /** + * RECOMMENDED + * An opaque value used by the client to maintain + * state between the request and callback. The authorization + * server includes this value when redirecting the user-agent back + * to the client. The parameter SHOULD be used for preventing + * cross-site request forgery as described in https://tools.ietf.org/html/rfc6749#section-10.12. + */ + public String state; +} diff --git a/library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2RefreshAccessTokenGrant.java b/library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2RefreshAccessTokenGrant.java new file mode 100644 index 0000000..4b1d9db --- /dev/null +++ b/library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2RefreshAccessTokenGrant.java @@ -0,0 +1,34 @@ +package de.rheinfabrik.heimdall2.grants; + +import de.rheinfabrik.heimdall2.OAuth2AccessToken; + +/** + * Class representing the Refreshing Access Token Grant as described in https://tools.ietf.org/html/rfc6749#section-6. + * + * @param The access token type. + */ +public abstract class OAuth2RefreshAccessTokenGrant implements OAuth2Grant { + + // Constants + + /** + * REQUIRED + * The OAuth2 "grant_type". + */ + public static final String GRANT_TYPE = "refresh_token"; + + // Properties + + /** + * REQUIRED + * The "refresh_token" issued to the client. + */ + public String refreshToken; + + /** + * OPTIONAL + * The "scope" of the access request as described by here (https://tools.ietf.org/html/rfc6749#section-3.3). + */ + public String scope; + +} diff --git a/library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2ResourceOwnerPasswordCredentialsGrant.java b/library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2ResourceOwnerPasswordCredentialsGrant.java new file mode 100644 index 0000000..bd293d1 --- /dev/null +++ b/library/src/main/java/de/rheinfabrik/heimdall2/grants/OAuth2ResourceOwnerPasswordCredentialsGrant.java @@ -0,0 +1,40 @@ +package de.rheinfabrik.heimdall2.grants; + +import de.rheinfabrik.heimdall2.OAuth2AccessToken; + +/** + * Class representing the Resource Owner Password Credentials Grant as described in https://tools.ietf.org/html/rfc6749#section-4.3. + * + * @param The access token type. + */ +public abstract class OAuth2ResourceOwnerPasswordCredentialsGrant implements OAuth2Grant { + + // Constants + + /** + * REQUIRED + * The OAuth2 "grant_type". + */ + public static final String GRANT_TYPE = "password"; + + // Properties + + /** + * REQUIRED + * The resource owner "username". + */ + public String username; + + /** + * REQUIRED + * The resource owner "password". + */ + public String password; + + /** + * OPTIONAL + * The "scope" of the access request as described by here (https://tools.ietf.org/html/rfc6749#section-3.3). + */ + public String scope; + +} From 9c7eec4161a262da30cf99d135bb1dd913d1a9d4 Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 8 Mar 2019 16:32:32 +0100 Subject: [PATCH 05/23] Update tests to use new package with rx updates --- .../OAuth2AccessTokenManagerSpecs.groovy | 16 ++++++++-------- .../OAuth2AccessTokenSpecs.groovy | 2 +- .../OAuth2AuthorizationCodeGrantSpecs.groovy | 2 +- .../OAuth2ClientCredentialsGrantSpecs.groovy | 2 +- .../grants/OAuth2ImplicitGrantSpecs.groovy | 2 +- .../OAuth2RefreshAccessTokenGrantSpecs.groovy | 2 +- ...urceOwnerPasswordCredentialsGrantSpecs.groovy | 2 +- .../{heimdall => heimdall2}/EmptyTestClass.java | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) rename library/src/test/groovy/de/rheinfabrik/{heimdall => heimdall2}/OAuth2AccessTokenManagerSpecs.groovy (95%) rename library/src/test/groovy/de/rheinfabrik/{heimdall => heimdall2}/OAuth2AccessTokenSpecs.groovy (99%) rename library/src/test/groovy/de/rheinfabrik/{heimdall => heimdall2}/grants/OAuth2AuthorizationCodeGrantSpecs.groovy (94%) rename library/src/test/groovy/de/rheinfabrik/{heimdall => heimdall2}/grants/OAuth2ClientCredentialsGrantSpecs.groovy (91%) rename library/src/test/groovy/de/rheinfabrik/{heimdall => heimdall2}/grants/OAuth2ImplicitGrantSpecs.groovy (91%) rename library/src/test/groovy/de/rheinfabrik/{heimdall => heimdall2}/grants/OAuth2RefreshAccessTokenGrantSpecs.groovy (91%) rename library/src/test/groovy/de/rheinfabrik/{heimdall => heimdall2}/grants/OAuth2ResourceOwnerPasswordCredentialsGrantSpecs.groovy (92%) rename library/src/test/java/de/rheinfabrik/{heimdall => heimdall2}/EmptyTestClass.java (88%) diff --git a/library/src/test/groovy/de/rheinfabrik/heimdall/OAuth2AccessTokenManagerSpecs.groovy b/library/src/test/groovy/de/rheinfabrik/heimdall2/OAuth2AccessTokenManagerSpecs.groovy similarity index 95% rename from library/src/test/groovy/de/rheinfabrik/heimdall/OAuth2AccessTokenManagerSpecs.groovy rename to library/src/test/groovy/de/rheinfabrik/heimdall2/OAuth2AccessTokenManagerSpecs.groovy index 43111c1..069ffd7 100644 --- a/library/src/test/groovy/de/rheinfabrik/heimdall/OAuth2AccessTokenManagerSpecs.groovy +++ b/library/src/test/groovy/de/rheinfabrik/heimdall2/OAuth2AccessTokenManagerSpecs.groovy @@ -1,8 +1,8 @@ -package de.rheinfabrik.heimdall +package de.rheinfabrik.heimdall2 -import de.rheinfabrik.heimdall.grants.OAuth2Grant -import de.rheinfabrik.heimdall.grants.OAuth2RefreshAccessTokenGrant -import rx.Single +import de.rheinfabrik.heimdall2.grants.OAuth2Grant +import de.rheinfabrik.heimdall2.grants.OAuth2RefreshAccessTokenGrant +import io.reactivex.Single import spock.lang.Specification import spock.lang.Title @@ -62,7 +62,7 @@ class OAuth2AccessTokenManagerGrantNewAccessTokenSpecs extends Specification { Calendar calendar = Calendar.getInstance() when: "I ask for a new access token" - OAuth2AccessToken newToken = tokenManager.grantNewAccessToken(grant, calendar).toBlocking().value() + OAuth2AccessToken newToken = tokenManager.grantNewAccessToken(grant, calendar).blockingGet() then: "The access token should have the correct expiration date" newToken.expirationDate.timeInMillis == calendar.getTimeInMillis() + 3000 @@ -82,7 +82,7 @@ class OAuth2AccessTokenManagerGrantNewAccessTokenSpecs extends Specification { OAuth2AccessTokenManager tokenManager = new OAuth2AccessTokenManager(Mock(OAuth2AccessTokenStorage)) when: "I ask for a new access token" - OAuth2AccessToken newToken = tokenManager.grantNewAccessToken(grant).toBlocking().value() + OAuth2AccessToken newToken = tokenManager.grantNewAccessToken(grant).blockingGet() then: "The access token should have the NO expiration date" newToken.expirationDate == null @@ -104,7 +104,7 @@ class OAuth2AccessTokenManagerGrantNewAccessTokenSpecs extends Specification { OAuth2AccessTokenManager tokenManager = new OAuth2AccessTokenManager(storage) when: "I ask for a new access token" - tokenManager.grantNewAccessToken(grant).toBlocking().value() + tokenManager.grantNewAccessToken(grant).blockingGet() then: "The storage is asked to save the token" 1 * storage.storeAccessToken(accessToken) @@ -170,7 +170,7 @@ class OAuth2AccessTokenManagerGetValidAccessTokenSpecs extends Specification { OAuth2AccessTokenManager tokenManager = new OAuth2AccessTokenManager(storage) when: "I ask for a valid access token" - OAuth2AccessToken validToken = tokenManager.getValidAccessToken(grant).toBlocking().value() + OAuth2AccessToken validToken = tokenManager.getValidAccessToken(grant).blockingGet() then: "The I receive the non-expired token" validToken == accessToken diff --git a/library/src/test/groovy/de/rheinfabrik/heimdall/OAuth2AccessTokenSpecs.groovy b/library/src/test/groovy/de/rheinfabrik/heimdall2/OAuth2AccessTokenSpecs.groovy similarity index 99% rename from library/src/test/groovy/de/rheinfabrik/heimdall/OAuth2AccessTokenSpecs.groovy rename to library/src/test/groovy/de/rheinfabrik/heimdall2/OAuth2AccessTokenSpecs.groovy index 952ef01..2120717 100644 --- a/library/src/test/groovy/de/rheinfabrik/heimdall/OAuth2AccessTokenSpecs.groovy +++ b/library/src/test/groovy/de/rheinfabrik/heimdall2/OAuth2AccessTokenSpecs.groovy @@ -1,4 +1,4 @@ -package de.rheinfabrik.heimdall +package de.rheinfabrik.heimdall2 import com.google.gson.Gson import spock.lang.Specification diff --git a/library/src/test/groovy/de/rheinfabrik/heimdall/grants/OAuth2AuthorizationCodeGrantSpecs.groovy b/library/src/test/groovy/de/rheinfabrik/heimdall2/grants/OAuth2AuthorizationCodeGrantSpecs.groovy similarity index 94% rename from library/src/test/groovy/de/rheinfabrik/heimdall/grants/OAuth2AuthorizationCodeGrantSpecs.groovy rename to library/src/test/groovy/de/rheinfabrik/heimdall2/grants/OAuth2AuthorizationCodeGrantSpecs.groovy index b9cfdd1..f2effea 100644 --- a/library/src/test/groovy/de/rheinfabrik/heimdall/grants/OAuth2AuthorizationCodeGrantSpecs.groovy +++ b/library/src/test/groovy/de/rheinfabrik/heimdall2/grants/OAuth2AuthorizationCodeGrantSpecs.groovy @@ -1,4 +1,4 @@ -package de.rheinfabrik.heimdall.grants +package de.rheinfabrik.heimdall2.grants import spock.lang.Specification import spock.lang.Title diff --git a/library/src/test/groovy/de/rheinfabrik/heimdall/grants/OAuth2ClientCredentialsGrantSpecs.groovy b/library/src/test/groovy/de/rheinfabrik/heimdall2/grants/OAuth2ClientCredentialsGrantSpecs.groovy similarity index 91% rename from library/src/test/groovy/de/rheinfabrik/heimdall/grants/OAuth2ClientCredentialsGrantSpecs.groovy rename to library/src/test/groovy/de/rheinfabrik/heimdall2/grants/OAuth2ClientCredentialsGrantSpecs.groovy index 82ff1a8..7cf782d 100644 --- a/library/src/test/groovy/de/rheinfabrik/heimdall/grants/OAuth2ClientCredentialsGrantSpecs.groovy +++ b/library/src/test/groovy/de/rheinfabrik/heimdall2/grants/OAuth2ClientCredentialsGrantSpecs.groovy @@ -1,4 +1,4 @@ -package de.rheinfabrik.heimdall.grants +package de.rheinfabrik.heimdall2.grants import spock.lang.Specification import spock.lang.Title diff --git a/library/src/test/groovy/de/rheinfabrik/heimdall/grants/OAuth2ImplicitGrantSpecs.groovy b/library/src/test/groovy/de/rheinfabrik/heimdall2/grants/OAuth2ImplicitGrantSpecs.groovy similarity index 91% rename from library/src/test/groovy/de/rheinfabrik/heimdall/grants/OAuth2ImplicitGrantSpecs.groovy rename to library/src/test/groovy/de/rheinfabrik/heimdall2/grants/OAuth2ImplicitGrantSpecs.groovy index 1949fd3..bdea92e 100644 --- a/library/src/test/groovy/de/rheinfabrik/heimdall/grants/OAuth2ImplicitGrantSpecs.groovy +++ b/library/src/test/groovy/de/rheinfabrik/heimdall2/grants/OAuth2ImplicitGrantSpecs.groovy @@ -1,4 +1,4 @@ -package de.rheinfabrik.heimdall.grants +package de.rheinfabrik.heimdall2.grants import spock.lang.Specification import spock.lang.Title diff --git a/library/src/test/groovy/de/rheinfabrik/heimdall/grants/OAuth2RefreshAccessTokenGrantSpecs.groovy b/library/src/test/groovy/de/rheinfabrik/heimdall2/grants/OAuth2RefreshAccessTokenGrantSpecs.groovy similarity index 91% rename from library/src/test/groovy/de/rheinfabrik/heimdall/grants/OAuth2RefreshAccessTokenGrantSpecs.groovy rename to library/src/test/groovy/de/rheinfabrik/heimdall2/grants/OAuth2RefreshAccessTokenGrantSpecs.groovy index 0a45c4a..40ccad9 100644 --- a/library/src/test/groovy/de/rheinfabrik/heimdall/grants/OAuth2RefreshAccessTokenGrantSpecs.groovy +++ b/library/src/test/groovy/de/rheinfabrik/heimdall2/grants/OAuth2RefreshAccessTokenGrantSpecs.groovy @@ -1,4 +1,4 @@ -package de.rheinfabrik.heimdall.grants +package de.rheinfabrik.heimdall2.grants import spock.lang.Specification import spock.lang.Title diff --git a/library/src/test/groovy/de/rheinfabrik/heimdall/grants/OAuth2ResourceOwnerPasswordCredentialsGrantSpecs.groovy b/library/src/test/groovy/de/rheinfabrik/heimdall2/grants/OAuth2ResourceOwnerPasswordCredentialsGrantSpecs.groovy similarity index 92% rename from library/src/test/groovy/de/rheinfabrik/heimdall/grants/OAuth2ResourceOwnerPasswordCredentialsGrantSpecs.groovy rename to library/src/test/groovy/de/rheinfabrik/heimdall2/grants/OAuth2ResourceOwnerPasswordCredentialsGrantSpecs.groovy index ee77302..426c7bd 100644 --- a/library/src/test/groovy/de/rheinfabrik/heimdall/grants/OAuth2ResourceOwnerPasswordCredentialsGrantSpecs.groovy +++ b/library/src/test/groovy/de/rheinfabrik/heimdall2/grants/OAuth2ResourceOwnerPasswordCredentialsGrantSpecs.groovy @@ -1,4 +1,4 @@ -package de.rheinfabrik.heimdall.grants +package de.rheinfabrik.heimdall2.grants import spock.lang.Specification import spock.lang.Title diff --git a/library/src/test/java/de/rheinfabrik/heimdall/EmptyTestClass.java b/library/src/test/java/de/rheinfabrik/heimdall2/EmptyTestClass.java similarity index 88% rename from library/src/test/java/de/rheinfabrik/heimdall/EmptyTestClass.java rename to library/src/test/java/de/rheinfabrik/heimdall2/EmptyTestClass.java index 961a39f..eb4ca9c 100644 --- a/library/src/test/java/de/rheinfabrik/heimdall/EmptyTestClass.java +++ b/library/src/test/java/de/rheinfabrik/heimdall2/EmptyTestClass.java @@ -1,4 +1,4 @@ -package de.rheinfabrik.heimdall; +package de.rheinfabrik.heimdall2; public class EmptyTestClass { // This is an empty class. It exists only because the gradle compileTestGroovy From 58fd9d2c5b0a3494a41737722bc2afbe0133e09e Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 8 Mar 2019 16:39:24 +0100 Subject: [PATCH 06/23] =?UTF-8?q?Missing=20new=20line=20=F0=9F=98=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- library/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/build.gradle b/library/build.gradle index 70e93b9..1364c78 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -36,4 +36,4 @@ dependencies { testImplementation('org.spockframework:spock-core:1.0-groovy-2.4') { exclude group: 'junit' } -} \ No newline at end of file +} From 41fac623cfb70ca2d0752281815fa59238f29f55 Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 8 Mar 2019 16:53:19 +0100 Subject: [PATCH 07/23] Remove rxjava1 --- library/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/library/build.gradle b/library/build.gradle index 1364c78..86a9c85 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -24,7 +24,6 @@ repositories { dependencies { // Rx - implementation 'io.reactivex:rxjava:1.1.1' implementation 'io.reactivex.rxjava2:rxjava:2.2.7' // GSON From b4d1bf8dd332d76cfdcb809d7d358b7c762e7dca Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 8 Mar 2019 16:53:46 +0100 Subject: [PATCH 08/23] Update gson --- library/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/build.gradle b/library/build.gradle index 86a9c85..7a774b7 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -27,7 +27,7 @@ dependencies { implementation 'io.reactivex.rxjava2:rxjava:2.2.7' // GSON - implementation 'com.google.code.gson:gson:2.8.0' + implementation 'com.google.code.gson:gson:2.8.5' // Testing testImplementation "cglib:cglib:2.2" From 2f9dac0a6fdcffbd548817a113e8c2a5cf2857e9 Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 8 Mar 2019 16:54:10 +0100 Subject: [PATCH 09/23] Remove old rxjava 1 support --- .../heimdall/OAuth2AccessToken.java | 91 ------------ .../heimdall/OAuth2AccessTokenManager.java | 110 -------------- .../heimdall/OAuth2AccessTokenStorage.java | 40 ----- .../grants/OAuth2AuthorizationCodeGrant.java | 140 ------------------ .../grants/OAuth2ClientCredentialsGrant.java | 27 ---- .../heimdall/grants/OAuth2Grant.java | 21 --- .../heimdall/grants/OAuth2ImplicitGrant.java | 49 ------ .../grants/OAuth2RefreshAccessTokenGrant.java | 34 ----- ...ResourceOwnerPasswordCredentialsGrant.java | 40 ----- 9 files changed, 552 deletions(-) delete mode 100644 library/src/main/java/de/rheinfabrik/heimdall/OAuth2AccessToken.java delete mode 100644 library/src/main/java/de/rheinfabrik/heimdall/OAuth2AccessTokenManager.java delete mode 100644 library/src/main/java/de/rheinfabrik/heimdall/OAuth2AccessTokenStorage.java delete mode 100644 library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2AuthorizationCodeGrant.java delete mode 100644 library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2ClientCredentialsGrant.java delete mode 100644 library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2Grant.java delete mode 100644 library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2ImplicitGrant.java delete mode 100644 library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2RefreshAccessTokenGrant.java delete mode 100644 library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2ResourceOwnerPasswordCredentialsGrant.java diff --git a/library/src/main/java/de/rheinfabrik/heimdall/OAuth2AccessToken.java b/library/src/main/java/de/rheinfabrik/heimdall/OAuth2AccessToken.java deleted file mode 100644 index 7b3f6ba..0000000 --- a/library/src/main/java/de/rheinfabrik/heimdall/OAuth2AccessToken.java +++ /dev/null @@ -1,91 +0,0 @@ -package de.rheinfabrik.heimdall; - -import com.google.gson.annotations.SerializedName; - -import java.io.Serializable; -import java.util.Calendar; - -/** - * OAuth2AccessToken represents an OAuth2AccessToken response as described in https://tools.ietf.org/html/rfc6749#section-5.1. - */ -public class OAuth2AccessToken implements Serializable { - - // Properties - - /** - * REQUIRED - * The type of the token issued as described in https://tools.ietf.org/html/rfc6749#section-7.1. - * Value is case insensitive. - */ - @SerializedName("token_type") - public String tokenType; - - /** - * REQUIRED - * The access token issued by the authorization server. - */ - @SerializedName("access_token") - public String accessToken; - - /** - * OPTIONAL - * The refresh token, which can be used to obtain new - * access tokens using the same authorization grant as described - * in https://tools.ietf.org/html/rfc6749#section-6. - */ - @SerializedName("refresh_token") - public String refreshToken; - - /** - * RECOMMENDED - * The lifetime in seconds of the access token. For - * example, the value "3600" denotes that the access token will - * expire in one hour from the time the response was generated. - * If omitted, the authorization server SHOULD provide the - * expiration time via other means or document the default value. - */ - @SerializedName("expires_in") - public Integer expiresIn; - - /** - * The expiration date used by Heimdall. - */ - @SerializedName("heimdall_expiration_date") - public Calendar expirationDate; - - // Public Api - - /** - * This method returns whether the access token is expired or not. - * - * @return True if expired. Otherwise false. - */ - public boolean isExpired() { - return expirationDate != null && Calendar.getInstance().after(expirationDate); - } - - // Object - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - - if (!(other instanceof OAuth2AccessToken)) { - return false; - } - - OAuth2AccessToken otherToken = (OAuth2AccessToken) other; - - return accessToken.equals(otherToken.accessToken) && tokenType.equals(otherToken.tokenType); - } - - @Override - public int hashCode() { - int result = tokenType.hashCode(); - result = 31 * result + accessToken.hashCode(); - - return result; - } -} diff --git a/library/src/main/java/de/rheinfabrik/heimdall/OAuth2AccessTokenManager.java b/library/src/main/java/de/rheinfabrik/heimdall/OAuth2AccessTokenManager.java deleted file mode 100644 index b8c214b..0000000 --- a/library/src/main/java/de/rheinfabrik/heimdall/OAuth2AccessTokenManager.java +++ /dev/null @@ -1,110 +0,0 @@ -package de.rheinfabrik.heimdall; - -import java.util.Calendar; - -import de.rheinfabrik.heimdall.grants.OAuth2Grant; -import de.rheinfabrik.heimdall.grants.OAuth2RefreshAccessTokenGrant; -import rx.Single; - -import static rx.Single.error; - -/** - * The all-seeing and all-hearing guardian sentry of your application who - * stands on the rainbow bridge to handle all your access tokens needs! - * - * @param The token type. - */ -public class OAuth2AccessTokenManager { - - // Members - - private final OAuth2AccessTokenStorage mStorage; - - // Constructor - - /** - * The designated constructor. - * - * @param storage The OAuth2AccessTokenStorage used to store and retrieve the access token. - */ - public OAuth2AccessTokenManager(OAuth2AccessTokenStorage storage) { - super(); - - if (storage == null) { - throw new IllegalArgumentException("Storage MUST NOT be null."); - } - - mStorage = storage; - } - - // Public API - - /** - * Returns the underlying storage. - * - * @return - An OAuth2AccessTokenStorage. - */ - public OAuth2AccessTokenStorage getStorage() { - return mStorage; - } - - /** - * Grants a new access token using the given OAuth2 grant. - * - * @param grant A class implementing the OAuth2Grant interface. - * @return - An observable emitting the granted access token. - */ - public Single grantNewAccessToken(OAuth2Grant grant) { - return grantNewAccessToken(grant, Calendar.getInstance()); - } - - /** - * Grants a new access token using the given OAuth2 grant. - * - * @param grant A class implementing the OAuth2Grant interface. - * @param calendar A calendar instance used to calculate the expiration date of the token. - * @return - An observable emitting the granted access token. - */ - public Single grantNewAccessToken(OAuth2Grant grant, Calendar calendar) { - if (grant == null) { - throw new IllegalArgumentException("Grant MUST NOT be null."); - } - - return grant.grantNewAccessToken() - .doOnSuccess(accessToken -> { - if (accessToken.expiresIn != null) { - Calendar expirationDate = (Calendar) calendar.clone(); - expirationDate.add(Calendar.SECOND, accessToken.expiresIn); - accessToken.expirationDate = expirationDate; - } - mStorage.storeAccessToken(accessToken); - }).toObservable().cache().toSingle(); - } - - /** - * Returns an Observable emitting an unexpired access token. - * NOTE: In order to work, Heimdall needs an access token which has a refresh_token and an - * expires_in field. - * - * @param refreshAccessTokenGrant The refresh grant that will be used if the access token is expired. - * @return - An Observable emitting an unexpired access token. - */ - public Single getValidAccessToken(final OAuth2RefreshAccessTokenGrant refreshAccessTokenGrant) { - if (refreshAccessTokenGrant == null) { - throw new IllegalArgumentException("RefreshAccessTokenGrant MUST NOT be null."); - } - - return mStorage.getStoredAccessToken() - .flatMap(accessToken -> { - if (accessToken == null) { - return error(new IllegalStateException("No access token found.")); - } else if (accessToken.isExpired()) { - refreshAccessTokenGrant.refreshToken = accessToken.refreshToken; - - return grantNewAccessToken(refreshAccessTokenGrant); - } else { - return Single.just(accessToken); - } - }); - } -} diff --git a/library/src/main/java/de/rheinfabrik/heimdall/OAuth2AccessTokenStorage.java b/library/src/main/java/de/rheinfabrik/heimdall/OAuth2AccessTokenStorage.java deleted file mode 100644 index 3b531da..0000000 --- a/library/src/main/java/de/rheinfabrik/heimdall/OAuth2AccessTokenStorage.java +++ /dev/null @@ -1,40 +0,0 @@ -package de.rheinfabrik.heimdall; - -import rx.Single; - -/** - * Interface used to define how to store and retrieve a stored access token. - * - * @param The access token type. - */ -public interface OAuth2AccessTokenStorage { - - // Public API - - /** - * Queries the stored access token. - * - * @return - An Observable emitting the stored access token. - */ - Single getStoredAccessToken(); - - /** - * Stores the given access token. - * - * @param token The access token which will be stored. - */ - void storeAccessToken(TAccessToken token); - - /** - * Checks whether there is or is not an access token - * - * @return - An Observable emitting true or false based on whether there is or is not an - * access token. - */ - Single hasAccessToken(); - - /** - * Removes the stored access token. - */ - void removeAccessToken(); -} diff --git a/library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2AuthorizationCodeGrant.java b/library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2AuthorizationCodeGrant.java deleted file mode 100644 index a7b38b9..0000000 --- a/library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2AuthorizationCodeGrant.java +++ /dev/null @@ -1,140 +0,0 @@ -package de.rheinfabrik.heimdall.grants; - -import java.io.UnsupportedEncodingException; -import java.net.URL; -import java.net.URLDecoder; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import de.rheinfabrik.heimdall.OAuth2AccessToken; -import rx.Observable; -import rx.Single; -import rx.subjects.BehaviorSubject; -import rx.subjects.PublishSubject; - -/** - * Class representing the Authorization Code Grant as described in https://tools.ietf.org/html/rfc6749#section-4.1. - * - * @param The access token type. - */ -public abstract class OAuth2AuthorizationCodeGrant implements OAuth2Grant { - - // Constants - - /** - * REQUIRED - * The "response_type" which MUST be "code". - */ - public final static String RESPONSE_TYPE = "code"; - - /** - * REQUIRED - * The "grant_type" which MUST be "authorization_code". - */ - public final static String GRANT_TYPE = "authorization_code"; - - // Properties - - /** - * REQUIRED - * The client identifier as described in https://tools.ietf.org/html/rfc6749#section-2.2. - */ - public String clientId; - - /** - * OPTIONAL - * As described in https://tools.ietf.org/html/rfc6749#section-3.1.2. - */ - public String redirectUri; - - /** - * OPTIONAL - * The scope of the access request as described in https://tools.ietf.org/html/rfc6749#section-3.3. - */ - public String scope; - - /** - * RECOMMENDED - * An opaque value used by the client to maintain - * state between the request and callback. The authorization - * server includes this value when redirecting the user-agent back - * to the client. The parameter SHOULD be used for preventing - * cross-site request forgery as described in https://tools.ietf.org/html/rfc6749#section-10.12. - */ - public String state; - - // Public Api - - /** - * Observable emitting the authorization Uri. - */ - public final Observable authorizationUri() { - return mAuthorizationUrlSubject.asObservable(); - } - - /** - * Command you should send a value to whenever an url in e.g. your web view has been loaded. - */ - public final PublishSubject onUrlLoadedCommand = PublishSubject.create(); - - // Abstract Api - - /** - * Called when the grant needs the authorization url. - */ - public abstract URL buildAuthorizationUrl(); - - /** - * Called when the grant was able to grab the code and it wants to exchange it for an access token. - */ - public abstract Observable exchangeTokenUsingCode(String code); - - // Members - - private final BehaviorSubject mAuthorizationUrlSubject = BehaviorSubject.create(); - - // OAuth2AccessToken - - @Override - public Single grantNewAccessToken() { - mAuthorizationUrlSubject.onNext(buildAuthorizationUrl()); - - return onUrlLoadedCommand - .map(uri -> { - List values = getQueryParameters(uri).get(RESPONSE_TYPE); - if (values != null && values.size() >= 1) { - return values.get(0); - } - - return null; - }) - .filter(code -> code != null) - .take(1) - .retry() - .concatMap(this::exchangeTokenUsingCode) - .toSingle(); - } - - // Private - - private static Map> getQueryParameters(URL url) { - final Map> query_pairs = new LinkedHashMap<>(); - final String[] pairs = url.getQuery().split("&"); - for (String pair : pairs) { - final int idx = pair.indexOf("="); - - try { - final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair; - if (!query_pairs.containsKey(key)) { - query_pairs.put(key, new LinkedList<>()); - } - final String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null; - query_pairs.get(key).add(value); - } catch (Exception ignored) {} - } - - return query_pairs; - } -} diff --git a/library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2ClientCredentialsGrant.java b/library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2ClientCredentialsGrant.java deleted file mode 100644 index 3089806..0000000 --- a/library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2ClientCredentialsGrant.java +++ /dev/null @@ -1,27 +0,0 @@ -package de.rheinfabrik.heimdall.grants; - -import de.rheinfabrik.heimdall.OAuth2AccessToken; - -/** - * Class representing the Client Credentials Grant as described in https://tools.ietf.org/html/rfc6749#section-4.4. - * - * @param The access token type. - */ -public abstract class OAuth2ClientCredentialsGrant implements OAuth2Grant { - - // Constants - - /** - * REQUIRED - * The OAuth2 "grant_type". - */ - public static final String GRANT_TYPE = "client_credentials"; - - // Properties - - /** - * OPTIONAL - * The scope of the access request as described in https://tools.ietf.org/html/rfc6749#section-3.3. - */ - public String scope; -} diff --git a/library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2Grant.java b/library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2Grant.java deleted file mode 100644 index 4c6f8b8..0000000 --- a/library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2Grant.java +++ /dev/null @@ -1,21 +0,0 @@ -package de.rheinfabrik.heimdall.grants; - -import de.rheinfabrik.heimdall.OAuth2AccessToken; -import rx.Single; - -/** - * Interface describing an OAuth2 Grant as described in https://tools.ietf.org/html/rfc6749#page-23. - * - * @param The access token type. - */ -public interface OAuth2Grant { - - // Abstract Api - - /** - * Performs the actual request to grant a new access token. - * - * @return - An Observable emitting the granted access token. - */ - Single grantNewAccessToken(); -} diff --git a/library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2ImplicitGrant.java b/library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2ImplicitGrant.java deleted file mode 100644 index 748a4f6..0000000 --- a/library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2ImplicitGrant.java +++ /dev/null @@ -1,49 +0,0 @@ -package de.rheinfabrik.heimdall.grants; - -import de.rheinfabrik.heimdall.OAuth2AccessToken; - -/** - * Class representing the Implicit Grant as described in https://tools.ietf.org/html/rfc6749#section-4.2. - * - * @param The access token type. - */ -public abstract class OAuth2ImplicitGrant implements OAuth2Grant { - - // Constants - - /** - * REQUIRED - * The "response_type" which MUST be "token". - */ - public final static String RESPONSE_TYPE = "token"; - - // Properties - - /** - * REQUIRED - * The client identifier as described in https://tools.ietf.org/html/rfc6749#section-2.2. - */ - public String clientId; - - /** - * OPTIONAL - * As described in https://tools.ietf.org/html/rfc6749#section-3.1.2. - */ - public String redirectUri; - - /** - * OPTIONAL - * The scope of the access request as described in https://tools.ietf.org/html/rfc6749#section-3.3. - */ - public String scope; - - /** - * RECOMMENDED - * An opaque value used by the client to maintain - * state between the request and callback. The authorization - * server includes this value when redirecting the user-agent back - * to the client. The parameter SHOULD be used for preventing - * cross-site request forgery as described in https://tools.ietf.org/html/rfc6749#section-10.12. - */ - public String state; -} diff --git a/library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2RefreshAccessTokenGrant.java b/library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2RefreshAccessTokenGrant.java deleted file mode 100644 index a3b030a..0000000 --- a/library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2RefreshAccessTokenGrant.java +++ /dev/null @@ -1,34 +0,0 @@ -package de.rheinfabrik.heimdall.grants; - -import de.rheinfabrik.heimdall.OAuth2AccessToken; - -/** - * Class representing the Refreshing Access Token Grant as described in https://tools.ietf.org/html/rfc6749#section-6. - * - * @param The access token type. - */ -public abstract class OAuth2RefreshAccessTokenGrant implements OAuth2Grant { - - // Constants - - /** - * REQUIRED - * The OAuth2 "grant_type". - */ - public static final String GRANT_TYPE = "refresh_token"; - - // Properties - - /** - * REQUIRED - * The "refresh_token" issued to the client. - */ - public String refreshToken; - - /** - * OPTIONAL - * The "scope" of the access request as described by here (https://tools.ietf.org/html/rfc6749#section-3.3). - */ - public String scope; - -} diff --git a/library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2ResourceOwnerPasswordCredentialsGrant.java b/library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2ResourceOwnerPasswordCredentialsGrant.java deleted file mode 100644 index 43c8015..0000000 --- a/library/src/main/java/de/rheinfabrik/heimdall/grants/OAuth2ResourceOwnerPasswordCredentialsGrant.java +++ /dev/null @@ -1,40 +0,0 @@ -package de.rheinfabrik.heimdall.grants; - -import de.rheinfabrik.heimdall.OAuth2AccessToken; - -/** - * Class representing the Resource Owner Password Credentials Grant as described in https://tools.ietf.org/html/rfc6749#section-4.3. - * - * @param The access token type. - */ -public abstract class OAuth2ResourceOwnerPasswordCredentialsGrant implements OAuth2Grant { - - // Constants - - /** - * REQUIRED - * The OAuth2 "grant_type". - */ - public static final String GRANT_TYPE = "password"; - - // Properties - - /** - * REQUIRED - * The resource owner "username". - */ - public String username; - - /** - * REQUIRED - * The resource owner "password". - */ - public String password; - - /** - * OPTIONAL - * The "scope" of the access request as described by here (https://tools.ietf.org/html/rfc6749#section-3.3). - */ - public String scope; - -} From 5a2f32196bc2f8c717e70b1684ac9aff03b5577b Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 8 Mar 2019 19:23:56 +0100 Subject: [PATCH 10/23] =?UTF-8?q?Update=20sample=E2=80=99s=20gradle=20prop?= =?UTF-8?q?erties=20and=20build.gradle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 4 +++- sample/build.gradle | 36 +++++++++++++++--------------------- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/gradle.properties b/gradle.properties index 1d3591c..915f0e6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,4 +15,6 @@ # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true \ No newline at end of file +# org.gradle.parallel=true +android.enableJetifier=true +android.useAndroidX=true \ No newline at end of file diff --git a/sample/build.gradle b/sample/build.gradle index bb539d7..56172be 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,24 +1,19 @@ apply plugin: 'com.android.application' -apply plugin: 'me.tatarka.retrolambda' buildscript { repositories { mavenCentral() } - - dependencies { - classpath 'me.tatarka:gradle-retrolambda:3.6.1' - } } android { - compileSdkVersion 25 - buildToolsVersion "25.0.3" + compileSdkVersion 28 + buildToolsVersion "28.0.3" defaultConfig { applicationId "de.rheinfabrik.heimdall" minSdkVersion 15 - targetSdkVersion 25 + targetSdkVersion 28 versionCode 1 versionName "1.0" } @@ -33,27 +28,26 @@ android { dependencies { // Android - compile 'com.android.support:appcompat-v7:23.1.1' - compile 'com.android.support:recyclerview-v7:23.1.1' - compile 'com.android.support:cardview-v7:23.1.1' + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.android.support:recyclerview-v7:28.0.0' + implementation 'com.android.support:cardview-v7:28.0.0' // Heimdall - compile project(':library') + implementation project(':library') // Rx - compile 'io.reactivex:rxandroid:1.1.0' - compile 'io.reactivex:rxjava:1.1.0' - compile 'com.trello:rxlifecycle:0.4.0' - compile 'com.trello:rxlifecycle-components:0.4.0' + implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' + implementation 'io.reactivex.rxjava2:rxjava:2.2.7' + implementation 'com.trello.rxlifecycle3:rxlifecycle-components:3.0.0' // Serialization - compile 'com.google.code.gson:gson:2.4' - compile 'org.parceler:parceler-api:0.2.16' + implementation 'com.google.code.gson:gson:2.4' + implementation 'org.parceler:parceler-api:0.2.16' // Network - compile files('libs/Heimdall.jar') - compile 'com.squareup.retrofit:retrofit:1.9.0' + implementation 'com.squareup.retrofit:retrofit:1.9.0' // Butterknife - compile 'com.jakewharton:butterknife:6.1.0' + annotationProcessor 'com.jakewharton:butterknife-compiler:8.2.1' + implementation 'com.jakewharton:butterknife:8.2.1' } \ No newline at end of file From 07d97acbd2097936f5869a0fdfd30f22bc9a0a28 Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 8 Mar 2019 19:24:16 +0100 Subject: [PATCH 11/23] =?UTF-8?q?Update=20sample=E2=80=99s=20manifest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sample/src/main/AndroidManifest.xml | 32 +++++++++++++++++------------ 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index 9805f60..cffae6e 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -1,30 +1,36 @@ - + - + - + - + - - + + - + From 95431ee742e75c8960e6063fd00773618015a187 Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 8 Mar 2019 19:29:06 +0100 Subject: [PATCH 12/23] Update sample minsdk to 21 --- sample/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample/build.gradle b/sample/build.gradle index 56172be..63d6b20 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -12,7 +12,7 @@ android { defaultConfig { applicationId "de.rheinfabrik.heimdall" - minSdkVersion 15 + minSdkVersion 21 targetSdkVersion 28 versionCode 1 versionName "1.0" From fe2f79df0eb351ca905c57121ec4a08be8a49c12 Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 8 Mar 2019 19:30:13 +0100 Subject: [PATCH 13/23] Migrate sample to rxjava2 --- .../actvities/LoginActivity.java | 11 +++---- .../heimdalldroid/actvities/MainActivity.java | 33 +++++++++---------- .../TraktTvListsRecyclerViewAdapter.java | 2 +- .../viewholder/TraktTvListViewHolder.java | 14 ++++---- .../network/TraktTvApiService.java | 4 +-- .../oauth2/TraktTvAuthorizationCodeGrant.java | 6 ++-- .../TraktTvOauth2AccessTokenManager.java | 11 +++---- .../TraktTvRefreshAccessTokenGrant.java | 8 ++--- ...edPreferencesOAuth2AccessTokenStorage.java | 6 ++-- sample/src/main/res/layout/activity_main.xml | 15 ++++----- 10 files changed, 52 insertions(+), 58 deletions(-) diff --git a/sample/src/main/java/de/rheinfabrik/heimdalldroid/actvities/LoginActivity.java b/sample/src/main/java/de/rheinfabrik/heimdalldroid/actvities/LoginActivity.java index a27c06e..4f613eb 100644 --- a/sample/src/main/java/de/rheinfabrik/heimdalldroid/actvities/LoginActivity.java +++ b/sample/src/main/java/de/rheinfabrik/heimdalldroid/actvities/LoginActivity.java @@ -1,24 +1,23 @@ package de.rheinfabrik.heimdalldroid.actvities; import android.graphics.Bitmap; -import android.net.Uri; import android.os.Bundle; import android.view.View; import android.webkit.WebView; import android.webkit.WebViewClient; -import com.trello.rxlifecycle.components.support.RxAppCompatActivity; +import butterknife.BindView; +import com.trello.rxlifecycle3.components.support.RxAppCompatActivity; +import io.reactivex.android.schedulers.AndroidSchedulers; import java.net.MalformedURLException; import java.net.URL; import butterknife.ButterKnife; -import butterknife.InjectView; import de.rheinfabrik.heimdalldroid.R; import de.rheinfabrik.heimdalldroid.network.oauth2.TraktTvAuthorizationCodeGrant; import de.rheinfabrik.heimdalldroid.network.oauth2.TraktTvOauth2AccessTokenManager; import de.rheinfabrik.heimdalldroid.utils.AlertDialogFactory; -import rx.android.schedulers.AndroidSchedulers; /** * Activity used to let the user login with his GitHub credentials. @@ -29,7 +28,7 @@ public class LoginActivity extends RxAppCompatActivity { // Members - @InjectView(R.id.webView) + @BindView(R.id.webView) protected WebView mWebView; // Activity lifecycle @@ -42,7 +41,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_login); // Inject views - ButterKnife.inject(this); + ButterKnife.bind(this); // Start authorization authorize(); diff --git a/sample/src/main/java/de/rheinfabrik/heimdalldroid/actvities/MainActivity.java b/sample/src/main/java/de/rheinfabrik/heimdalldroid/actvities/MainActivity.java index 4e4cc5f..dfef0f3 100644 --- a/sample/src/main/java/de/rheinfabrik/heimdalldroid/actvities/MainActivity.java +++ b/sample/src/main/java/de/rheinfabrik/heimdalldroid/actvities/MainActivity.java @@ -4,21 +4,22 @@ import android.content.Intent; import android.os.Build; import android.os.Bundle; -import android.support.v4.widget.SwipeRefreshLayout; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.webkit.CookieManager; -import com.trello.rxlifecycle.components.support.RxAppCompatActivity; - +import android.widget.Toolbar; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import com.trello.rxlifecycle3.components.support.RxAppCompatActivity; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; import java.util.List; import butterknife.ButterKnife; -import butterknife.InjectView; +import butterknife.BindView; import de.rheinfabrik.heimdalldroid.R; import de.rheinfabrik.heimdalldroid.adapter.TraktTvListsRecyclerViewAdapter; import de.rheinfabrik.heimdalldroid.network.TraktTvApiFactory; @@ -27,8 +28,6 @@ import de.rheinfabrik.heimdalldroid.utils.AlertDialogFactory; import de.rheinfabrik.heimdalldroid.utils.IntentFactory; import retrofit.RetrofitError; -import rx.Observable; -import rx.android.schedulers.AndroidSchedulers; /** * Activity showing either the list of the user's repositories or the login screen. @@ -43,13 +42,13 @@ public class MainActivity extends RxAppCompatActivity { // Members - @InjectView(R.id.recyclerView) + @BindView(R.id.recyclerView) protected RecyclerView mRecyclerView; - @InjectView(R.id.toolbar) + @BindView(R.id.toolbar) protected Toolbar mToolbar; - @InjectView(R.id.swipeRefreshLayout) + @BindView(R.id.swipeRefreshLayout) protected SwipeRefreshLayout mSwipeRefreshLayout; private TraktTvOauth2AccessTokenManager mTokenManager; @@ -64,10 +63,10 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_main); // Inject views - ButterKnife.inject(this); + ButterKnife.bind(this); // Setup toolbar - setSupportActionBar(mToolbar); + setActionBar(mToolbar); // Setup swipe refresh layout mSwipeRefreshLayout.setOnRefreshListener(MainActivity.this::refresh); @@ -108,10 +107,8 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.logout: { - logout(); - } + if (item.getItemId() == R.id.logout) { + logout(); } return super.onOptionsItemSelected(item); diff --git a/sample/src/main/java/de/rheinfabrik/heimdalldroid/adapter/TraktTvListsRecyclerViewAdapter.java b/sample/src/main/java/de/rheinfabrik/heimdalldroid/adapter/TraktTvListsRecyclerViewAdapter.java index d8295b7..b8f02ea 100644 --- a/sample/src/main/java/de/rheinfabrik/heimdalldroid/adapter/TraktTvListsRecyclerViewAdapter.java +++ b/sample/src/main/java/de/rheinfabrik/heimdalldroid/adapter/TraktTvListsRecyclerViewAdapter.java @@ -1,10 +1,10 @@ package de.rheinfabrik.heimdalldroid.adapter; -import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.recyclerview.widget.RecyclerView; import java.util.List; import de.rheinfabrik.heimdalldroid.R; diff --git a/sample/src/main/java/de/rheinfabrik/heimdalldroid/adapter/viewholder/TraktTvListViewHolder.java b/sample/src/main/java/de/rheinfabrik/heimdalldroid/adapter/viewholder/TraktTvListViewHolder.java index 3d8da30..fa8139f 100644 --- a/sample/src/main/java/de/rheinfabrik/heimdalldroid/adapter/viewholder/TraktTvListViewHolder.java +++ b/sample/src/main/java/de/rheinfabrik/heimdalldroid/adapter/viewholder/TraktTvListViewHolder.java @@ -1,11 +1,11 @@ package de.rheinfabrik.heimdalldroid.adapter.viewholder; -import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.TextView; +import androidx.recyclerview.widget.RecyclerView; import butterknife.ButterKnife; -import butterknife.InjectView; +import butterknife.BindView; import de.rheinfabrik.heimdalldroid.R; import de.rheinfabrik.heimdalldroid.network.models.TraktTvList; @@ -16,13 +16,13 @@ public class TraktTvListViewHolder extends RecyclerView.ViewHolder { // Members - @InjectView(R.id.titleTextView) + @BindView(R.id.titleTextView) protected TextView mTitleTextView; - @InjectView(R.id.descriptionTextView) + @BindView(R.id.descriptionTextView) protected TextView mDescriptionTextView; - @InjectView(R.id.likeCountTextView) + @BindView(R.id.likeCountTextView) protected TextView mLikeCountTextView; // Constructor @@ -31,7 +31,7 @@ public TraktTvListViewHolder(View itemView) { super(itemView); // Inject views - ButterKnife.inject(this, itemView); + ButterKnife.bind(this, itemView); } // Public Api @@ -45,6 +45,6 @@ public void bind(TraktTvList traktTvList) { mDescriptionTextView.setText(traktTvList.description); // Set like count - mLikeCountTextView.setText(String.valueOf(traktTvList.numberOfLikes) + itemView.getContext().getString(R.string.likes_postfix)); + mLikeCountTextView.setText(traktTvList.numberOfLikes + itemView.getContext().getString(R.string.likes_postfix)); } } diff --git a/sample/src/main/java/de/rheinfabrik/heimdalldroid/network/TraktTvApiService.java b/sample/src/main/java/de/rheinfabrik/heimdalldroid/network/TraktTvApiService.java index bdd5c02..9fb4809 100644 --- a/sample/src/main/java/de/rheinfabrik/heimdalldroid/network/TraktTvApiService.java +++ b/sample/src/main/java/de/rheinfabrik/heimdalldroid/network/TraktTvApiService.java @@ -1,8 +1,9 @@ package de.rheinfabrik.heimdalldroid.network; +import io.reactivex.Observable; import java.util.List; -import de.rheinfabrik.heimdall.OAuth2AccessToken; +import de.rheinfabrik.heimdall2.OAuth2AccessToken; import de.rheinfabrik.heimdalldroid.network.models.AccessTokenRequestBody; import de.rheinfabrik.heimdalldroid.network.models.RefreshTokenRequestBody; import de.rheinfabrik.heimdalldroid.network.models.RevokeAccessTokenBody; @@ -11,7 +12,6 @@ import retrofit.http.GET; import retrofit.http.Header; import retrofit.http.POST; -import rx.Observable; /** * Interface for communicating to the TraktTv API (http://docs.trakt.apiary.io/#). diff --git a/sample/src/main/java/de/rheinfabrik/heimdalldroid/network/oauth2/TraktTvAuthorizationCodeGrant.java b/sample/src/main/java/de/rheinfabrik/heimdalldroid/network/oauth2/TraktTvAuthorizationCodeGrant.java index 7e9fd3d..f9e02de 100644 --- a/sample/src/main/java/de/rheinfabrik/heimdalldroid/network/oauth2/TraktTvAuthorizationCodeGrant.java +++ b/sample/src/main/java/de/rheinfabrik/heimdalldroid/network/oauth2/TraktTvAuthorizationCodeGrant.java @@ -2,13 +2,13 @@ import android.net.Uri; +import io.reactivex.Observable; import java.net.URL; -import de.rheinfabrik.heimdall.OAuth2AccessToken; -import de.rheinfabrik.heimdall.grants.OAuth2AuthorizationCodeGrant; +import de.rheinfabrik.heimdall2.OAuth2AccessToken; +import de.rheinfabrik.heimdall2.grants.OAuth2AuthorizationCodeGrant; import de.rheinfabrik.heimdalldroid.network.TraktTvApiFactory; import de.rheinfabrik.heimdalldroid.network.models.AccessTokenRequestBody; -import rx.Observable; /** * TraktTv authorization code grant as described in http://docs.trakt.apiary.io/#reference/authentication-oauth. diff --git a/sample/src/main/java/de/rheinfabrik/heimdalldroid/network/oauth2/TraktTvOauth2AccessTokenManager.java b/sample/src/main/java/de/rheinfabrik/heimdalldroid/network/oauth2/TraktTvOauth2AccessTokenManager.java index e479ad4..9cf95a9 100644 --- a/sample/src/main/java/de/rheinfabrik/heimdalldroid/network/oauth2/TraktTvOauth2AccessTokenManager.java +++ b/sample/src/main/java/de/rheinfabrik/heimdalldroid/network/oauth2/TraktTvOauth2AccessTokenManager.java @@ -3,14 +3,14 @@ import android.content.Context; import android.content.SharedPreferences; -import de.rheinfabrik.heimdall.OAuth2AccessToken; -import de.rheinfabrik.heimdall.OAuth2AccessTokenManager; -import de.rheinfabrik.heimdall.OAuth2AccessTokenStorage; +import de.rheinfabrik.heimdall2.OAuth2AccessToken; +import de.rheinfabrik.heimdall2.OAuth2AccessTokenManager; +import de.rheinfabrik.heimdall2.OAuth2AccessTokenStorage; import de.rheinfabrik.heimdalldroid.TraktTvAPIConfiguration; import de.rheinfabrik.heimdalldroid.network.TraktTvApiFactory; import de.rheinfabrik.heimdalldroid.network.models.RevokeAccessTokenBody; import de.rheinfabrik.heimdalldroid.utils.SharedPreferencesOAuth2AccessTokenStorage; -import rx.Single; +import io.reactivex.Single; /** * Token manger used to handle all your access token needs with the TraktTv API (http://docs.trakt.apiary.io/#). @@ -77,7 +77,6 @@ public Single logout() { RevokeAccessTokenBody body = new RevokeAccessTokenBody(accessToken.accessToken); return TraktTvApiFactory.newApiService().revokeAccessToken(body); }) - .doOnNext(x -> getStorage().removeAccessToken()) - .toSingle(); + .doOnNext(x -> getStorage().removeAccessToken()).singleOrError(); } } diff --git a/sample/src/main/java/de/rheinfabrik/heimdalldroid/network/oauth2/TraktTvRefreshAccessTokenGrant.java b/sample/src/main/java/de/rheinfabrik/heimdalldroid/network/oauth2/TraktTvRefreshAccessTokenGrant.java index 8d07eb8..7359c99 100644 --- a/sample/src/main/java/de/rheinfabrik/heimdalldroid/network/oauth2/TraktTvRefreshAccessTokenGrant.java +++ b/sample/src/main/java/de/rheinfabrik/heimdalldroid/network/oauth2/TraktTvRefreshAccessTokenGrant.java @@ -1,10 +1,10 @@ package de.rheinfabrik.heimdalldroid.network.oauth2; -import de.rheinfabrik.heimdall.OAuth2AccessToken; -import de.rheinfabrik.heimdall.grants.OAuth2RefreshAccessTokenGrant; +import de.rheinfabrik.heimdall2.OAuth2AccessToken; +import de.rheinfabrik.heimdall2.grants.OAuth2RefreshAccessTokenGrant; import de.rheinfabrik.heimdalldroid.network.TraktTvApiFactory; import de.rheinfabrik.heimdalldroid.network.models.RefreshTokenRequestBody; -import rx.Single; +import io.reactivex.Single; /** * TraktTv refresh token grant as described in http://docs.trakt.apiary.io/#reference/authentication-oauth/token/exchange-refresh_token-for-access_token. @@ -22,6 +22,6 @@ public class TraktTvRefreshAccessTokenGrant extends OAuth2RefreshAccessTokenGran @Override public Single grantNewAccessToken() { RefreshTokenRequestBody body = new RefreshTokenRequestBody(refreshToken, clientId, clientSecret, redirectUri, GRANT_TYPE); - return TraktTvApiFactory.newApiService().refreshAccessToken(body).toSingle(); + return TraktTvApiFactory.newApiService().refreshAccessToken(body).singleOrError(); } } diff --git a/sample/src/main/java/de/rheinfabrik/heimdalldroid/utils/SharedPreferencesOAuth2AccessTokenStorage.java b/sample/src/main/java/de/rheinfabrik/heimdalldroid/utils/SharedPreferencesOAuth2AccessTokenStorage.java index a5d8e19..d0f6148 100755 --- a/sample/src/main/java/de/rheinfabrik/heimdalldroid/utils/SharedPreferencesOAuth2AccessTokenStorage.java +++ b/sample/src/main/java/de/rheinfabrik/heimdalldroid/utils/SharedPreferencesOAuth2AccessTokenStorage.java @@ -4,9 +4,9 @@ import com.google.gson.Gson; -import de.rheinfabrik.heimdall.OAuth2AccessToken; -import de.rheinfabrik.heimdall.OAuth2AccessTokenStorage; -import rx.Single; +import de.rheinfabrik.heimdall2.OAuth2AccessToken; +import de.rheinfabrik.heimdall2.OAuth2AccessTokenStorage; +import io.reactivex.Single; /** * A simple storage that saves the access token as plain text in the passed shared preferences. diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml index 038a26d..c406818 100644 --- a/sample/src/main/res/layout/activity_main.xml +++ b/sample/src/main/res/layout/activity_main.xml @@ -1,33 +1,32 @@ - - + android:theme="@style/AppThemeNoActionBar" /> - - + android:background="@color/hint_of_red" /> - + From 1d03822d571eabbb936b64ab92d1ac6a6b6330d9 Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 8 Mar 2019 19:33:55 +0100 Subject: [PATCH 14/23] Missing new line gradle properties --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 915f0e6..acf164f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,4 +17,4 @@ # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true android.enableJetifier=true -android.useAndroidX=true \ No newline at end of file +android.useAndroidX=true From 747885fe4f1cb09b28d332cc94af66e31fe9c846 Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 8 Mar 2019 19:35:20 +0100 Subject: [PATCH 15/23] Missing new line sample build.gradle --- sample/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample/build.gradle b/sample/build.gradle index 63d6b20..641aa01 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -50,4 +50,4 @@ dependencies { // Butterknife annotationProcessor 'com.jakewharton:butterknife-compiler:8.2.1' implementation 'com.jakewharton:butterknife:8.2.1' -} \ No newline at end of file +} From cbe48da374332e9fb50c5eec6d365e796b9621e2 Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 8 Mar 2019 19:56:42 +0100 Subject: [PATCH 16/23] travis update :) --- .travis.yml | 46 +++++++++++++++++++++++++++------------------- circle.yml | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 19 deletions(-) create mode 100644 circle.yml diff --git a/.travis.yml b/.travis.yml index 8897580..144011d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,33 +1,41 @@ language: android - +sudo: required jdk: oraclejdk8 env: global: - - MALLOC_ARENA_MAX=2 - - ADB_INSTALL_TIMEOUT=10 - matrix: - - ANDROID_TARGET=android-22 ANDROID_ABI=armeabi-v7a + - ANDROID_API_LEVEL=28 + - ANDROID_BUILD_TOOLS_VERSION=28.0.3 + - ANDROID_ABI=armeabi-v7a android: components: - tools - - build-tools-25.0.3 - - android-25 - - android-24 - - android-22 - - android-23 - - sys-img-armeabi-v7a-android-22 - - extra-google-m2repository + - platform-tools + - tools - extra-android-m2repository - - extra-android-support + licenses: + - 'android-sdk-preview-license-52d11cd2' + - 'android-sdk-license-.+' + - 'google-gdk-license-.+' -sudo: required +before_install: + - touch $HOME/.android/repositories.cfg + - yes | sdkmanager "platforms;android-28" + - yes | sdkmanager "build-tools;28.0.3" + +before_cache: + - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock + - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ + +cache: + directories: + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ + - $HOME/.android/build-cache before_script: - - echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI - - emulator -avd test -no-skin -no-audio -no-window & - - android-wait-for-emulator - - adb shell input keyevent 82 & + - chmod +x gradlew -script: ./gradlew library:test \ No newline at end of file +script: + - ./gradlew library:test \ No newline at end of file diff --git a/circle.yml b/circle.yml new file mode 100644 index 0000000..610031c --- /dev/null +++ b/circle.yml @@ -0,0 +1,41 @@ +version: 2 +jobs: + + build_test: + resource_class: large + docker: + - image: circleci/android:api-28-alpha + environment: + _JAVA_OPTIONS: "-Xms512m -Xmx3096m" + GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx3096m -XX:+HeapDumpOnOutOfMemoryError"' + steps: + - checkout + + - run: + name: Generate cache key + command: ./.scripts/checksum.sh /tmp/checksum.txt + + - restore_cache: + key: gradle-{{ checksum "/tmp/checksum.txt" }} + + - run: + name: Download Dependencies + command: ./gradlew androidDependencies + no_output_timeout: 30m + + - run: + name: Run Tests + command: ./gradlew test + no_output_timeout: 30m + + - save_cache: + key: gradle-{{ checksum "/tmp/checksum.txt" }} + paths: + - ~/.gradle/caches + - ~/.gradle/wrapper + +workflows: + version: 2 + build_test_deploy: + jobs: + - build_test From 28b971fdaed27d65b93416efbcc55c5ef1d923d1 Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 8 Mar 2019 20:25:11 +0100 Subject: [PATCH 17/23] =?UTF-8?q?Remove=20retrolambda=20from=20library=20?= =?UTF-8?q?=F0=9F=A4=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- library/build.gradle | 5 ----- 1 file changed, 5 deletions(-) diff --git a/library/build.gradle b/library/build.gradle index 7a774b7..7b6cfc9 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -1,12 +1,7 @@ apply plugin: 'groovy' apply plugin: 'maven' -apply plugin: 'me.tatarka.retrolambda' buildscript { - dependencies { - classpath 'me.tatarka:gradle-retrolambda:3.6.1' - } - repositories { mavenCentral() } From bcb6c9bfb52fc5010f5588757c311c61962e2b99 Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 15 Mar 2019 10:13:04 +0100 Subject: [PATCH 18/23] Remove unused circle.yml file --- circle.yml | 41 ----------------------------------------- 1 file changed, 41 deletions(-) delete mode 100644 circle.yml diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 610031c..0000000 --- a/circle.yml +++ /dev/null @@ -1,41 +0,0 @@ -version: 2 -jobs: - - build_test: - resource_class: large - docker: - - image: circleci/android:api-28-alpha - environment: - _JAVA_OPTIONS: "-Xms512m -Xmx3096m" - GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx3096m -XX:+HeapDumpOnOutOfMemoryError"' - steps: - - checkout - - - run: - name: Generate cache key - command: ./.scripts/checksum.sh /tmp/checksum.txt - - - restore_cache: - key: gradle-{{ checksum "/tmp/checksum.txt" }} - - - run: - name: Download Dependencies - command: ./gradlew androidDependencies - no_output_timeout: 30m - - - run: - name: Run Tests - command: ./gradlew test - no_output_timeout: 30m - - - save_cache: - key: gradle-{{ checksum "/tmp/checksum.txt" }} - paths: - - ~/.gradle/caches - - ~/.gradle/wrapper - -workflows: - version: 2 - build_test_deploy: - jobs: - - build_test From dad48ca671a5e08057a7fe2084dd0ce19a91f461 Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 22 Mar 2019 09:59:25 +0100 Subject: [PATCH 19/23] Remove double tools --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 144011d..be010b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,6 @@ android: components: - tools - platform-tools - - tools - extra-android-m2repository licenses: - 'android-sdk-preview-license-52d11cd2' From ab64a038a623df6da24086dd2e43cb6fa9dee86b Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 22 Mar 2019 10:02:22 +0100 Subject: [PATCH 20/23] Adding google first --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index a9f27ab..277ddc3 100644 --- a/build.gradle +++ b/build.gradle @@ -2,8 +2,8 @@ buildscript { repositories { - jcenter() google() + jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.3.2' @@ -15,7 +15,7 @@ buildscript { allprojects { repositories { - jcenter() google() + jcenter() } } From ff8a8d7780cee0a5e433b291628ded0c5b4dceab Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 22 Mar 2019 10:11:48 +0100 Subject: [PATCH 21/23] Add missing usage of androidx libs sample gradle --- sample/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sample/build.gradle b/sample/build.gradle index 641aa01..f40ecb3 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -28,9 +28,9 @@ android { dependencies { // Android - implementation 'com.android.support:appcompat-v7:28.0.0' - implementation 'com.android.support:recyclerview-v7:28.0.0' - implementation 'com.android.support:cardview-v7:28.0.0' + implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.recyclerview:recyclerview:1.0.0' + implementation 'androidx.cardview:cardview:1.0.0' // Heimdall implementation project(':library') @@ -41,7 +41,7 @@ dependencies { implementation 'com.trello.rxlifecycle3:rxlifecycle-components:3.0.0' // Serialization - implementation 'com.google.code.gson:gson:2.4' + implementation 'com.google.code.gson:gson:2.8.5' implementation 'org.parceler:parceler-api:0.2.16' // Network From 1ad4cef2534b4f2acb85885248c3447a526ea94b Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 22 Mar 2019 10:12:57 +0100 Subject: [PATCH 22/23] Return to 15 minSdk in sample --- sample/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample/build.gradle b/sample/build.gradle index f40ecb3..4c9bd51 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -12,7 +12,7 @@ android { defaultConfig { applicationId "de.rheinfabrik.heimdall" - minSdkVersion 21 + minSdkVersion 15 targetSdkVersion 28 versionCode 1 versionName "1.0" From d8e27462ccf9c25187ccb0bc5292504b89e7dd71 Mon Sep 17 00:00:00 2001 From: Walter Ching Date: Fri, 22 Mar 2019 10:20:41 +0100 Subject: [PATCH 23/23] Newline travis yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index be010b9..29d3c5b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,4 +37,4 @@ before_script: - chmod +x gradlew script: - - ./gradlew library:test \ No newline at end of file + - ./gradlew library:test