Skip to content

Commit

Permalink
feat(jans-auth-server): Token Status List support (#8620)
Browse files Browse the repository at this point in the history
* chore(jans-auth-server): renamed OXAUTH_UMA_TICKET -> UMA_TICKET

Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth-server): Token Status List support

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* fix(jans-auth-server): corrected requestContext and azd decoding

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth-server): added token status list endpoint and status claim with index.

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth): new cluster beans and services

Signed-off-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>

* feat(jans-auth-server): added head index to list

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth): move beans to core model

Signed-off-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>

* feat(jans-auth): add index range to TokenPool

Signed-off-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>

* feat(jans-auth-server): added application/statuslist+json support

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth): add methods to allocate/release TokenPool

Signed-off-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>

* feat(jans-auth): fix TokenPool sort

Signed-off-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>

* feat(jans-auth): implement method to get nextIndex for token

Signed-off-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>

* feat(jans-auth): implement method to get nextIndex for token

Signed-off-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>

* feat(jans-auth): instead of using token list status use expiration date

Signed-off-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>

* fix(jans-auth-server): fixed index during list joins and npe on nextIndex.

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth-server): populate statusListIndex in access and id tokens

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth): add ClusterNode services

Signed-off-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>

* feat(jans-auth): add node base dn

Signed-off-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>

* feat(jans-auth-server): added status list update on revoke

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* fix after merge

Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth): add schema for new entries

Signed-off-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>

* feat(jans-auth): fix allocate

Signed-off-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>

* feat(jans-auth): fix cluster nodes expiration

Signed-off-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>

* feat(jans-auth-server): added status list as jwt support

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth): Deprecate TokenPoolStatus

Signed-off-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>

* feat(jans-auth): implement updateWithLock for concurent lock on revoke

Signed-off-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>

* feat(jans-auth-server): use updateWithLock during status update index

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth-server): update status list on token revoke in separate thread

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth-server): renamed TokenPool -> StatusTokenPool, TokenPoolService -> StatusTokenPoolService

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth-server): removed token head index (we are using status token pools instead)

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth-server): added status list to swagger

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth-server): added ou=node,o=jans to config

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth-server): throw configuration exception if node baseDn is missed

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth-server): set status_list feature flag enabled by default

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* fix(jans-auth-server): fixed node allocation

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* fix(jans-auth-server): corrected bug in getClusterNodeLast

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth-server): keep lockKey static and save in jansNode after locking

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* fix(jans-auth-server): different fixes for cluster node management

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* fix(jans-auth-server): fixed allocation of status index pools

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* chore(jans-auth-server): added more logs for status index pool allocation

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth): igore timezone when DB is PostgresSQL

Signed-off-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>

* feat(jans-auth): fetch all node entries if DB is LDAP

Signed-off-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>

* feat(jans-auth-server): added status list client

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* fix(jans-auth-server): fixed pool allocation

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* chore(jans-auth-server): renamed endpoint /token_status_list -> /status_list

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-orm): resovle bean property name with AttributeName #8773

* chore(jans-auth-server): renamed token_status_list -> status_list

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* chore(jans-auth-server): token statuses VALID - 0, INVALID - 1

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* chore(jans-auth-server): moved status list to model for re-using

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth-server): added batch index update and fixed concurrent update issue

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth-server): use new index update method in existing revoke code

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* fix(jans-auth-server): fixed status pool index joining

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* chore(jans-auth-server): code improvements

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* test(jans-auth-server): added full integration test for status list

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* test(jans-auth-server): added test for CN case

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* feat(jans-auth-server): mark indexes which we are about to re-use as VALID

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* code re-format

Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>

* docs(config-api): regenerating config swagger api

Signed-off-by: pujavs <pujas.works@gmail.com>

---------

Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>
Signed-off-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>
Signed-off-by: pujavs <pujas.works@gmail.com>
Co-authored-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>
Co-authored-by: pujavs <pujas.works@gmail.com>
Co-authored-by: Mohammad Abudayyeh <47318409+moabu@users.noreply.github.com>
  • Loading branch information
4 people committed Jun 28, 2024
1 parent 1e2a4b0 commit 51101e4
Show file tree
Hide file tree
Showing 71 changed files with 2,595 additions and 164 deletions.
41 changes: 9 additions & 32 deletions agama/transpiler/src/main/java/io/jans/agama/dsl/Transpiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,24 @@
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;

import io.jans.agama.antlr.AuthnFlowLexer;
import io.jans.agama.antlr.AuthnFlowParser;
import io.jans.agama.dsl.error.SyntaxException;
import io.jans.agama.dsl.error.RecognitionErrorListener;
import io.jans.agama.dsl.error.SyntaxException;
import net.sf.saxon.dom.NodeOverNodeInfo;
import net.sf.saxon.s9api.*;
import net.sf.saxon.sapling.SaplingDocument;
import org.antlr.v4.runtime.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import net.sf.saxon.dom.NodeOverNodeInfo;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XPathCompiler;
import net.sf.saxon.s9api.XdmItem;
import net.sf.saxon.s9api.XdmNode;
import net.sf.saxon.sapling.SaplingDocument;

import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Token;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static java.nio.charset.StandardCharsets.UTF_8;

public class Transpiler {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ public static void parse(String json, OpenIdConfigurationResponse response) {
response.setIssuer(jsonObj.optString(ISSUER, null));
response.setAuthorizationEndpoint(jsonObj.optString(AUTHORIZATION_ENDPOINT, null));
response.setAuthorizationChallengeEndpoint(jsonObj.optString(AUTHORIZATION_CHALLENGE_ENDPOINT, null));
response.setStatusListEndpoint(jsonObj.optString(STATUS_LIST_ENDPOINT, null));
response.setTokenEndpoint(jsonObj.optString(TOKEN_ENDPOINT, null));
response.setRevocationEndpoint(jsonObj.optString(REVOCATION_ENDPOINT, null));
response.setSessionRevocationEndpoint(jsonObj.optString(SESSION_REVOCATION_ENDPOINT, null));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class OpenIdConfigurationResponse extends BaseResponse implements Seriali
private String issuer;
private String authorizationEndpoint;
private String authorizationChallengeEndpoint;
private String statusListEndpoint;
private String tokenEndpoint;
private String revocationEndpoint;
private String sessionRevocationEndpoint;
Expand Down Expand Up @@ -239,6 +240,24 @@ public void setAuthorizationChallengeEndpoint(String authorizationChallengeEndpo
this.authorizationChallengeEndpoint = authorizationChallengeEndpoint;
}

/**
* Gets status list
*
* @return status list
*/
public String getStatusListEndpoint() {
return statusListEndpoint;
}

/**
* Sets status list
*
* @param statusListEndpoint status list
*/
public void setStatusListEndpoint(String statusListEndpoint) {
this.statusListEndpoint = statusListEndpoint;
}

/**
* Returns the URL of the Token endpoint.
*
Expand Down Expand Up @@ -1300,6 +1319,7 @@ public String toString() {
"issuer='" + issuer + '\'' +
", authorizationEndpoint='" + authorizationEndpoint + '\'' +
", authorizationChallengeEndpoint='" + authorizationChallengeEndpoint + '\'' +
", statusListEndpoint='" + statusListEndpoint + '\'' +
", tokenEndpoint='" + tokenEndpoint + '\'' +
", revocationEndpoint='" + revocationEndpoint + '\'' +
", userInfoEndpoint='" + userInfoEndpoint + '\'' +
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package io.jans.as.client;

import jakarta.ws.rs.HttpMethod;
import jakarta.ws.rs.client.Invocation;
import org.apache.log4j.Logger;

/**
* @author Yuriy Z
*/
public class StatusListClient extends BaseClient<StatusListRequest, StatusListResponse> {

private static final Logger LOG = Logger.getLogger(StatusListClient.class);

/**
* Constructs a client for status list.
*
* @param url status list endpoint
*/
public StatusListClient(String url) {
super(url);
}

@Override
public String getHttpMethod() {
return HttpMethod.POST;
}

public StatusListResponse exec(StatusListRequest request) {
setRequest(request);
return exec();
}

public StatusListResponse exec() {
initClient();

Invocation.Builder clientRequest = webTarget.request();
applyCookies(clientRequest);

clientRequest.header("Content-Type", request.getContentType());

try {
clientResponse = clientRequest.buildGet().invoke();

final StatusListResponse response = new StatusListResponse(clientResponse);
setResponse(response);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
} finally {
closeConnection();
}

return getResponse();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.jans.as.client;

import io.jans.as.model.config.Constants;

/**
* @author Yuriy Z
*/
public class StatusListRequest extends BaseRequest {

public StatusListRequest() {
setContentType(Constants.CONTENT_TYPE_STATUSLIST_JSON);
}

@Override
public String getQueryString() {
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package io.jans.as.client;

import io.jans.as.model.config.Constants;
import io.jans.as.model.jwt.Jwt;
import io.jans.as.model.session.EndSessionErrorResponseType;
import io.jans.model.tokenstatus.StatusList;
import jakarta.ws.rs.core.Response;
import org.json.JSONObject;

import java.io.IOException;

/**
* @author Yuriy Z
*/
public class StatusListResponse extends BaseResponseWithErrors<EndSessionErrorResponseType> {

private String lst;
private int bits;
private Jwt jwt;

public StatusListResponse() {
}

public StatusListResponse(Response clientResponse) {
super(clientResponse);
injectData(clientResponse);
}

public StatusList getStatusList() throws IOException {
return StatusList.fromEncoded(lst, bits);
}

@Override
public EndSessionErrorResponseType fromString(String params) {
return EndSessionErrorResponseType.fromString(params);
}

public void injectData(Response clientResponse) {
injectErrorIfExistSilently(entity);
if (getErrorType() != null) {
return;
}

if (clientResponse.getStatus() != 200) {
return;
}

final String contentType = clientResponse.getHeaderString("Content-Type");
if (Constants.CONTENT_TYPE_STATUSLIST_JWT.equalsIgnoreCase(contentType)) {
jwt = Jwt.parseSilently(entity);
if (jwt != null) {
final JSONObject statusList = jwt.getClaims().getClaimAsJSON("status_list");
lst = statusList.getString("lst");
bits = statusList.getInt("bits");
}
} else if (Constants.CONTENT_TYPE_STATUSLIST_JSON.equalsIgnoreCase(contentType)) {
final JSONObject json = new JSONObject(entity);
final JSONObject statusList = json.getJSONObject("status_list");
lst = statusList.getString("lst");
bits = statusList.getInt("bits");
} else {
throw new UnsupportedOperationException("Unable to recognize content-type: " + contentType);
}
}

public String getLst() {
return lst;
}

public void setLst(String lst) {
this.lst = lst;
}

public int getBits() {
return bits;
}

public void setBits(int bits) {
this.bits = bits;
}

public Jwt getJwt() {
return jwt;
}

public void setJwt(Jwt jwt) {
this.jwt = jwt;
}

@Override
public String toString() {
return "StatusListResponse{" +
"lst='" + lst + '\'' +
", bits=" + bits +
", jwt=" + jwt +
"} " + super.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public abstract class BaseTest {
protected String gluuConfigurationEndpoint;
protected String tokenEndpoint;
protected String tokenRevocationEndpoint;
protected String statusListEndpoint;
protected String userInfoEndpoint;
protected String clientInfoEndpoint;
protected String checkSessionIFrame;
Expand Down Expand Up @@ -304,6 +305,14 @@ public void setAuthorizationChallengeEndpoint(String authorizationChallengeEndpo
this.authorizationChallengeEndpoint = authorizationChallengeEndpoint;
}

public String getStatusListEndpoint() {
return statusListEndpoint;
}

public void setStatusListEndpoint(String statusListEndpoint) {
this.statusListEndpoint = statusListEndpoint;
}

public String getTokenEndpoint() {
return tokenEndpoint;
}
Expand Down Expand Up @@ -1000,6 +1009,7 @@ public void discovery(ITestContext context) throws Exception {

authorizationEndpoint = response.getAuthorizationEndpoint();
authorizationChallengeEndpoint = response.getAuthorizationChallengeEndpoint();
statusListEndpoint = response.getStatusListEndpoint();
tokenEndpoint = response.getTokenEndpoint();
tokenRevocationEndpoint = response.getRevocationEndpoint();
userInfoEndpoint = response.getUserInfoEndpoint();
Expand All @@ -1024,6 +1034,7 @@ public void discovery(ITestContext context) throws Exception {

authorizationEndpoint = context.getCurrentXmlTest().getParameter("authorizationEndpoint");
authorizationChallengeEndpoint = context.getCurrentXmlTest().getParameter("authorizationChallengeEndpoint");
statusListEndpoint = context.getCurrentXmlTest().getParameter("statusListEndpoint");
tokenEndpoint = context.getCurrentXmlTest().getParameter("tokenEndpoint");
tokenRevocationEndpoint = context.getCurrentXmlTest().getParameter("tokenRevocationEndpoint");
userInfoEndpoint = context.getCurrentXmlTest().getParameter("userInfoEndpoint");
Expand Down
Loading

0 comments on commit 51101e4

Please sign in to comment.