Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 67 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
9c1b30a
chore(jans-auth-server): renamed OXAUTH_UMA_TICKET -> UMA_TICKET
yuriyz May 31, 2024
eb1340f
feat(jans-auth-server): Token Status List support
yuriyz May 31, 2024
d32bca8
fix(jans-auth-server): corrected requestContext and azd decoding
yuriyz May 31, 2024
d5b3ba9
feat(jans-auth-server): added token status list endpoint and status c…
yuriyz Jun 5, 2024
3987f87
feat(jans-auth): new cluster beans and services
yurem Jun 6, 2024
b9c5f3d
feat(jans-auth-server): added head index to list
yuriyz Jun 6, 2024
e79d463
Merge remote-tracking branch 'remotes/origin/token_list_changes' into…
yuriyz Jun 7, 2024
a1acf85
feat(jans-auth): move beans to core model
yurem Jun 7, 2024
ff7a3f9
feat(jans-auth): add index range to TokenPool
yurem Jun 7, 2024
f66c254
Merge branch 'main' into jans-auth-server-8562
yuriyz Jun 7, 2024
e8c6962
feat(jans-auth-server): added application/statuslist+json support
yuriyz Jun 7, 2024
9fc9e89
feat(jans-auth): add methods to allocate/release TokenPool
yurem Jun 7, 2024
2bb825c
feat(jans-auth): add methods to allocate/release TokenPool
yurem Jun 7, 2024
83f36ce
feat(jans-auth): fix TokenPool sort
yurem Jun 7, 2024
723ce03
feat(jans-auth): implement method to get nextIndex for token
yurem Jun 7, 2024
607cb03
feat(jans-auth): implement method to get nextIndex for token
yurem Jun 7, 2024
78bcd84
feat(jans-auth): instead of using token list status use expiration date
yurem Jun 7, 2024
63ab6b6
fix(jans-auth-server): fixed index during list joins and npe on nextI…
yuriyz Jun 7, 2024
80d0efd
Merge remote-tracking branch 'origin/jans-auth-server-8562' into jans…
yuriyz Jun 7, 2024
47b5f9b
feat(jans-auth-server): populate statusListIndex in access and id tokens
yuriyz Jun 7, 2024
5a48866
feat(jans-auth): add ClusterNode services
yurem Jun 10, 2024
4a573da
feat(jans-auth): add node base dn
yurem Jun 10, 2024
3501d12
feat(jans-auth-server): added status list update on revoke
yuriyz Jun 10, 2024
e54e82d
Merge remote-tracking branch 'origin/jans-auth-server-8562' into jans…
yuriyz Jun 10, 2024
192c3fc
fix after merge
yuriyz Jun 10, 2024
fce9bb4
feat(jans-auth): add schema for new entries
yurem Jun 11, 2024
408b7e4
feat(jans-auth): fix allocate
yurem Jun 11, 2024
8ffef42
feat(jans-auth): fix cluster nodes expiration
yurem Jun 11, 2024
2c48d79
merged main
yuriyz Jun 12, 2024
8be4f18
feat(jans-auth-server): added status list as jwt support
yuriyz Jun 12, 2024
f324c8d
feat(jans-auth): Deprecate TokenPoolStatus
yurem Jun 13, 2024
c86dd7e
feat(jans-auth): implement updateWithLock for concurent lock on revoke
yurem Jun 13, 2024
53e5c3a
feat(jans-auth-server): use updateWithLock during status update index
yuriyz Jun 13, 2024
e3cf9d9
feat(jans-auth-server): update status list on token revoke in separat…
yuriyz Jun 13, 2024
84e6d79
feat(jans-auth-server): renamed TokenPool -> StatusTokenPool, TokenPo…
yuriyz Jun 14, 2024
c066c3b
feat(jans-auth-server): removed token head index (we are using status…
yuriyz Jun 18, 2024
39c16ff
feat(jans-auth-server): added status list to swagger
yuriyz Jun 18, 2024
3a36b83
feat(jans-auth-server): added ou=node,o=jans to config
yuriyz Jun 18, 2024
46d9abc
feat(jans-auth-server): throw configuration exception if node baseDn …
yuriyz Jun 18, 2024
f1d4caf
feat(jans-auth-server): set status_list feature flag enabled by default
yuriyz Jun 19, 2024
bd4018c
fix(jans-auth-server): fixed node allocation
yuriyz Jun 19, 2024
214a489
fix(jans-auth-server): corrected bug in getClusterNodeLast
yuriyz Jun 19, 2024
3e7e322
feat(jans-auth-server): keep lockKey static and save in jansNode afte…
yuriyz Jun 19, 2024
c763ed9
fix(jans-auth-server): different fixes for cluster node management
yuriyz Jun 20, 2024
a58599e
fix(jans-auth-server): fixed allocation of status index pools
yuriyz Jun 20, 2024
44fb548
chore(jans-auth-server): added more logs for status index pool alloca…
yuriyz Jun 21, 2024
1f35103
feat(jans-auth): igore timezone when DB is PostgresSQL
yurem Jun 21, 2024
7a18504
feat(jans-auth): fetch all node entries if DB is LDAP
yurem Jun 24, 2024
1719b72
feat(jans-auth-server): added status list client
yuriyz Jun 24, 2024
11b0fbe
fix(jans-auth-server): fixed pool allocation
yuriyz Jun 24, 2024
3445a31
chore(jans-auth-server): renamed endpoint /token_status_list -> /stat…
yuriyz Jun 24, 2024
7180494
feat(jans-orm): resovle bean property name with AttributeName #8773
yurem Jun 24, 2024
36a453e
chore(jans-auth-server): renamed token_status_list -> status_list
yuriyz Jun 25, 2024
17b1431
chore(jans-auth-server): token statuses VALID - 0, INVALID - 1
yuriyz Jun 25, 2024
d5b2e82
chore(jans-auth-server): moved status list to model for re-using
yuriyz Jun 25, 2024
0a1842c
feat(jans-auth-server): added batch index update and fixed concurrent…
yuriyz Jun 26, 2024
ccf2306
feat(jans-auth-server): use new index update method in existing revok…
yuriyz Jun 26, 2024
a143dd0
fix(jans-auth-server): fixed status pool index joining
yuriyz Jun 27, 2024
6ca1e80
chore(jans-auth-server): code improvements
yuriyz Jun 27, 2024
da5f7bb
test(jans-auth-server): added full integration test for status list
yuriyz Jun 28, 2024
2148d90
test(jans-auth-server): added test for CN case
yuriyz Jun 28, 2024
c173c8d
feat(jans-auth-server): mark indexes which we are about to re-use as …
yuriyz Jun 28, 2024
f66b3a7
merged main
yuriyz Jun 28, 2024
e2d73a8
code re-format
yuriyz Jun 28, 2024
14d5dec
docs(config-api): regenerating config swagger api
pujavs Jun 28, 2024
adc3c4a
Merge branch 'main' into jans-auth-server-8562
yuriyz Jun 28, 2024
799831e
Merge branch 'main' into jans-auth-server-8562
moabu Jun 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat(jans-auth): add ClusterNode services
Signed-off-by: Yuriy Movchan <Yuriy.Movchan@gmail.com>
  • Loading branch information
yurem committed Jun 10, 2024
commit 5a48866b3142ef5ba5e66b3c6b30c7ba6c93cff3
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,6 @@ public class ConfigurationFactory extends ApplicationConfigurationFactory {
private long loadedRevision = -1;
private boolean loadedFromLdap = true;

private Integer nodeId = 0;

@PostConstruct
public void init() {
log.info("Initializing ConfigurationFactory ...");
Expand Down Expand Up @@ -604,8 +602,4 @@ public String getContextPath() {
return contextPath;
}

public Integer getNodeId() {
return nodeId ;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,19 @@

package io.jans.as.server.service;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import org.jboss.weld.util.reflection.ParameterizedTypeImpl;
import org.slf4j.Logger;

import com.google.common.collect.Lists;

import io.jans.as.common.service.common.ApplicationFactory;
import io.jans.as.model.common.FeatureFlagType;
import io.jans.as.model.configuration.AppConfiguration;
Expand All @@ -15,6 +27,7 @@
import io.jans.as.server.service.cdi.event.AuthConfigurationEvent;
import io.jans.as.server.service.cdi.event.ReloadAuthScript;
import io.jans.as.server.service.ciba.CibaRequestsProcessorJob;
import io.jans.as.server.service.cluster.ClusterManager;
import io.jans.as.server.service.expiration.ExpirationNotificatorTimer;
import io.jans.as.server.service.external.ExternalAuthenticationService;
import io.jans.as.server.service.logger.LoggerService;
Expand Down Expand Up @@ -69,16 +82,6 @@
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.servlet.ServletContext;
import org.jboss.weld.util.reflection.ParameterizedTypeImpl;
import org.slf4j.Logger;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

/**
* @author Javier Rojas Blum
Expand Down Expand Up @@ -138,6 +141,9 @@ public class AppInitializer {

@Inject
private PythonService pythonService;

@Inject
private ClusterManager clusterManager;

@Inject
private MetricService metricService;
Expand Down Expand Up @@ -248,6 +254,7 @@ public void applicationInitialized(@Observes @Initialized(ApplicationScoped.clas
initSchedulerService();

// Schedule timer tasks
clusterManager.initTimer();
metricService.initTimer();
configurationFactory.initTimer();
loggerService.initTimer(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,43 +10,48 @@

import org.slf4j.Logger;

import io.jans.as.server.model.config.ConfigurationFactory;
import io.jans.as.server.service.cdi.event.TokenPoolUpdateEvent;
import io.jans.model.cluster.ClusterNode;
import io.jans.service.cdi.async.Asynchronous;
import io.jans.service.cdi.event.Scheduled;
import io.jans.service.timer.event.TimerEvent;
import io.jans.service.timer.schedule.TimerSchedule;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.BeforeDestroyed;
import jakarta.enterprise.event.Event;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
import jakarta.servlet.ServletContext;

/**
* @author Yuriy Movchan
* @version 1.0, 06/03/2024
*/
@ApplicationScoped
public class TokenPoolManager {
public class ClusterManager {

@Inject
private Logger log;

@Inject
private ConfigurationFactory configurationFactory;

@Inject
private TokenPoolService tokenPoolService;
private ClusterNodeService clusterNodeService;

@Inject
private Event<TimerEvent> timerEvent;

private AtomicBoolean isActive;

private ClusterNode clusterNode;

@PostConstruct
public void init() {
log.info("Initializing Token Pool Manager ...");
log.info("Initializing Cluster Manager ...");
this.isActive = new AtomicBoolean(false);

this.clusterNode = clusterNodeService.allocate();

log.info("Assigned cluster node id '{}' for this instance", clusterNode.getId());
}

public void initTimer() {
Expand All @@ -70,20 +75,26 @@ public void reloadPoliciesTimerEvent(@Observes @Scheduled TokenPoolUpdateEvent t
}

try {
updateTokenPools();
updateClusterNode();
} catch (Throwable ex) {
log.error("Exception happened while reloading policies", ex);
} finally {
this.isActive.set(false);
}
}

private void updateTokenPools() {
Integer nodeId = configurationFactory.getNodeId();

// TODO: Revoked tokens updates?

private void updateClusterNode() {
clusterNodeService.refresh(clusterNode);
}


public void destroy(@Observes @BeforeDestroyed(ApplicationScoped.class) ServletContext init) {
log.info("Stopping cluster manager...");
clusterNodeService.release(clusterNode);
}

public Integer getClusterNodeId() {
return clusterNode.getId();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@
package io.jans.as.server.service.cluster;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import org.slf4j.Logger;

import io.jans.as.model.config.StaticConfiguration;
import io.jans.model.cluster.ClusterNode;
import io.jans.orm.PersistenceEntryManager;
import io.jans.orm.exception.EntryPersistenceException;
import io.jans.orm.model.PagedResult;
import io.jans.orm.model.SortOrder;
import io.jans.orm.search.filter.Filter;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
Expand All @@ -25,6 +30,9 @@
@ApplicationScoped
public class ClusterNodeService {

public static long DELAY_AFTER_EXPIRATION = 3 * 1000; // 3 minutes
public static String CLUSTER_TYPE_JANS_AUTH = "jans-auth";

@Inject
private Logger log;

Expand Down Expand Up @@ -60,7 +68,7 @@ public ClusterNode getClusterNodeById(Integer id) {
public List<ClusterNode> getAllClusterNodes() {
String clusterNodesBaseDn = staticConfiguration.getBaseDn().getNodes();

return entryManager.findEntries(clusterNodesBaseDn, ClusterNode.class, Filter.createPresenceFilter("jansNum"));
return entryManager.findEntries(clusterNodesBaseDn, ClusterNode.class, Filter.createEqualityFilter("jansType", CLUSTER_TYPE_JANS_AUTH));
}

public List<String> getClusterNodesDns(List<Integer> nodeIds) {
Expand All @@ -76,14 +84,137 @@ public List<String> getClusterNodesDns(List<Integer> nodeIds) {
return clusterNodesDns;
}

public void persist(ClusterNode clusterNode) {
/**
* returns last TokenPool or null if none
*
* @return TokenPool
*/
public ClusterNode getClusterNodeLast() {
String clusterNodesBaseDn = staticConfiguration.getBaseDn().getNodes();

PagedResult<ClusterNode> pagedResult = entryManager.findPagedEntries(clusterNodesBaseDn, ClusterNode.class, Filter.createEqualityFilter("jansType", CLUSTER_TYPE_JANS_AUTH), null, "jansNum", SortOrder.DESCENDING, 1, 1, 1);
if (pagedResult.getEntriesCount() >= 1) {
return pagedResult.getEntries().get(0);
}


return null;
}

/**
* returns a list of expired ClusterNodes
*
* @return list of ClusterNodes
*/
public List<ClusterNode> getClusterNodesExpired() {
String clusterNodesBaseDn = staticConfiguration.getBaseDn().getNodes();

Date expirationDate = new Date(System.currentTimeMillis() + DELAY_AFTER_EXPIRATION);

Filter filter = Filter.createORFilter(Filter.createEqualityFilter("jansType", CLUSTER_TYPE_JANS_AUTH),
Filter.createGreaterOrEqualFilter("jansLastUpd", entryManager.encodeTime(clusterNodesBaseDn, expirationDate)));

return entryManager.findEntries(clusterNodesBaseDn, ClusterNode.class, filter);
}

protected void persist(ClusterNode clusterNode) {
entryManager.persist(clusterNode);
}

public void update(ClusterNode clusterNode) {
entryManager.merge(clusterNode);
}

public ClusterNode allocate() {
// Try to use existing expired entry
List<ClusterNode> clusterNodes = getClusterNodesExpired();

for (ClusterNode clusterNode : clusterNodes) {
// Attempt to set random value in lockKey
String lockKey = UUID.randomUUID().toString();
clusterNode.setLockKey(lockKey);

// Do lock operation in try/catch for safety and do not throw error to upper levels
try {
update(clusterNode);

// Load node after update
ClusterNode lockedClusterNode = getClusterNodeByDn(clusterNode.getDn());

// If lock is ours reset entry and return it
if (lockKey.equals(lockedClusterNode.getLockKey())) {
reset(clusterNode);
return clusterNode;
}
} catch (EntryPersistenceException ex) {
log.trace("Unexpected error happened during entry lock", ex);
}
}

// There are no free entries. server need to add new one with next index
int maxSteps = 10;
do {
ClusterNode lastClusterNode = getClusterNodeLast();

Integer lastClusterNodeIndex = lastClusterNode == null ? 0 : lastClusterNode.getId() + 1;

Date currentTime = new Date();
ClusterNode clusterNode = new ClusterNode();
clusterNode.setId(lastClusterNodeIndex);
clusterNode.setDn(getDnForClusterNode(lastClusterNodeIndex));
clusterNode.setCreationDate(currentTime);
clusterNode.setLastUpdate(currentTime);
clusterNode.setType(CLUSTER_TYPE_JANS_AUTH);

// Attempt to set random value in lockKey
String lockKey = UUID.randomUUID().toString();
clusterNode.setLockKey(lockKey);

// Do persist operation in try/catch for safety and do not throw error to upper
// levels
try {
persist(lastClusterNode);

// Load node after update
ClusterNode lockedClusterNode = getClusterNodeByDn(clusterNode.getDn());

// if lock is ours return it
if (lockKey.equals(lockedClusterNode.getLockKey())) {
return clusterNode;
}

} catch (EntryPersistenceException ex) {
log.trace("Unexpected error happened during entry lock", ex);
}
log.debug("Attempting to persist new token list. Attempt before fail: '{}'", maxSteps);
} while (maxSteps >= 0);

// This should not happens
throw new EntryPersistenceException("Failed to allocate ClusterNode!!!");
}

public void release(ClusterNode clusterNode) {
clusterNode.setLastUpdate(null);
clusterNode.setCreationDate(null);
clusterNode.setLockKey(null);

update(clusterNode);
}

public void refresh(ClusterNode clusterNode) {
clusterNode.setLastUpdate(new Date());

update(clusterNode);
}

public void reset(ClusterNode clusterNode) {
Date currentTime = new Date();
clusterNode.setCreationDate(currentTime);
clusterNode.setLastUpdate(currentTime);

update(clusterNode);
}

public String getDnForClusterNode(Integer id) {
return String.format("jansNum=%d,%s", id, staticConfiguration.getBaseDn().getNodes());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,14 @@ public List<TokenPool> getTokenPoolsExpired() {
return setIndexes(entryManager.findEntries(tokenPoolsBaseDn, TokenPool.class, filter));
}

protected void persist(TokenPool tokenPool) {
entryManager.persist(tokenPool);
}

public void update(TokenPool tokenPool) {
entryManager.merge(tokenPool);
}

public TokenPool allocate(Integer nodeId) {
// Try to use existing expired entry
List<TokenPool> tokenPools = getTokenPoolsExpired();
Expand Down Expand Up @@ -209,7 +217,7 @@ public TokenPool allocate(Integer nodeId) {
String lockKey = UUID.randomUUID().toString();
tokenPool.setLockKey(lockKey);

// Do persist ooperation in try/catch for safety and do not throw error to upper
// Do persist operation in try/catch for safety and do not throw error to upper
// levels
try {
persist(lastTokenPool);
Expand Down Expand Up @@ -242,10 +250,6 @@ public void release(TokenPool tokenPool) {
update(tokenPool);
}

protected void persist(TokenPool tokenPool) {
entryManager.persist(tokenPool);
}

public void reset(TokenPool tokenPool, Integer nodeId) {
long currentTime = System.currentTimeMillis();
tokenPool.setNodeId(nodeId);
Expand All @@ -256,10 +260,6 @@ public void reset(TokenPool tokenPool, Integer nodeId) {
update(tokenPool);
}

public void update(TokenPool tokenPool) {
entryManager.merge(tokenPool);
}

private TokenPool setIndexes(TokenPool tokenPool) {
if (tokenPool == null) {
return tokenPool;
Expand Down
Loading