Skip to content

Commit

Permalink
Rewrite persistence to H2
Browse files Browse the repository at this point in the history
This does not include any migration of existing data. By switching to this version, you lose all previously persisted API tokens.

Consolidation of the data model from separate dbs into 3 tables in the same db.

Slightly simplify the group sync by just updating the db once at the end.
  • Loading branch information
tumbl3w33d committed Aug 22, 2024
1 parent ab85128 commit fa55827
Show file tree
Hide file tree
Showing 28 changed files with 909 additions and 1,054 deletions.
4 changes: 2 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"initializeCommand": "docker pull mcr.microsoft.com/devcontainers/base:debian",
"features": {
"ghcr.io/devcontainers/features/java:1": {
"version": "21",
"version": "22",
"jdkDistro": "tem",
"installMaven": "true",
"installGradle": "false"
Expand All @@ -17,7 +17,7 @@
"java.configuration.updateBuildConfiguration": "automatic",
"java.configuration.runtimes": [
{
"name": "JavaSE-21",
"name": "JavaSE-22",
"path": "/usr/local/sdkman/candidates/java/current"
}
],
Expand Down
7 changes: 1 addition & 6 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,7 @@
</dependency>
<dependency>
<groupId>org.sonatype.nexus</groupId>
<artifactId>nexus-orient</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.orientechnologies</groupId>
<artifactId>orientdb-core</artifactId>
<artifactId>nexus-datastore-mybatis</artifactId>
<scope>provided</scope>
</dependency>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,105 +1,92 @@
package com.github.tumbl3w33d;

import static com.github.tumbl3w33d.OAuth2ProxyRealm.CLASS_USER_LOGIN;
import static com.github.tumbl3w33d.OAuth2ProxyRealm.FIELD_LAST_LOGIN;
import static com.github.tumbl3w33d.OAuth2ProxyRealm.FIELD_USER_ID;
import static com.github.tumbl3w33d.OAuth2ProxyRealm.formatDateString;
import static com.github.tumbl3w33d.OAuth2ProxyRealm.generateSecureRandomString;
import static org.sonatype.nexus.logging.task.TaskLogType.NEXUS_LOG_ONLY;

import java.sql.Timestamp;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.List;
import java.util.Set;

import javax.inject.Inject;
import javax.inject.Named;

import org.sonatype.nexus.logging.task.TaskLogging;
import org.sonatype.nexus.orient.DatabaseInstance;
import org.sonatype.nexus.scheduling.Cancelable;
import org.sonatype.nexus.scheduling.TaskSupport;
import org.sonatype.nexus.security.user.UserManager;
import org.sonatype.nexus.security.user.UserNotFoundException;

import com.github.tumbl3w33d.h2.OAuth2ProxyLoginRecordStore;
import com.github.tumbl3w33d.users.OAuth2ProxyUserManager;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.sql.query.OSQLSynchQuery;
import com.github.tumbl3w33d.users.db.OAuth2ProxyLoginRecord;

@Named
@TaskLogging(NEXUS_LOG_ONLY)
public class OAuth2ProxyApiTokenInvalidateTask
extends TaskSupport
implements Cancelable {

private final DatabaseInstance databaseInstance;

private final OAuth2ProxyUserManager userManager;

@Inject
public OAuth2ProxyApiTokenInvalidateTask(@Named(OAuth2ProxyDatabase.NAME) DatabaseInstance databaseInstance,
final List<UserManager> userManagers, final OAuth2ProxyUserManager userManager) {

this.databaseInstance = databaseInstance;
this.userManager = userManager;

OAuth2ProxyRealm.ensureUserLoginTimestampSchema(databaseInstance, log);
}

private void resetApiToken(String userId) {
try {
userManager.changePassword(userId, generateSecureRandomString(32));
log.debug("API token reset for user {} succeeded", userId);
} catch (UserNotFoundException e) {
log.error("Unable to reset API token of user {} - {}", userId, e);
}
}

@Override
protected Void execute() throws Exception {
try (ODatabaseDocumentTx db = databaseInstance.acquire()) {
db.begin();

List<ODocument> userLogins = db.query(new OSQLSynchQuery<ODocument>(
"select from " + CLASS_USER_LOGIN));

if (userLogins.isEmpty()) {
log.debug("Nothing to do");
} else {
for (ODocument userLogin : userLogins) {
String userId = userLogin.field(FIELD_USER_ID);
Date lastLoginDate = userLogin.field(FIELD_LAST_LOGIN);

Instant lastLoginInstant = lastLoginDate.toInstant();
Instant nowInstant = Instant.now();

log.debug("Last known login for {} was {}", userId,
formatDateString(lastLoginDate));

long timePassed = ChronoUnit.DAYS.between(lastLoginInstant, nowInstant);

int configuredDuration = getConfiguration()
.getInteger(OAuth2ProxyApiTokenInvalidateTaskDescriptor.CONFIG_EXPIRY, 1);

log.debug("Time passed since login: {} - configured maximum: {}", timePassed,
configuredDuration);

if (timePassed >= configuredDuration) {
resetApiToken(userId);
log.info("Reset api token of user {} because they did not show up for a while", userId);
}
}

}
} catch (Exception e) {
log.error("Failed to retrieve login timestamps - {}", e);
}
return null;
}

@Override
public String getMessage() {
return "Invalidate OAuth2 Proxy API tokens of users who did not show up for a while";
}
public class OAuth2ProxyApiTokenInvalidateTask extends TaskSupport implements Cancelable {

private final OAuth2ProxyLoginRecordStore loginRecordStore;

private final OAuth2ProxyUserManager userManager;

@Inject
public OAuth2ProxyApiTokenInvalidateTask(@Named OAuth2ProxyLoginRecordStore loginRecordStore,
final List<UserManager> userManagers, final OAuth2ProxyUserManager userManager) {

this.loginRecordStore = loginRecordStore;
this.userManager = userManager;
}

private void resetApiToken(String userId) {
try {
userManager.changePassword(userId, OAuth2ProxyRealm.generateSecureRandomString(32));
log.debug("API token reset for user {} succeeded", userId);
} catch (UserNotFoundException e) {
log.error("Unable to reset API token of user {} - {}", userId, e);
}
}

@Override
protected Void execute() throws Exception {

Set<OAuth2ProxyLoginRecord> loginRecords = loginRecordStore.getAllLoginRecords();

if (loginRecords.isEmpty()) {
log.debug("No login records found, nothing to do");
return null;
}

for (OAuth2ProxyLoginRecord loginRecord : loginRecords) {
String userId = loginRecord.getId();
Timestamp lastLoginDate = loginRecord.getLastLogin();

Instant lastLoginInstant = lastLoginDate.toInstant();
Instant nowInstant = Instant.now();

log.debug("Last known login for {} was {}", userId,
OAuth2ProxyRealm.formatDateString(lastLoginDate));

long timePassed = ChronoUnit.DAYS.between(lastLoginInstant, nowInstant);

int configuredDuration = getConfiguration()
.getInteger(OAuth2ProxyApiTokenInvalidateTaskDescriptor.CONFIG_EXPIRY, 1);

log.debug("Time passed since login: {} - configured maximum: {}", timePassed,
configuredDuration);

if (timePassed >= configuredDuration) {
resetApiToken(userId);
log.info("Reset api token of user {} because they did not show up for a while",
userId);
}
}

return null;
}

@Override
public String getMessage() {
return "Invalidate OAuth2 Proxy API tokens of users who did not show up for a while";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,28 @@
@Named
@Singleton
public class OAuth2ProxyApiTokenInvalidateTaskDescriptor
extends TaskDescriptorSupport {
extends TaskDescriptorSupport {

public static final String TYPE_ID = "oauth2-proxy-api-token.cleanup";

public static final String CONFIG_EXPIRY = TYPE_ID + "-expiry";

private static final NumberTextFormField field = new NumberTextFormField(CONFIG_EXPIRY,
"Expiration in days",
"After this duration the API token will be overwritten and the user must renew it interactively.",
FormField.MANDATORY).withMinimumValue(1).withInitialValue(30);

private static final NumberTextFormField field = new
NumberTextFormField(CONFIG_EXPIRY,
"Expiration in days",
"After this duration the API token will be overwritten and the user must renew it interactively."
,
FormField.MANDATORY).withMinimumValue(1).withInitialValue(30);

@Inject
public OAuth2ProxyApiTokenInvalidateTaskDescriptor() {
super(TYPE_ID, OAuth2ProxyApiTokenInvalidateTask.class, "OAuth2 Proxy API token invalidator",
TaskDescriptorSupport.VISIBLE, TaskDescriptorSupport.EXPOSED,
TaskDescriptorSupport.REQUEST_RECOVERY, new FormField[] { field });
super(TYPE_ID, OAuth2ProxyApiTokenInvalidateTask.class, "OAuth2 Proxy API token invalidator",
TaskDescriptorSupport.VISIBLE, TaskDescriptorSupport.EXPOSED,
TaskDescriptorSupport.REQUEST_RECOVERY, new FormField[] { field });
}

@Override
public boolean allowConcurrentRun() {
return false;
}

}
39 changes: 0 additions & 39 deletions src/main/java/com/github/tumbl3w33d/OAuth2ProxyDatabase.java

This file was deleted.

Loading

0 comments on commit fa55827

Please sign in to comment.