From 919922690a9ee5f880a6432f4da29580cac17656 Mon Sep 17 00:00:00 2001 From: Jak Spalding <191585+jak@users.noreply.github.com> Date: Wed, 26 Jun 2024 16:24:39 +0100 Subject: [PATCH] feat: add sessions and refresh tokens --- .../com/auth0/client/mgmt/UsersEntity.java | 99 +++++++++++ .../filter/CheckpointPaginationFilter.java | 38 +++++ .../users/refreshtokens/RefreshToken.java | 97 +++++++++++ .../refreshtokens/RefreshTokensPage.java | 44 +++++ .../users/refreshtokens/ResourceServer.java | 30 ++++ .../mgmt/users/sessions/Authentication.java | 21 +++ .../users/sessions/AuthenticationMethod.java | 39 +++++ .../json/mgmt/users/sessions/Client.java | 19 +++ .../json/mgmt/users/sessions/Device.java | 55 ++++++ .../json/mgmt/users/sessions/Session.java | 104 ++++++++++++ .../mgmt/users/sessions/SessionsPage.java | 44 +++++ .../java/com/auth0/client/MockServer.java | 2 + .../auth0/client/mgmt/UsersEntityTest.java | 157 ++++++++++++++++++ .../CheckpointPaginationFilterTest.java | 37 +++++ .../refreshtokens/RefreshTokensPageTest.java | 44 +++++ .../mgmt/users/sessions/SessionsPageTest.java | 50 ++++++ .../resources/mgmt/user_refresh_tokens.json | 44 +++++ src/test/resources/mgmt/user_sessions.json | 35 ++++ 18 files changed, 959 insertions(+) create mode 100644 src/main/java/com/auth0/client/mgmt/filter/CheckpointPaginationFilter.java create mode 100644 src/main/java/com/auth0/json/mgmt/users/refreshtokens/RefreshToken.java create mode 100644 src/main/java/com/auth0/json/mgmt/users/refreshtokens/RefreshTokensPage.java create mode 100644 src/main/java/com/auth0/json/mgmt/users/refreshtokens/ResourceServer.java create mode 100644 src/main/java/com/auth0/json/mgmt/users/sessions/Authentication.java create mode 100644 src/main/java/com/auth0/json/mgmt/users/sessions/AuthenticationMethod.java create mode 100644 src/main/java/com/auth0/json/mgmt/users/sessions/Client.java create mode 100644 src/main/java/com/auth0/json/mgmt/users/sessions/Device.java create mode 100644 src/main/java/com/auth0/json/mgmt/users/sessions/Session.java create mode 100644 src/main/java/com/auth0/json/mgmt/users/sessions/SessionsPage.java create mode 100644 src/test/java/com/auth0/client/mgmt/filter/CheckpointPaginationFilterTest.java create mode 100644 src/test/java/com/auth0/json/mgmt/users/refreshtokens/RefreshTokensPageTest.java create mode 100644 src/test/java/com/auth0/json/mgmt/users/sessions/SessionsPageTest.java create mode 100644 src/test/resources/mgmt/user_refresh_tokens.json create mode 100644 src/test/resources/mgmt/user_sessions.json diff --git a/src/main/java/com/auth0/client/mgmt/UsersEntity.java b/src/main/java/com/auth0/client/mgmt/UsersEntity.java index 6d06a3b6..9ee4bfa6 100644 --- a/src/main/java/com/auth0/client/mgmt/UsersEntity.java +++ b/src/main/java/com/auth0/client/mgmt/UsersEntity.java @@ -13,6 +13,8 @@ import com.auth0.json.mgmt.users.RecoveryCode; import com.auth0.json.mgmt.users.User; import com.auth0.json.mgmt.users.UsersPage; +import com.auth0.json.mgmt.users.refreshtokens.RefreshTokensPage; +import com.auth0.json.mgmt.users.sessions.SessionsPage; import com.auth0.net.EmptyBodyRequest; import com.auth0.net.BaseRequest; import com.auth0.net.Request; @@ -787,6 +789,103 @@ public Request updateAuthenticationMethodById(String userI return request; } + /** + * Get refresh tokens for a user + * A token with {@code read:refresh_tokens} is needed. + * See https://auth0.com/docs/api/management/v2/users/get-refresh-tokens-for-user + * + * @param userId the role id + * @param filter an optional pagination filter + * @return a Request to execute + */ + public Request listRefreshTokens(String userId, CheckpointPaginationFilter filter) { + Asserts.assertNotNull(userId, "user id"); + HttpUrl.Builder builder = baseUrl + .newBuilder() + .addPathSegments("api/v2/users") + .addPathSegment(userId) + .addPathSegment("refresh-tokens"); + if (filter != null) { + for (Map.Entry e : filter.getAsMap().entrySet()) { + builder.addQueryParameter(e.getKey(), String.valueOf(e.getValue())); + } + } + String url = builder.build().toString(); + return new BaseRequest<>(client, tokenProvider, url, HttpMethod.GET, new TypeReference() { + }); + } + + /** + * Delete all refresh tokens for a user. + * A token with scope {@code delete:refresh_tokens} is needed. + * See https://auth0.com/docs/api/management/v2/users/delete-refresh-tokens-for-user + * + * @param userId the user to delete the refresh tokens for + * @return a Request to execute. + */ + public Request deleteRefreshTokens(String userId) { + Asserts.assertNotNull(userId, "user ID"); + + String url = baseUrl + .newBuilder() + .addPathSegments("api/v2/users") + .addPathSegment(userId) + .addPathSegment("refresh-tokens") + .build() + .toString(); + + return new VoidRequest(this.client, tokenProvider, url, HttpMethod.DELETE); + } + + + /** + * Get sessions for user + * A token with {@code read:sessions} is needed. + * See https://auth0.com/docs/api/management/v2/users/get-sessions-for-user + * + * @param userId the role id + * @param filter an optional pagination filter + * @return a Request to execute + */ + public Request listSessions(String userId, CheckpointPaginationFilter filter) { + Asserts.assertNotNull(userId, "user id"); + HttpUrl.Builder builder = baseUrl + .newBuilder() + .addPathSegments("api/v2/users") + .addPathSegment(userId) + .addPathSegment("sessions"); + if (filter != null) { + for (Map.Entry e : filter.getAsMap().entrySet()) { + builder.addQueryParameter(e.getKey(), String.valueOf(e.getValue())); + } + } + String url = builder.build().toString(); + return new BaseRequest<>(client, tokenProvider, url, HttpMethod.GET, new TypeReference() { + }); + } + + /** + * Delete sessions for user + * A token with scope {@code delete:sessions} is needed. + * See https://auth0.com/docs/api/management/v2/users/delete-sessions-for-user + * + * @param userId the user to delete the sessions for + * @return a Request to execute. + */ + public Request deleteSessions(String userId) { + Asserts.assertNotNull(userId, "user ID"); + + String url = baseUrl + .newBuilder() + .addPathSegments("api/v2/users") + .addPathSegment(userId) + .addPathSegment("sessions") + .build() + .toString(); + + return new VoidRequest(this.client, tokenProvider, url, HttpMethod.DELETE); + } + private static void encodeAndAddQueryParam(HttpUrl.Builder builder, BaseFilter filter) { if (filter != null) { for (Map.Entry e : filter.getAsMap().entrySet()) { diff --git a/src/main/java/com/auth0/client/mgmt/filter/CheckpointPaginationFilter.java b/src/main/java/com/auth0/client/mgmt/filter/CheckpointPaginationFilter.java new file mode 100644 index 00000000..1f881b7c --- /dev/null +++ b/src/main/java/com/auth0/client/mgmt/filter/CheckpointPaginationFilter.java @@ -0,0 +1,38 @@ +package com.auth0.client.mgmt.filter; + +public class CheckpointPaginationFilter extends BaseFilter { + + /** + * Return results inside an object that contains the total result count (true) or as a direct array of results (false, default). + * + * @param includeTotals whether to include or not total result count. + * @return this filter instance + */ + public CheckpointPaginationFilter withTotals(boolean includeTotals) { + parameters.put("include_totals", includeTotals); + return this; + } + + /** + * Optional ID from which to start selection (exclusive). + * + * @param from the ID from which to start selection. This can be obtained from the {@code next} field returned from + * a checkpoint-paginated result. + * @return this filter instance. + */ + public CheckpointPaginationFilter withFrom(String from) { + parameters.put("from", from); + return this; + } + + /** + * Number of results per page. Defaults to 50. + * + * @param take the amount of entries to retrieve per page. + * @return this filter instance. + */ + public CheckpointPaginationFilter withTake(int take) { + parameters.put("take", take); + return this; + } +} diff --git a/src/main/java/com/auth0/json/mgmt/users/refreshtokens/RefreshToken.java b/src/main/java/com/auth0/json/mgmt/users/refreshtokens/RefreshToken.java new file mode 100644 index 00000000..1551f4dd --- /dev/null +++ b/src/main/java/com/auth0/json/mgmt/users/refreshtokens/RefreshToken.java @@ -0,0 +1,97 @@ +package com.auth0.json.mgmt.users.refreshtokens; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Date; +import java.util.List; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class RefreshToken { + @JsonProperty("id") + private String id; + @JsonProperty("user_id") + private String userId; + @JsonProperty("created_at") + private Date createdAt; + @JsonProperty("idle_expires_at") + private Date idleExpiresAt; + @JsonProperty("expires_at") + private Date expiresAt; + @JsonProperty("client_id") + private String clientId; + @JsonProperty("session_id") + private String sessionId; + @JsonProperty("rotating") + private Boolean rotating; + @JsonProperty("resource_servers") + private List resourceServers; + + /** + * @return The ID of the refresh token + */ + public String getId() { + return id; + } + + /** + * @return ID of the user which can be used when interacting with other APIs. + */ + public String getUserId() { + return userId; + } + + /** + * @return The date and time when the refresh token was created + */ + public Date getCreatedAt() { + return createdAt; + } + + /** + * + * @return The date and time when the refresh token will expire if idle + */ + public Date getIdleExpiresAt() { + return idleExpiresAt; + } + + /** + * + * @return The date and time when the refresh token will expire + */ + public Date getExpiresAt() { + return expiresAt; + } + + /** + * @return ID of the client application granted with this refresh token + */ + public String getClientId() { + return clientId; + } + + /** + * + * @return ID of the authenticated session used to obtain this refresh-token + */ + public String getSessionId() { + return sessionId; + } + + /** + * @return True if the token is a rotating refresh token + */ + public Boolean isRotating() { + return rotating; + } + + /** + * @return A list of the resource server IDs associated to this refresh-token and their granted scopes + */ + public List getResourceServers() { + return resourceServers; + } +} diff --git a/src/main/java/com/auth0/json/mgmt/users/refreshtokens/RefreshTokensPage.java b/src/main/java/com/auth0/json/mgmt/users/refreshtokens/RefreshTokensPage.java new file mode 100644 index 00000000..224c0b62 --- /dev/null +++ b/src/main/java/com/auth0/json/mgmt/users/refreshtokens/RefreshTokensPage.java @@ -0,0 +1,44 @@ +package com.auth0.json.mgmt.users.refreshtokens; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +/** + * This does not extend com.auth0.json.mgmt.Page because the URL only supports "next" and "take" pagination. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class RefreshTokensPage { + @JsonProperty("total") + private Integer total; + + @JsonProperty("next") + private String next; + + @JsonProperty("tokens") + private List tokens; + + /** + * @return the total number of refresh tokens. This is only present when `include_totals` is passed as a query parameter. + */ + public Integer getTotal() { + return total; + } + + /** + * @return the token ID from which to start selection for a new page + */ + public String getNext() { + return next; + } + + /** + * @return the list of Tokens + */ + public List getTokens() { + return tokens; + } +} diff --git a/src/main/java/com/auth0/json/mgmt/users/refreshtokens/ResourceServer.java b/src/main/java/com/auth0/json/mgmt/users/refreshtokens/ResourceServer.java new file mode 100644 index 00000000..b423de41 --- /dev/null +++ b/src/main/java/com/auth0/json/mgmt/users/refreshtokens/ResourceServer.java @@ -0,0 +1,30 @@ +package com.auth0.json.mgmt.users.refreshtokens; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ResourceServer { + @JsonProperty("audience") + private String audience; + @JsonProperty("scopes") + private List scopes; + + /** + * @return Resource server ID + */ + public String getAudience() { + return audience; + } + + /** + * @return List of scopes for the refresh token + */ + public List getScopes() { + return scopes; + } +} diff --git a/src/main/java/com/auth0/json/mgmt/users/sessions/Authentication.java b/src/main/java/com/auth0/json/mgmt/users/sessions/Authentication.java new file mode 100644 index 00000000..dd9ba115 --- /dev/null +++ b/src/main/java/com/auth0/json/mgmt/users/sessions/Authentication.java @@ -0,0 +1,21 @@ +package com.auth0.json.mgmt.users.sessions; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Authentication { + @JsonProperty("methods") + private List methods; + + /** + * @return Contains the authentication methods a user has completed during their session + */ + public List getMethods() { + return methods; + } +} diff --git a/src/main/java/com/auth0/json/mgmt/users/sessions/AuthenticationMethod.java b/src/main/java/com/auth0/json/mgmt/users/sessions/AuthenticationMethod.java new file mode 100644 index 00000000..c55634c5 --- /dev/null +++ b/src/main/java/com/auth0/json/mgmt/users/sessions/AuthenticationMethod.java @@ -0,0 +1,39 @@ +package com.auth0.json.mgmt.users.sessions; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Date; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class AuthenticationMethod { + @JsonProperty("name") + private String name; + @JsonProperty("timestamp") + private Date timestamp; + @JsonProperty("type") + private String type; + + /** + * @return One of: "federated", "passkey", "pwd", "sms", "email", "mfa", "mock" or a custom method denoted by a URL + */ + public String getName() { + return name; + } + + /** + * @return Timestamp of when the signal was received + */ + public Date getTimestamp() { + return timestamp; + } + + /** + * @return A specific MFA factor. Only present when "name" is set to "mfa" + */ + public String getType() { + return type; + } +} diff --git a/src/main/java/com/auth0/json/mgmt/users/sessions/Client.java b/src/main/java/com/auth0/json/mgmt/users/sessions/Client.java new file mode 100644 index 00000000..aa51c6e9 --- /dev/null +++ b/src/main/java/com/auth0/json/mgmt/users/sessions/Client.java @@ -0,0 +1,19 @@ +package com.auth0.json.mgmt.users.sessions; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Client { + @JsonProperty("client_id") + private String clientId; + + /** + * @return ID of client for the session + */ + public String getClientId() { + return clientId; + } +} diff --git a/src/main/java/com/auth0/json/mgmt/users/sessions/Device.java b/src/main/java/com/auth0/json/mgmt/users/sessions/Device.java new file mode 100644 index 00000000..1665c6ed --- /dev/null +++ b/src/main/java/com/auth0/json/mgmt/users/sessions/Device.java @@ -0,0 +1,55 @@ +package com.auth0.json.mgmt.users.sessions; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Device { + @JsonProperty("initial_ip") + private String initialIP; + @JsonProperty("initial_asn") + private String initialASN; + @JsonProperty("last_user_agent") + private String lastUserAgent; + @JsonProperty("last_ip") + private String lastIP; + @JsonProperty("last_asn") + private String lastASN; + + /** + * @return First IP address associated with this session + */ + public String getInitialIP() { + return initialIP; + } + + /** + * @return First autonomous system number associated with this session + */ + public String getInitialASN() { + return initialASN; + } + + /** + * @return Last user agent of the device from which this user logged in + */ + public String getLastUserAgent() { + return lastUserAgent; + } + + /** + * @return Last IP address from which this user logged in + */ + public String getLastIP() { + return lastIP; + } + + /** + * @return Last autonomous system number from which this user logged in + */ + public String getLastASN() { + return lastASN; + } +} diff --git a/src/main/java/com/auth0/json/mgmt/users/sessions/Session.java b/src/main/java/com/auth0/json/mgmt/users/sessions/Session.java new file mode 100644 index 00000000..ae0fe6c6 --- /dev/null +++ b/src/main/java/com/auth0/json/mgmt/users/sessions/Session.java @@ -0,0 +1,104 @@ +package com.auth0.json.mgmt.users.sessions; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Date; +import java.util.List; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Session { + @JsonProperty("id") + private String id; + @JsonProperty("user_id") + private String userId; + @JsonProperty("created_at") + private Date createdAt; + @JsonProperty("updated_at") + private Date updatedAt; + @JsonProperty("authenticated_at") + private Date authenticatedAt; + @JsonProperty("idle_expires_at") + private Date idleExpiresAt; + @JsonProperty("expires_at") + private Date expiresAt; + @JsonProperty("device") + private Device device; + @JsonProperty("clients") + private List clients; + @JsonProperty("authentication") + private Authentication authentication; + + /** + * @return The ID of the session + */ + public String getId() { + return id; + } + + /** + * @return ID of the user which can be used when interacting with other APIs. + */ + public String getUserId() { + return userId; + } + + /** + * + * @return The date and time when the session was created + */ + public Date getCreatedAt() { + return createdAt; + } + + /** + * @return The date and time when the session was last updated + */ + public Date getUpdatedAt() { + return updatedAt; + } + + /** + * @return The date and time when the session was last authenticated + */ + public Date getAuthenticatedAt() { + return authenticatedAt; + } + + /** + * @return The date and time when the session will expire if idle + */ + public Date getIdleExpiresAt() { + return idleExpiresAt; + } + + /** + * @return The date and time when the session will expire + */ + public Date getExpiresAt() { + return expiresAt; + } + + /** + * @return Metadata related to the device used in the session + */ + public Device getDevice() { + return device; + } + + /** + * @return List of client details for the session + */ + public List getClients() { + return clients; + } + + /** + * @return Details about authentication signals obtained during the login flow + */ + public Authentication getAuthentication() { + return authentication; + } +} diff --git a/src/main/java/com/auth0/json/mgmt/users/sessions/SessionsPage.java b/src/main/java/com/auth0/json/mgmt/users/sessions/SessionsPage.java new file mode 100644 index 00000000..da536451 --- /dev/null +++ b/src/main/java/com/auth0/json/mgmt/users/sessions/SessionsPage.java @@ -0,0 +1,44 @@ +package com.auth0.json.mgmt.users.sessions; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +/** + * This does not extend com.auth0.json.mgmt.Page because the URL only supports "next" and "take" pagination. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class SessionsPage { + @JsonProperty("total") + private Integer total; + + @JsonProperty("next") + private String next; + + @JsonProperty("sessions") + private List sessions; + + /** + * @return the total number of refresh tokens. This is only present when `include_totals` is passed as a query parameter. + */ + public Integer getTotal() { + return total; + } + + /** + * @return the token ID from which to start selection for a new page + */ + public String getNext() { + return next; + } + + /** + * @return the list of Sessions + */ + public List getSessions() { + return sessions; + } +} diff --git a/src/test/java/com/auth0/client/MockServer.java b/src/test/java/com/auth0/client/MockServer.java index 9df874b2..5b162e67 100644 --- a/src/test/java/com/auth0/client/MockServer.java +++ b/src/test/java/com/auth0/client/MockServer.java @@ -78,6 +78,8 @@ public class MockServer { public static final String MGMT_USERS_PAGED_LIST = "src/test/resources/mgmt/users_paged_list.json"; public static final String MGMT_USER_PERMISSIONS_PAGED_LIST = "src/test/resources/mgmt/user_permissions_paged_list.json"; public static final String MGMT_USER_ROLES_PAGED_LIST = "src/test/resources/mgmt/user_roles_paged_list.json"; + public static final String MGMT_USER_REFRESH_TOKENS = "src/test/resources/mgmt/user_refresh_tokens.json"; + public static final String MGMT_USER_SESSIONS = "src/test/resources/mgmt/user_sessions.json"; public static final String MGMT_USER = "src/test/resources/mgmt/user.json"; public static final String MGMT_RECOVERY_CODE = "src/test/resources/mgmt/recovery_code.json"; public static final String MGMT_IDENTITIES_LIST = "src/test/resources/mgmt/identities_list.json"; diff --git a/src/test/java/com/auth0/client/mgmt/UsersEntityTest.java b/src/test/java/com/auth0/client/mgmt/UsersEntityTest.java index 61f519c9..a2556ee5 100644 --- a/src/test/java/com/auth0/client/mgmt/UsersEntityTest.java +++ b/src/test/java/com/auth0/client/mgmt/UsersEntityTest.java @@ -4,6 +4,7 @@ import com.auth0.client.mgmt.filter.LogEventFilter; import com.auth0.client.mgmt.filter.PageFilter; import com.auth0.client.mgmt.filter.UserFilter; +import com.auth0.client.mgmt.filter.CheckpointPaginationFilter; import com.auth0.json.mgmt.guardian.Enrollment; import com.auth0.json.mgmt.logevents.LogEvent; import com.auth0.json.mgmt.logevents.LogEventsPage; @@ -17,6 +18,8 @@ import com.auth0.json.mgmt.users.UsersPage; import com.auth0.json.mgmt.users.authenticationmethods.AuthenticationMethod; import com.auth0.json.mgmt.users.authenticationmethods.AuthenticationMethodsPage; +import com.auth0.json.mgmt.users.refreshtokens.RefreshTokensPage; +import com.auth0.json.mgmt.users.sessions.SessionsPage; import com.auth0.net.Request; import com.auth0.net.client.HttpMethod; import okhttp3.mockwebserver.RecordedRequest; @@ -1348,4 +1351,158 @@ public void shouldInvalidateRememberedBrowsers() throws Exception { assertThat(recordedRequest, hasMethodAndPath(HttpMethod.POST, "/api/v2/users/userId/multifactor/actions/invalidate-remember-browser")); assertThat(recordedRequest, hasHeader("Authorization", "Bearer apiToken")); } + + @Test + public void shouldListRefreshTokensWithoutFilter() throws Exception { + Request request = api.users().listRefreshTokens("1", null); + assertThat(request, is(notNullValue())); + + server.jsonResponse(MGMT_USER_REFRESH_TOKENS, 200); + RefreshTokensPage response = request.execute().getBody(); + RecordedRequest recordedRequest = server.takeRequest(); + + assertThat(recordedRequest, hasMethodAndPath(HttpMethod.GET, "/api/v2/users/1/refresh-tokens")); + assertThat(recordedRequest, hasHeader("Content-Type", "application/json")); + assertThat(recordedRequest, hasHeader("Authorization", "Bearer apiToken")); + + assertThat(response, is(notNullValue())); + assertThat(response.getTokens(), hasSize(2)); + } + + @Test + public void shouldListRefreshTokensWithPage() throws Exception { + CheckpointPaginationFilter filter = new CheckpointPaginationFilter().withFrom("tokenId2").withTake(5); + Request request = api.users().listRefreshTokens("1", filter); + assertThat(request, is(notNullValue())); + + server.jsonResponse(MGMT_USER_REFRESH_TOKENS, 200); + RefreshTokensPage response = request.execute().getBody(); + RecordedRequest recordedRequest = server.takeRequest(); + + assertThat(recordedRequest, hasMethodAndPath(HttpMethod.GET, "/api/v2/users/1/refresh-tokens")); + assertThat(recordedRequest, hasHeader("Content-Type", "application/json")); + assertThat(recordedRequest, hasHeader("Authorization", "Bearer apiToken")); + assertThat(recordedRequest, hasQueryParameter("from", "tokenId2")); + assertThat(recordedRequest, hasQueryParameter("take", "5")); + + assertThat(response, is(notNullValue())); + assertThat(response.getTokens(), hasSize(2)); + } + + @Test + public void shouldListRefreshTokensWithTotal() throws Exception { + CheckpointPaginationFilter filter = new CheckpointPaginationFilter().withTotals(true); + Request request = api.users().listRefreshTokens("1", filter); + assertThat(request, is(notNullValue())); + + server.jsonResponse(MGMT_USER_REFRESH_TOKENS, 200); + RefreshTokensPage response = request.execute().getBody(); + RecordedRequest recordedRequest = server.takeRequest(); + + assertThat(recordedRequest, hasMethodAndPath(HttpMethod.GET, "/api/v2/users/1/refresh-tokens")); + assertThat(recordedRequest, hasHeader("Content-Type", "application/json")); + assertThat(recordedRequest, hasHeader("Authorization", "Bearer apiToken")); + assertThat(recordedRequest, hasQueryParameter("include_totals", "true")); + + assertThat(response, is(notNullValue())); + assertThat(response.getTokens(), hasSize(2)); + assertThat(response.getTotal(), is(11)); + } + + @Test + public void shouldNotDeleteRefreshTokensWithNullUserId() { + verifyThrows(IllegalArgumentException.class, + () -> api.users().deleteRefreshTokens(null), + "'user ID' cannot be null!"); + } + + @Test + public void shouldDeleteRefreshTokens() throws Exception { + Request request = api.users().deleteRefreshTokens("1"); + assertThat(request, is(notNullValue())); + + server.noContentResponse(); + request.execute().getBody(); + RecordedRequest recordedRequest = server.takeRequest(); + + assertThat(recordedRequest, hasMethodAndPath(HttpMethod.DELETE, "/api/v2/users/1/refresh-tokens")); + assertThat(recordedRequest, hasHeader("Authorization", "Bearer apiToken")); + } + + @Test + public void shouldListSessionsWithoutFilter() throws Exception { + Request request = api.users().listSessions("1", null); + assertThat(request, is(notNullValue())); + + server.jsonResponse(MGMT_USER_SESSIONS, 200); + SessionsPage response = request.execute().getBody(); + RecordedRequest recordedRequest = server.takeRequest(); + + assertThat(recordedRequest, hasMethodAndPath(HttpMethod.GET, "/api/v2/users/1/sessions")); + assertThat(recordedRequest, hasHeader("Content-Type", "application/json")); + assertThat(recordedRequest, hasHeader("Authorization", "Bearer apiToken")); + + assertThat(response, is(notNullValue())); + assertThat(response.getSessions(), hasSize(1)); + } + + @Test + public void shouldListSessionsWithPage() throws Exception { + CheckpointPaginationFilter filter = new CheckpointPaginationFilter().withFrom("sessionId3").withTake(9); + Request request = api.users().listSessions("1", filter); + assertThat(request, is(notNullValue())); + + server.jsonResponse(MGMT_USER_SESSIONS, 200); + SessionsPage response = request.execute().getBody(); + RecordedRequest recordedRequest = server.takeRequest(); + + assertThat(recordedRequest, hasMethodAndPath(HttpMethod.GET, "/api/v2/users/1/sessions")); + assertThat(recordedRequest, hasHeader("Content-Type", "application/json")); + assertThat(recordedRequest, hasHeader("Authorization", "Bearer apiToken")); + assertThat(recordedRequest, hasQueryParameter("from", "sessionId3")); + assertThat(recordedRequest, hasQueryParameter("take", "9")); + + assertThat(response, is(notNullValue())); + assertThat(response.getSessions(), hasSize(1)); + } + + @Test + public void shouldListSessionsWithTotal() throws Exception { + CheckpointPaginationFilter filter = new CheckpointPaginationFilter().withTotals(true); + Request request = api.users().listSessions("1", filter); + assertThat(request, is(notNullValue())); + + server.jsonResponse(MGMT_USER_SESSIONS, 200); + SessionsPage response = request.execute().getBody(); + RecordedRequest recordedRequest = server.takeRequest(); + + assertThat(recordedRequest, hasMethodAndPath(HttpMethod.GET, "/api/v2/users/1/sessions")); + assertThat(recordedRequest, hasHeader("Content-Type", "application/json")); + assertThat(recordedRequest, hasHeader("Authorization", "Bearer apiToken")); + assertThat(recordedRequest, hasQueryParameter("include_totals", "true")); + + assertThat(response, is(notNullValue())); + assertThat(response.getSessions(), hasSize(1)); + assertThat(response.getTotal(), is(9)); + } + + @Test + public void shouldNotDeleteSessionsWithNullUserId() { + verifyThrows(IllegalArgumentException.class, + () -> api.users().deleteRefreshTokens(null), + "'user ID' cannot be null!"); + } + + @Test + public void shouldDeleteSessions() throws Exception { + Request request = api.users().deleteSessions("1"); + assertThat(request, is(notNullValue())); + + server.noContentResponse(); + request.execute().getBody(); + RecordedRequest recordedRequest = server.takeRequest(); + + assertThat(recordedRequest, hasMethodAndPath(HttpMethod.DELETE, "/api/v2/users/1/sessions")); + assertThat(recordedRequest, hasHeader("Authorization", "Bearer apiToken")); + } } diff --git a/src/test/java/com/auth0/client/mgmt/filter/CheckpointPaginationFilterTest.java b/src/test/java/com/auth0/client/mgmt/filter/CheckpointPaginationFilterTest.java new file mode 100644 index 00000000..69cb4641 --- /dev/null +++ b/src/test/java/com/auth0/client/mgmt/filter/CheckpointPaginationFilterTest.java @@ -0,0 +1,37 @@ +package com.auth0.client.mgmt.filter; + +import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +public class CheckpointPaginationFilterTest { + + private CheckpointPaginationFilter filter; + + @BeforeEach + public void setUp() { + filter = new CheckpointPaginationFilter(); + } + + @Test + public void shouldIncludeTotals() { + CheckpointPaginationFilter instance = filter.withTotals(true); + + assertThat(filter, is(instance)); + assertThat(filter.getAsMap(), is(notNullValue())); + assertThat(filter.getAsMap(), Matchers.hasEntry("include_totals", true)); + } + + @Test + public void shouldIncludeCheckpointParams() { + CheckpointPaginationFilter instance = filter.withFrom("abc123").withTake(2); + + assertThat(filter.getAsMap(), is(notNullValue())); + assertThat(filter.getAsMap(), Matchers.hasEntry("from", "abc123")); + assertThat(filter.getAsMap(), Matchers.hasEntry("take", 2)); + } +} diff --git a/src/test/java/com/auth0/json/mgmt/users/refreshtokens/RefreshTokensPageTest.java b/src/test/java/com/auth0/json/mgmt/users/refreshtokens/RefreshTokensPageTest.java new file mode 100644 index 00000000..b0e701eb --- /dev/null +++ b/src/test/java/com/auth0/json/mgmt/users/refreshtokens/RefreshTokensPageTest.java @@ -0,0 +1,44 @@ +package com.auth0.json.mgmt.users.refreshtokens; + +import com.auth0.json.JsonTest; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +public class RefreshTokensPageTest extends JsonTest { + private static final String json = "{\n\"tokens\": [\n{\n\"id\": \"tokenId1\",\n\"user_id\": \"userId1\",\n\"created_at\": \"2024-06-26T09:10:26.643Z\",\n\"updated_at\": \"2024-06-26T09:10:27.131Z\",\n\"expires_at\": \"2024-07-03T09:10:26.643Z\",\n\"client_id\": \"clientId1\",\n\"session_id\": \"sessionId1\",\n\"rotating\": false,\n\"resource_servers\": [\n{\n\"audience\": \"https://api.example.com\",\n\"scopes\": [\n\"read:examples\",\n\"write:examples\"\n]\n}\n]\n},\n{\n\"id\": \"tokenId2\",\n\"user_id\": \"userId1\",\n\"created_at\": \"2024-06-26T09:10:26.643Z\",\n\"updated_at\": \"2024-06-26T09:10:27.131Z\",\n\"expires_at\": \"2024-07-03T09:10:26.643Z\",\n\"client_id\": \"clientId2\",\n\"session_id\": \"sessionId2\",\n\"rotating\": true,\n\"resource_servers\": [\n{\n\"audience\": \"https://api.example.com\",\n\"scopes\": [\n\"read:examples\",\n\"write:examples\"\n]\n}\n]\n}\n],\n\"next\": \"token1\"\n}\n"; + private static final String jsonWithTotal = "{\n\"tokens\": [\n{\n\"id\": \"tokenId1\",\n\"user_id\": \"userId1\",\n\"created_at\": \"2024-06-26T09:10:26.643Z\",\n\"updated_at\": \"2024-06-26T09:10:27.131Z\",\n\"expires_at\": \"2024-07-03T09:10:26.643Z\",\n\"client_id\": \"clientId1\",\n\"session_id\": \"sessionId1\",\n\"rotating\": false,\n\"resource_servers\": [\n{\n\"audience\": \"https://api.example.com\",\n\"scopes\": [\n\"read:examples\",\n\"write:examples\"\n]\n}\n]\n},\n{\n\"id\": \"tokenId2\",\n\"user_id\": \"userId1\",\n\"created_at\": \"2024-06-26T09:10:26.643Z\",\n\"updated_at\": \"2024-06-26T09:10:27.131Z\",\n\"expires_at\": \"2024-07-03T09:10:26.643Z\",\n\"client_id\": \"clientId2\",\n\"session_id\": \"sessionId2\",\n\"rotating\": true,\n\"resource_servers\": [\n{\n\"audience\": \"https://api.example.com\",\n\"scopes\": [\n\"read:examples\",\n\"write:examples\"\n]\n}\n]\n}\n],\n\"next\": \"token1\",\n\"total\": 11\n}\n"; + + @Test + public void shouldDeserialize() throws Exception { + RefreshTokensPage page = fromJSON(json, RefreshTokensPage.class); + + assertThat(page.getTotal(), is(nullValue())); + assertThat(page.getNext(), is("token1")); + + assertThat(page.getTokens().size(), is(2)); + assertThat(page.getTokens().get(0).getId(), is("tokenId1")); + assertThat(page.getTokens().get(0).getUserId(), is("userId1")); + assertThat(page.getTokens().get(0).getCreatedAt(), is(parseJSONDate("2024-06-26T09:10:26.643Z"))); + assertThat(page.getTokens().get(0).getIdleExpiresAt(), is(nullValue())); + assertThat(page.getTokens().get(0).getExpiresAt(), is(parseJSONDate("2024-07-03T09:10:26.643Z"))); + assertThat(page.getTokens().get(0).getClientId(), is("clientId1")); + assertThat(page.getTokens().get(0).getSessionId(), is("sessionId1")); + assertThat(page.getTokens().get(0).isRotating(), is(false)); + + assertThat(page.getTokens().get(0).getResourceServers().size(), is(1)); + assertThat(page.getTokens().get(0).getResourceServers().get(0).getAudience(), is("https://api.example.com")); + assertThat(page.getTokens().get(0).getResourceServers().get(0).getScopes().size(), is(2)); + assertThat(page.getTokens().get(0).getResourceServers().get(0).getScopes().get(0), is("read:examples")); + assertThat(page.getTokens().get(0).getResourceServers().get(0).getScopes().get(1), is("write:examples")); + } + + @Test + public void shouldDeserializeWithTotal() throws Exception { + RefreshTokensPage page = fromJSON(jsonWithTotal, RefreshTokensPage.class); + + assertThat(page.getTotal(), is(11)); + } +} diff --git a/src/test/java/com/auth0/json/mgmt/users/sessions/SessionsPageTest.java b/src/test/java/com/auth0/json/mgmt/users/sessions/SessionsPageTest.java new file mode 100644 index 00000000..6fdcf2dc --- /dev/null +++ b/src/test/java/com/auth0/json/mgmt/users/sessions/SessionsPageTest.java @@ -0,0 +1,50 @@ +package com.auth0.json.mgmt.users.sessions; + +import com.auth0.json.JsonTest; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +public class SessionsPageTest extends JsonTest { + private static final String json = "{\"sessions\":[{\"id\":\"sessionId1\",\n\"user_id\":\"userId1\",\n\"created_at\":\"2024-06-26T09:10:26.643Z\",\n\"updated_at\":\"2024-06-26T09:10:27.131Z\",\n\"authenticated_at\":\"2024-06-26T09:10:26.643Z\",\n\"authentication\":{\n\"methods\":[\n{\n\"name\":\"pwd\",\n\"timestamp\":\"2024-06-26T09:10:26.643Z\"\n}\n]\n},\n\"idle_expires_at\":\"2024-06-26T09:40:27.131Z\",\n\"expires_at\":\"2024-07-03T09:10:26.643Z\",\n\"device\":{\n\"initial_asn\":\"1234\",\n\"initial_ip\":\"203.0.113.1\",\n\"last_user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36\",\n\"last_ip\":\"203.0.113.1\",\n\"last_asn\":\"1234\"\n},\n\"clients\":[\n{\n\"client_id\":\"clientId1\"\n}\n]\n}\n],\n\"next\":\"sessionId1\"\n}\n"; + private static final String jsonWithTotals = "{\"sessions\":[{\"id\":\"sessionId1\",\n\"user_id\":\"userId1\",\n\"created_at\":\"2024-06-26T09:10:26.643Z\",\n\"updated_at\":\"2024-06-26T09:10:27.131Z\",\n\"authenticated_at\":\"2024-06-26T09:10:26.643Z\",\n\"authentication\":{\n\"methods\":[\n{\n\"name\":\"pwd\",\n\"timestamp\":\"2024-06-26T09:10:26.643Z\"\n}\n]\n},\n\"idle_expires_at\":\"2024-06-26T09:40:27.131Z\",\n\"expires_at\":\"2024-07-03T09:10:26.643Z\",\n\"device\":{\n\"initial_asn\":\"1234\",\n\"initial_ip\":\"203.0.113.1\",\n\"last_user_agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36\",\n\"last_ip\":\"203.0.113.1\",\n\"last_asn\":\"1234\"\n},\n\"clients\":[\n{\n\"client_id\":\"clientId1\"\n}\n]\n}\n],\n\"next\":\"sessionId1\",\n\"total\":11\n}\n"; + + @Test + public void shouldDeserialize() throws Exception { + SessionsPage page = fromJSON(json, SessionsPage.class); + + assertThat(page.getTotal(), is(nullValue())); + assertThat(page.getNext(), is("sessionId1")); + assertThat(page.getSessions().size(), is(1)); + assertThat(page.getSessions().get(0).getId(), is("sessionId1")); + assertThat(page.getSessions().get(0).getUserId(), is("userId1")); + assertThat(page.getSessions().get(0).getCreatedAt(), is(parseJSONDate("2024-06-26T09:10:26.643Z"))); + assertThat(page.getSessions().get(0).getUpdatedAt(), is(parseJSONDate("2024-06-26T09:10:27.131Z"))); + assertThat(page.getSessions().get(0).getAuthenticatedAt(), is(parseJSONDate("2024-06-26T09:10:26.643Z"))); + assertThat(page.getSessions().get(0).getIdleExpiresAt(), is(parseJSONDate("2024-06-26T09:40:27.131Z"))); + assertThat(page.getSessions().get(0).getExpiresAt(), is(parseJSONDate("2024-07-03T09:10:26.643Z"))); + + assertThat(page.getSessions().get(0).getDevice().getInitialASN(), is("1234")); + assertThat(page.getSessions().get(0).getDevice().getInitialIP(), is("203.0.113.1")); + assertThat(page.getSessions().get(0).getDevice().getLastUserAgent(), is("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36")); + assertThat(page.getSessions().get(0).getDevice().getLastIP(), is("203.0.113.1")); + assertThat(page.getSessions().get(0).getDevice().getLastASN(), is("1234")); + + assertThat(page.getSessions().get(0).getClients().size(), is(1)); + assertThat(page.getSessions().get(0).getClients().get(0).getClientId(), is("clientId1")); + + assertThat(page.getSessions().get(0).getAuthentication().getMethods().size(), is(1)); + assertThat(page.getSessions().get(0).getAuthentication().getMethods().get(0).getName(), is("pwd")); + assertThat(page.getSessions().get(0).getAuthentication().getMethods().get(0).getTimestamp(), is(parseJSONDate("2024-06-26T09:10:26.643Z"))); + assertThat(page.getSessions().get(0).getAuthentication().getMethods().get(0).getType(), is(nullValue())); + } + + @Test + public void shouldDeserializeWithTotals() throws Exception { + SessionsPage page = fromJSON(jsonWithTotals, SessionsPage.class); + + assertThat(page.getTotal(), is(11)); + } +} diff --git a/src/test/resources/mgmt/user_refresh_tokens.json b/src/test/resources/mgmt/user_refresh_tokens.json new file mode 100644 index 00000000..c1ed7aa9 --- /dev/null +++ b/src/test/resources/mgmt/user_refresh_tokens.json @@ -0,0 +1,44 @@ +{ + "tokens": [ + { + "id": "tokenId1", + "user_id": "userId1", + "created_at": "2024-06-26T09:10:26.643Z", + "updated_at": "2024-06-26T09:10:27.131Z", + "expires_at": "2024-07-03T09:10:26.643Z", + "client_id": "clientId1", + "session_id": "sessionId1", + "rotating": false, + "resource_servers": [ + { + "audience": "https://api.example.com", + "scopes": [ + "read:examples", + "write:examples" + ] + } + ] + }, + { + "id": "tokenId2", + "user_id": "userId1", + "created_at": "2024-06-26T09:10:26.643Z", + "updated_at": "2024-06-26T09:10:27.131Z", + "expires_at": "2024-07-03T09:10:26.643Z", + "client_id": "clientId2", + "session_id": "sessionId2", + "rotating": true, + "resource_servers": [ + { + "audience": "https://api.example.com", + "scopes": [ + "read:examples", + "write:examples" + ] + } + ] + } + ], + "next": "token1", + "total": 11 +} diff --git a/src/test/resources/mgmt/user_sessions.json b/src/test/resources/mgmt/user_sessions.json new file mode 100644 index 00000000..750b127e --- /dev/null +++ b/src/test/resources/mgmt/user_sessions.json @@ -0,0 +1,35 @@ +{ + "sessions": [ + { + "id": "sessionId1", + "user_id": "userId1", + "created_at": "2024-06-26T09:10:26.643Z", + "updated_at": "2024-06-26T09:10:27.131Z", + "authenticated_at": "2024-06-26T09:10:26.643Z", + "authentication": { + "methods": [ + { + "name": "pwd", + "timestamp": "2024-06-26T09:10:26.643Z" + } + ] + }, + "idle_expires_at": "2024-06-26T09:40:27.131Z", + "expires_at": "2024-07-03T09:10:26.643Z", + "device": { + "initial_asn": "1234", + "initial_ip": "203.0.113.1", + "last_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36", + "last_ip": "203.0.113.1", + "last_asn": "1234" + }, + "clients": [ + { + "client_id": "clientId1" + } + ] + } + ], + "next": "sessionId1", + "total": 9 +}