From 30f6e1a4bacb90a711ed6f91bc124267d44b9d44 Mon Sep 17 00:00:00 2001 From: pujavs <43700552+pujavs@users.noreply.github.com> Date: Thu, 18 Aug 2022 12:52:05 +0530 Subject: [PATCH] feat(jans-config-api): session management endpoint (#2158) * feat(jans-config-api): Session endpoint - wip * feat(jans-config-api): session endpoint wip * feat(jans-config-api): session endpoint wip * feat(jans-config-api): session endpoint wip * feat(jans-config-api): user session management - wip * feat(jans-config-api): session management * feat(jans-config-api): session management * feat(jans-config-api): session management * feat(jans-config-api): session management --- .../configapi/util/ApiAccessConstants.java | 3 + .../io/jans/configapi/util/ApiConstants.java | 6 +- .../docs/jans-config-api-swagger.yaml | 121 +++++++++++++++++- .../profiles/local/test.properties | 6 +- .../jans/configapi/rest/ApiApplication.java | 1 + .../rest/resource/auth/SessionResource.java | 54 ++++++++ .../security/client/AuthClientFactory.java | 50 ++++++-- .../security/service/OpenIdService.java | 21 +-- .../service/auth/SessionService.java | 100 +++++++++++++++ .../java/io/jans/configapi/util/AuthUtil.java | 41 +++++- .../main/resources/config-api-rs-protect.json | 18 +++ .../resources/feature/agama/agama.feature | 8 +- .../resources/feature/session/session.feature | 40 ++++++ .../test/resources/karate-config-jenkins.js | 1 + .../src/test/resources/karate-config.js | 1 + 15 files changed, 434 insertions(+), 37 deletions(-) create mode 100644 jans-config-api/server/src/main/java/io/jans/configapi/rest/resource/auth/SessionResource.java create mode 100644 jans-config-api/server/src/main/java/io/jans/configapi/service/auth/SessionService.java create mode 100644 jans-config-api/server/src/test/resources/feature/session/session.feature diff --git a/jans-config-api/common/src/main/java/io/jans/configapi/util/ApiAccessConstants.java b/jans-config-api/common/src/main/java/io/jans/configapi/util/ApiAccessConstants.java index 798f354b599..1c2c0ba94b1 100644 --- a/jans-config-api/common/src/main/java/io/jans/configapi/util/ApiAccessConstants.java +++ b/jans-config-api/common/src/main/java/io/jans/configapi/util/ApiAccessConstants.java @@ -73,5 +73,8 @@ private ApiAccessConstants() { public static final String AGAMA_WRITE_ACCESS = "https://jans.io/oauth/config/agama.write"; public static final String AGAMA_DELETE_ACCESS = "https://jans.io/oauth/config/agama.delete"; + public static final String JANS_AUTH_SESSION_READ_ACCESS = "https://jans.io/oauth/jans-auth-server/session.readonly"; + public static final String JANS_AUTH_SESSION_DELETE_ACCESS = "https://jans.io/oauth/jans-auth-server/session.delete"; + public static final String JANS_AUTH_REVOKE_SESSION = "revoke_session"; } diff --git a/jans-config-api/common/src/main/java/io/jans/configapi/util/ApiConstants.java b/jans-config-api/common/src/main/java/io/jans/configapi/util/ApiConstants.java index bc8e68e0077..c1c46b16aa9 100644 --- a/jans-config-api/common/src/main/java/io/jans/configapi/util/ApiConstants.java +++ b/jans-config-api/common/src/main/java/io/jans/configapi/util/ApiConstants.java @@ -25,7 +25,7 @@ private ApiConstants() {} public static final String REQUEST_OBJECT = "/request_object"; public static final String UMA = "/uma"; public static final String DYN_REGISTRATION = "/dyn_registration"; - public static final String SESSIONID = "/sessionid"; + public static final String SESSION = "/session"; public static final String CLIENTS = "/clients"; public static final String OPENID = "/openid"; public static final String SCOPES = "/scopes"; @@ -76,6 +76,8 @@ private ApiConstants() {} public static final String USERNAME_PATH = "/{username}"; public static final String CLIENTID_PATH = "/{clientId}"; public static final String CREATORID_PATH = "/{creatorId}"; + public static final String SESSIONID_PATH = "/{sessionId}"; + public static final String USERDN_PATH = "/{userDn}"; public static final String AGAMA = "/agama"; public static final String QNAME_PATH = "{qname}"; public static final String ENABLED = "enabled"; @@ -98,6 +100,8 @@ private ApiConstants() {} public static final String CLIENTID = "clientId"; public static final String CREATOR = "creator"; public static final String CREATORID = "creatorId"; + public static final String SESSIONID = "sessionId"; + public static final String USERDN = "userDn"; public static final String ALL = "all"; diff --git a/jans-config-api/docs/jans-config-api-swagger.yaml b/jans-config-api/docs/jans-config-api-swagger.yaml index 8b13a7110e3..609e96efae7 100644 --- a/jans-config-api/docs/jans-config-api-swagger.yaml +++ b/jans-config-api/docs/jans-config-api-swagger.yaml @@ -35,6 +35,7 @@ tags: - name: Statistics - User - name: Health - Check - name: Server Stats + - name: Auth - Session Management - name: Configuration – User Management - name: SCIM - Config Management - name: Organization Configuration @@ -2815,6 +2816,57 @@ paths: $ref: '#/components/schemas/StatsData' '500': description: Internal Server Error + + /jans-config-api/api/v1/jans-auth-server/session: + get: + summary: Returns current session. + description: Returns current session. + operationId: get-sessions + security: + - oauth2: [https://jans.io/oauth/jans-auth-server/session.readonly revoke_session] + tags: + - Auth - Session Management + responses: + '200': + description: OK + content: + application/json: + schema: + title: Auth session + description: List of auth session. + type: array + items: + $ref: '#/components/schemas/SessionId' + '401': + $ref: '#/components/responses/Unauthorized' + '500': + description: Internal Server Error + + /jans-config-api/api/v1/jans-auth-server/session/{userDn}: + parameters: + - name: userDn + in: path + description: User domain name. + required: true + schema: + type: string + post: + summary: Revoke all sessions by userDn. + description: Revoke all sessions by userDn. + operationId: revoke-user-session + security: + - oauth2: [https://jans.io/oauth/jans-auth-server/session.delete] + tags: + - Auth - Session Management + responses: + '200': + description: Ok + '401': + $ref: '#/components/responses/Unauthorized' + '404': + description: Not Found + '500': + description: Internal Server Error /jans-config-api/mgt/configuser: get: @@ -3698,7 +3750,8 @@ components: https://jans.io/oauth/config/agama.readonly: View Agama Flow related information https://jans.io/oauth/config/agama.write: Manage Agama Flow related information https://jans.io/oauth/config/agama.delete: Delete Agama Flow related information - + https://jans.io/oauth/jans-auth-server/session.readonly: View Session related information + https://jans.io/oauth/jans-auth-server/session.delete: Delete Session information responses: Found: @@ -7471,3 +7524,69 @@ components: codeError: type: string description: Errors in the flow source detected by Agama transpiler + + SessionId: + title: Session details + description: Session details + type: object + properties: + dn: + type: string + description: Domain name. + id: + type: string + description: Unique session id + outsideSid: + type: string + description: User session id + lastUsedAt: + description: Timestamp of session used last time. + type: string + format: date + userDn: + description: Session user domain name. + type: string + authenticationTime: + description: Session authentication time. + type: string + format: date + state: + description: Session status + type: string + enum: + - authenticated + - unauthenticated + sessionState: + description: state of session. + type: string + permissionGranted: + type: boolean + description: Boolean flag indicated if permission granted + isJwt: + type: boolean + description: Boolean flag indicated if jwt + jwt: + type: string + description: Jwt + permissionGrantedMap: + description: Map containing permission. + type: object + additionalProperties: + type: boolean + sessionAttributes: + description: Session attributes + type: object + additionalProperties: + type: string + expirationDate: + description: Expiration date. + type: string + format: date + deletable: + type: boolean + description: If permission is deletable + creationDate: + description: Session creation date. + type: string + format: date + \ No newline at end of file diff --git a/jans-config-api/profiles/local/test.properties b/jans-config-api/profiles/local/test.properties index 763fc6813ac..6a7582b4f0c 100644 --- a/jans-config-api/profiles/local/test.properties +++ b/jans-config-api/profiles/local/test.properties @@ -1,9 +1,9 @@ #LOCAL -test.scopes=https://jans.io/oauth/config/acrs.readonly https://jans.io/oauth/config/acrs.write https://jans.io/oauth/config/attributes.readonly https://jans.io/oauth/config/attributes.write https://jans.io/oauth/config/attributes.delete https://jans.io/oauth/config/cache.readonly https://jans.io/oauth/config/cache.write https://jans.io/oauth/config/openid/clients.readonly https://jans.io/oauth/config/openid/clients.write https://jans.io/oauth/config/openid/clients.delete https://jans.io/oauth/jans-auth-server/config/properties.readonly https://jans.io/oauth/jans-auth-server/config/properties.write https://jans.io/oauth/config/smtp.readonly https://jans.io/oauth/config/smtp.write https://jans.io/oauth/config/smtp.delete https://jans.io/oauth/config/database/couchbase.readonly https://jans.io/oauth/config/database/couchbase.write https://jans.io/oauth/config/database/couchbase.delete https://jans.io/oauth/config/scripts.readonly https://jans.io/oauth/config/scripts.write https://jans.io/oauth/config/scripts.delete https://jans.io/oauth/config/fido2.readonly https://jans.io/oauth/config/fido2.write https://jans.io/oauth/config/jwks.readonly https://jans.io/oauth/config/jwks.write https://jans.io/oauth/config/database/ldap.readonly https://jans.io/oauth/config/database/ldap.write https://jans.io/oauth/config/database/ldap.delete https://jans.io/oauth/config/logging.readonly https://jans.io/oauth/config/logging.write https://jans.io/oauth/config/scopes.readonly https://jans.io/oauth/config/scopes.write https://jans.io/oauth/config/scopes.delete https://jans.io/oauth/config/uma/resources.readonly https://jans.io/oauth/config/uma/resources.write https://jans.io/oauth/config/uma/resources.delete https://jans.io/oauth/config/database/sql.readonly https://jans.io/oauth/config/database/sql.write https://jans.io/oauth/config/database/sql.delete https://jans.io/oauth/config/stats.readonly jans_stat https://jans.io/scim/users.read https://jans.io/scim/users.write https://jans.io/oauth/config/scim/users.read https://jans.io/oauth/config/scim/users.write https://jans.io/scim/config.readonly https://jans.io/scim/config.write https://jans.io/oauth/config/organization.readonly https://jans.io/oauth/config/organization.write https://jans.io/oauth/config/user.readonly https://jans.io/oauth/config/user.write https://jans.io/oauth/config/user.delete https://jans.io/oauth/config/agama.readonly https://jans.io/oauth/config/agama.write https://jans.io/oauth/config/agama.delete +test.scopes=https://jans.io/oauth/config/acrs.readonly https://jans.io/oauth/config/acrs.write https://jans.io/oauth/config/attributes.readonly https://jans.io/oauth/config/attributes.write https://jans.io/oauth/config/attributes.delete https://jans.io/oauth/config/cache.readonly https://jans.io/oauth/config/cache.write https://jans.io/oauth/config/openid/clients.readonly https://jans.io/oauth/config/openid/clients.write https://jans.io/oauth/config/openid/clients.delete https://jans.io/oauth/jans-auth-server/config/properties.readonly https://jans.io/oauth/jans-auth-server/config/properties.write https://jans.io/oauth/config/smtp.readonly https://jans.io/oauth/config/smtp.write https://jans.io/oauth/config/smtp.delete https://jans.io/oauth/config/database/couchbase.readonly https://jans.io/oauth/config/database/couchbase.write https://jans.io/oauth/config/database/couchbase.delete https://jans.io/oauth/config/scripts.readonly https://jans.io/oauth/config/scripts.write https://jans.io/oauth/config/scripts.delete https://jans.io/oauth/config/fido2.readonly https://jans.io/oauth/config/fido2.write https://jans.io/oauth/config/jwks.readonly https://jans.io/oauth/config/jwks.write https://jans.io/oauth/config/database/ldap.readonly https://jans.io/oauth/config/database/ldap.write https://jans.io/oauth/config/database/ldap.delete https://jans.io/oauth/config/logging.readonly https://jans.io/oauth/config/logging.write https://jans.io/oauth/config/scopes.readonly https://jans.io/oauth/config/scopes.write https://jans.io/oauth/config/scopes.delete https://jans.io/oauth/config/uma/resources.readonly https://jans.io/oauth/config/uma/resources.write https://jans.io/oauth/config/uma/resources.delete https://jans.io/oauth/config/database/sql.readonly https://jans.io/oauth/config/database/sql.write https://jans.io/oauth/config/database/sql.delete https://jans.io/oauth/config/stats.readonly jans_stat https://jans.io/scim/users.read https://jans.io/scim/users.write https://jans.io/oauth/config/scim/users.read https://jans.io/oauth/config/scim/users.write https://jans.io/scim/config.readonly https://jans.io/scim/config.write https://jans.io/oauth/config/organization.readonly https://jans.io/oauth/config/organization.write https://jans.io/oauth/config/user.readonly https://jans.io/oauth/config/user.write https://jans.io/oauth/config/user.delete https://jans.io/oauth/config/agama.readonly https://jans.io/oauth/config/agama.write https://jans.io/oauth/config/agama.delete https://jans.io/oauth/jans-auth-server/session.readonly https://jans.io/oauth/jans-auth-server/session.delete revoke_session # jans.server token.endpoint=https://jans.server1/jans-auth/restv1/token token.grant.type=client_credentials -test.client.id=1800.e9131b86-f39f-421c-9dde-b7f90c21a2fe -test.client.secret=zur7eMIXyDTu +test.client.id=1800.f32764fe-81ca-4735-8443-2cb9f714df3b +test.client.secret=UKeuz96lEage test.issuer=https://jans.server1 \ No newline at end of file diff --git a/jans-config-api/server/src/main/java/io/jans/configapi/rest/ApiApplication.java b/jans-config-api/server/src/main/java/io/jans/configapi/rest/ApiApplication.java index 60b41684934..7282ed98df3 100644 --- a/jans-config-api/server/src/main/java/io/jans/configapi/rest/ApiApplication.java +++ b/jans-config-api/server/src/main/java/io/jans/configapi/rest/ApiApplication.java @@ -48,6 +48,7 @@ public Set> getClasses() { classes.add(OrganizationResource.class); classes.add(SqlConfigurationResource.class); classes.add(AgamaResource.class); + classes.add(SessionResource.class); return classes; } diff --git a/jans-config-api/server/src/main/java/io/jans/configapi/rest/resource/auth/SessionResource.java b/jans-config-api/server/src/main/java/io/jans/configapi/rest/resource/auth/SessionResource.java new file mode 100644 index 00000000000..b83b03c9ac4 --- /dev/null +++ b/jans-config-api/server/src/main/java/io/jans/configapi/rest/resource/auth/SessionResource.java @@ -0,0 +1,54 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.rest.resource.auth; + +import io.jans.as.common.model.session.SessionId; +import io.jans.configapi.core.rest.ProtectedApi; + +import io.jans.configapi.service.auth.SessionService; +import io.jans.configapi.util.ApiAccessConstants; +import io.jans.configapi.util.ApiConstants; + +import jakarta.inject.Inject; +import jakarta.validation.constraints.NotNull; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import java.util.List; + +import org.slf4j.Logger; + +@Path(ApiConstants.JANS_AUTH + ApiConstants.SESSION) +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class SessionResource extends ConfigBaseResource { + + @Inject + Logger log; + + @Inject + SessionService sessionService; + + @GET + @ProtectedApi(scopes = { ApiAccessConstants.JANS_AUTH_SESSION_READ_ACCESS }) + public Response getAllSessions() { + final List sessions = sessionService.getSessions(); + logger.debug("sessions:{}", sessions); + return Response.ok(sessions).build(); + } + + @POST + @ProtectedApi(scopes = { ApiAccessConstants.JANS_AUTH_SESSION_DELETE_ACCESS, ApiAccessConstants.JANS_AUTH_REVOKE_SESSION }) + @Path(ApiConstants.USERDN_PATH) + public Response getAppConfiguration(@PathParam(ApiConstants.USERDN) @NotNull String userDn) { + logger.debug("userDn:{}", userDn); + sessionService.revokeSession(userDn); + return Response.ok().build(); + } + +} diff --git a/jans-config-api/server/src/main/java/io/jans/configapi/security/client/AuthClientFactory.java b/jans-config-api/server/src/main/java/io/jans/configapi/security/client/AuthClientFactory.java index f338875f836..4b9d1b65f94 100644 --- a/jans-config-api/server/src/main/java/io/jans/configapi/security/client/AuthClientFactory.java +++ b/jans-config-api/server/src/main/java/io/jans/configapi/security/client/AuthClientFactory.java @@ -13,6 +13,8 @@ import io.jans.as.client.service.StatService; import io.jans.as.client.JwkResponse; +import io.jans.as.client.RevokeSessionResponse; +import io.jans.as.client.RevokeSessionRequest; import io.jans.as.client.TokenRequest; import io.jans.as.client.TokenResponse; import io.jans.as.client.service.IntrospectionService; @@ -45,7 +47,6 @@ @ApplicationScoped public class AuthClientFactory { - private static final String CONTENT_TYPE = "Content-Type"; private static Logger log = LoggerFactory.getLogger(AuthClientFactory.class); @@ -55,7 +56,7 @@ public static IntrospectionService getIntrospectionService(String url, boolean f } public static IntrospectionResponse getIntrospectionResponse(String url, String header, String token, - boolean followRedirects) { + boolean followRedirects) { log.debug("Introspect Token - url:{}, header:{}, token:{} ,followRedirects:{} ", url, header, token, followRedirects); ResteasyWebTarget target = (ResteasyWebTarget) ClientBuilder.newClient() @@ -64,13 +65,14 @@ public static IntrospectionResponse getIntrospectionResponse(String url, String return proxy.introspectToken(header, token); } - public static JsonNode getStatResponse(String url, String token, String month, String startMonth, String endMonth, String format) { + public static JsonNode getStatResponse(String url, String token, String month, String startMonth, String endMonth, + String format) { if (log.isDebugEnabled()) { - log.debug("Stat Response Token - url:{}, token:{}, month:{}, startMonth:{}, endMonth:{}, format:{} ", escapeLog(url), escapeLog(token), - escapeLog(month), escapeLog(startMonth), escapeLog(endMonth), escapeLog(format)); + log.debug("Stat Response Token - url:{}, token:{}, month:{}, startMonth:{}, endMonth:{}, format:{} ", + escapeLog(url), escapeLog(token), escapeLog(month), escapeLog(startMonth), escapeLog(endMonth), + escapeLog(format)); } - ResteasyWebTarget webTarget = (ResteasyWebTarget) ClientBuilder.newClient() - .target(url); + ResteasyWebTarget webTarget = (ResteasyWebTarget) ClientBuilder.newClient().target(url); StatService statService = webTarget.proxy(StatService.class); return statService.stat(token, month, startMonth, endMonth, format); } @@ -89,7 +91,7 @@ public static JsonNode getHealthCheckResponse(String url) { } public static TokenResponse requestAccessToken(final String tokenUrl, final String clientId, - final String clientSecret, final String scope) { + final String clientSecret, final String scope) { log.debug("Request for Access Token - tokenUrl:{}, clientId:{}, clientSecret:{}, scope:{} ", tokenUrl, clientId, clientSecret, scope); Response response = null; @@ -140,8 +142,7 @@ private static IntrospectionService createIntrospectionService(String url, boole ApacheHttpClient43Engine engine = null; try { engine = ClientFactoryUtil.createEngine(followRedirects); - ResteasyWebTarget resteasyWebTarget = (ResteasyWebTarget) ClientBuilder - .newClient().target(url); + ResteasyWebTarget resteasyWebTarget = (ResteasyWebTarget) ClientBuilder.newClient().target(url); return resteasyWebTarget.proxy(IntrospectionService.class); } finally { if (engine != null) { @@ -189,6 +190,35 @@ public static JSONWebKeySet getJSONWebKeys(String jwksUri) { return null; } + public static RevokeSessionResponse revokeSession(String url, String token, String userId) { + log.debug("Request for Access Token - url:{}, token:{}, userId:{} ", url, + token, userId); + Response response = null; + try { + RevokeSessionRequest revokeSessionRequest = new RevokeSessionRequest("uid", "test"); + + Builder request = getClientBuilder(url); + request.header("Authorization", "Basic " + token); + request.header(CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED); + final MultivaluedHashMap multivaluedHashMap = new MultivaluedHashMap<>( + revokeSessionRequest.getParameters()); + response = request.post(Entity.form(multivaluedHashMap)); + log.trace("Response for Access Token - response:{}", response); + if (response.getStatus() == 200) { + String entity = response.readEntity(String.class); + RevokeSessionResponse revokeSessionResponse = new RevokeSessionResponse(); + revokeSessionResponse.setEntity(entity); + revokeSessionResponse.injectDataFromJson(entity); + return revokeSessionResponse; + } + } finally { + + if (response != null) { + response.close(); + } + } + return null; + } private static Builder getClientBuilder(String url) { return ClientBuilder.newClient().target(url).request(); diff --git a/jans-config-api/server/src/main/java/io/jans/configapi/security/service/OpenIdService.java b/jans-config-api/server/src/main/java/io/jans/configapi/security/service/OpenIdService.java index 56f77e3083b..e116fa3ad4a 100644 --- a/jans-config-api/server/src/main/java/io/jans/configapi/security/service/OpenIdService.java +++ b/jans-config-api/server/src/main/java/io/jans/configapi/security/service/OpenIdService.java @@ -45,18 +45,10 @@ public IntrospectionService getIntrospectionService() { return introspectionService; } - public String getIntrospectionEndpoint() { - return configurationService.find().getIntrospectionEndpoint(); - } - - public String getTokenEndpoint() { - return configurationService.find().getTokenEndpoint(); - } - public IntrospectionResponse getIntrospectionResponse(String header, String token, String issuer) throws JsonProcessingException { log.debug("oAuth Introspection request , header:{}, token:{}, issuer:{}", header, token, issuer); - String introspectionUrl = getIntrospectionEndpoint(); + String introspectionUrl = authUtil.getIntrospectionEndpoint(); if (StringUtils.isNotBlank(issuer)) { introspectionUrl = AuthClientFactory.getIntrospectionEndpoint(issuer); log.trace("oAuth Issuer's introspectionUrl:{}", introspectionUrl); @@ -67,13 +59,8 @@ public IntrospectionResponse getIntrospectionResponse(String header, String toke } public String requestAccessToken(final String clientId, final List scope) { - log.info("oAuth request AccessToken - clientId:{}, scope:{} ", clientId, scope); - String tokenUrl = getTokenEndpoint(); - Token token = authUtil.requestAccessToken(tokenUrl, clientId, scope); - log.info("oAuth AccessToken response - token:{}", token); - if (token != null) { - return token.getAccessToken(); - } - return null; + String accessToken = authUtil.requestAccessToken(clientId, scope); + log.info("oAuth AccessToken response - accessToken:{}", accessToken); + return accessToken; } } diff --git a/jans-config-api/server/src/main/java/io/jans/configapi/service/auth/SessionService.java b/jans-config-api/server/src/main/java/io/jans/configapi/service/auth/SessionService.java new file mode 100644 index 00000000000..e389424863f --- /dev/null +++ b/jans-config-api/server/src/main/java/io/jans/configapi/service/auth/SessionService.java @@ -0,0 +1,100 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.service.auth; + +import io.jans.service.CacheService; +import io.jans.as.common.model.session.SessionId; +import io.jans.as.common.model.session.SessionIdState; +import io.jans.as.model.config.StaticConfiguration; +import io.jans.orm.PersistenceEntryManager; +import io.jans.orm.search.filter.Filter; +import io.jans.util.StringHelper; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.NotFoundException; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; + +@ApplicationScoped +public class SessionService { + + @Inject + PersistenceEntryManager persistenceEntryManager; + + @Inject + StaticConfiguration staticConfiguration; + + @Inject + CacheService cacheService; + + @Inject + private Logger logger; + + private String getDnForSession(String sessionId) { + if (StringHelper.isEmpty(sessionId)) { + return staticConfiguration.getBaseDn().getSessions(); + } + return String.format("jansId=%s,%s", sessionId, staticConfiguration.getBaseDn().getSessions()); + } + + public SessionId getSessionById(String sid) { + logger.debug("Get Session by sid:{}", sid); + SessionId sessionId = null; + try { + sessionId = persistenceEntryManager.find(SessionId.class, getDnForSession(sid)); + } catch (Exception ex) { + logger.error("Failed to load session entry", ex); + } + return sessionId; + } + + public List getAllSessions(int sizeLimit) { + logger.debug("Get All Session sizeLimit:{}", sizeLimit); + return persistenceEntryManager.findEntries(getDnForSession(null), SessionId.class, null, sizeLimit); + } + + public List getAllSessions() { + return persistenceEntryManager.findEntries(getDnForSession(null), SessionId.class, null); + } + + public List getSessions() { + List sessionList = persistenceEntryManager.findEntries(getDnForSession(null), SessionId.class, null, + 0); + logger.debug("All sessionList:{}", sessionList); + + sessionList.sort((SessionId s1, SessionId s2) -> s2.getCreationDate().compareTo(s1.getCreationDate())); + logger.debug("Sorted Session sessionList:{}", sessionList); + return sessionList; + } + + public void revokeSession(String userDn) { + logger.debug("Revoke session userDn:{}, cacheService:{}", userDn, cacheService); + + if (StringUtils.isNotBlank(userDn)) { + Filter filter = Filter.createANDFilter(Filter.createEqualityFilter("jansUsrDN", userDn), + Filter.createEqualityFilter("jansState", SessionIdState.AUTHENTICATED)); + + List sessionList = persistenceEntryManager.findEntries(getDnForSession(null), SessionId.class, + filter); + logger.debug("User sessionList:{}", sessionList); + + if (sessionList == null || sessionList.isEmpty()) { + throw new NotFoundException( + "No " + SessionIdState.AUTHENTICATED + " session exists for the user '" + userDn + "'!!!"); + } + + sessionList.stream().forEach(session -> { + persistenceEntryManager.remove(session.getDn(), SessionId.class); + cacheService.remove(session.getDn()); + }); + } + + } + +} diff --git a/jans-config-api/server/src/main/java/io/jans/configapi/util/AuthUtil.java b/jans-config-api/server/src/main/java/io/jans/configapi/util/AuthUtil.java index ee3e40b4d78..9509d95fc69 100644 --- a/jans-config-api/server/src/main/java/io/jans/configapi/util/AuthUtil.java +++ b/jans-config-api/server/src/main/java/io/jans/configapi/util/AuthUtil.java @@ -1,6 +1,7 @@ package io.jans.configapi.util; import com.unboundid.ldap.sdk.DN; +import io.jans.as.client.RevokeSessionResponse; import io.jans.as.client.TokenResponse; import io.jans.as.common.model.registration.Client; import io.jans.as.common.service.common.EncryptionService; @@ -69,6 +70,18 @@ public String getAuthOpenidConfigurationUrl() { public String getIssuer() { return this.configurationService.find().getIssuer(); } + + public String getIntrospectionEndpoint() { + return configurationService.find().getIntrospectionEndpoint(); + } + + public String getTokenEndpoint() { + return configurationService.find().getTokenEndpoint(); + } + + public String getEndSessionEndpoint() { + return this.configurationService.find().getEndSessionEndpoint(); + } public String getServiceUrl(String url) { return this.getIssuer() + url; @@ -248,8 +261,19 @@ private void addMethodScopes(ResourceInfo resourceInfo, List scopes) { scopes.addAll(Stream.of(methodAnnotation.scopes()).collect(Collectors.toList())); } } + + public String requestAccessToken(final String clientId, final List scope) { + log.info("Request for AccessToken - clientId:{}, scope:{} ", clientId, scope); + String tokenUrl = getTokenEndpoint(); + Token token = getAccessToken(tokenUrl, clientId, scope); + log.info("oAuth AccessToken response - token:{}", token); + if (token != null) { + return token.getAccessToken(); + } + return null; + } - public Token requestAccessToken(final String tokenUrl, final String clientId, final List scopes) { + public Token getAccessToken(final String tokenUrl, final String clientId, final List scopes) { log.debug("Access Token Request - tokenUrl:{}, clientId:{}, scopes:{}", tokenUrl, clientId, scopes); // Get clientSecret @@ -409,5 +433,20 @@ public boolean isValidDn(String dn, boolean strictNameChecking) { } + public RevokeSessionResponse revokeSession(final String url,final String token, final String userId) { + log.debug("Revoke session Request - url:{}, token:{}, userId:{}", url, token, userId); + + + + RevokeSessionResponse revokeSessionResponse = AuthClientFactory.revokeSession(url, token,userId); + log.debug("revokeSessionResponse:{}",revokeSessionResponse); + if (revokeSessionResponse != null) { + + log.debug("revokeSessionResponse.getEntity():{}, revokeSessionResponse.getStatus():{} ", revokeSessionResponse.getEntity(), revokeSessionResponse.getStatus()); + + + } + return revokeSessionResponse; + } } diff --git a/jans-config-api/server/src/main/resources/config-api-rs-protect.json b/jans-config-api/server/src/main/resources/config-api-rs-protect.json index 896bc78f3cb..230e25310a2 100644 --- a/jans-config-api/server/src/main/resources/config-api-rs-protect.json +++ b/jans-config-api/server/src/main/resources/config-api-rs-protect.json @@ -677,5 +677,23 @@ } ] } + , + { + "path":"/jans-config-api/api/v1/jans-auth-server/session", + "conditions":[ + { + "httpMethods":["GET"], + "scopes":[ + "https://jans.io/oauth/jans-auth-server/session.readonly" + ] + }, + { + "httpMethods":["POST"], + "scopes":[ + "https://jans.io/oauth/jans-auth-server/session.delete" + ] + } + ] + } ] } \ No newline at end of file diff --git a/jans-config-api/server/src/test/resources/feature/agama/agama.feature b/jans-config-api/server/src/test/resources/feature/agama/agama.feature index 5b503944f00..30fd9e7529c 100644 --- a/jans-config-api/server/src/test/resources/feature/agama/agama.feature +++ b/jans-config-api/server/src/test/resources/feature/agama/agama.feature @@ -52,7 +52,7 @@ Scenario: Fetch agama flow by name Then status 200 And print response - +@ignore @CreateUpdateDelete Scenario: Create, update and delete agama flow #Create agama flow @@ -124,7 +124,7 @@ Scenario: Create, update and delete agama flow Then status 204 And print response - +@ignore @CreateFlowWithDataInRequestBodyUpdateDelete Scenario: Create agama flow with source data in request body #Create agama flow @@ -188,7 +188,7 @@ Scenario: Create agama flow with source data in request body Then status 204 And print response - +@ignore @CreateAndUpdateFlowWithDataInRequestBodyUpdateDelete Scenario: Create agama flow with source data in request body #Create agama flow @@ -253,7 +253,7 @@ Scenario: Create agama flow with source data in request body Then status 204 And print response - +@ignore @CreateAndPatchFlowAndDelete Scenario: Create and Patch agama flow #Create agama flow diff --git a/jans-config-api/server/src/test/resources/feature/session/session.feature b/jans-config-api/server/src/test/resources/feature/session/session.feature new file mode 100644 index 00000000000..e065087d7ca --- /dev/null +++ b/jans-config-api/server/src/test/resources/feature/session/session.feature @@ -0,0 +1,40 @@ + +Feature: Session flow + +Background: +* def mainUrl = session_url + +Scenario: Fetch all session + Given url mainUrl + When method GET + Then status 401 + And print response + + +Scenario: Fetch all session + Given url mainUrl + And print 'accessToken = '+accessToken + And header Authorization = 'Bearer ' + accessToken + When method GET + Then status 200 + And print response + + +@ignore +Scenario: Revoke user session + Given url mainUrl + And print 'accessToken = '+accessToken + And header Authorization = 'Bearer ' + accessToken + When method GET + Then status 200 + And print response + Then def result = response[0] + And print result + And def userDn = result.userDn + Given url mainUrl + '/' +userDn + And header Authorization = 'Bearer ' + accessToken + And request {} + When method POST + Then status 200 + And print response + diff --git a/jans-config-api/server/src/test/resources/karate-config-jenkins.js b/jans-config-api/server/src/test/resources/karate-config-jenkins.js index bd49002e138..e91549d462e 100644 --- a/jans-config-api/server/src/test/resources/karate-config-jenkins.js +++ b/jans-config-api/server/src/test/resources/karate-config-jenkins.js @@ -62,6 +62,7 @@ function() { org_configuration_url: baseUrl + '/jans-config-api/api/v1/org', user_url: baseUrl + '/jans-config-api/api/v1/user', agama_url: baseUrl + '/jans-config-api/api/v1/agama', + session_url: baseUrl + '/jans-config-api/api/v1/jans-auth-server/session', }; karate.configure('connectTimeout', 30000); diff --git a/jans-config-api/server/src/test/resources/karate-config.js b/jans-config-api/server/src/test/resources/karate-config.js index 501c158068c..d58163a8295 100644 --- a/jans-config-api/server/src/test/resources/karate-config.js +++ b/jans-config-api/server/src/test/resources/karate-config.js @@ -62,6 +62,7 @@ function() { org_configuration_url: baseUrl + '/jans-config-api/api/v1/org', user_url: baseUrl + '/jans-config-api/api/v1/user', agama_url: baseUrl + '/jans-config-api/api/v1/agama', + session_url: baseUrl + '/jans-config-api/api/v1/jans-auth-server/session', }; karate.configure('connectTimeout', 30000);