implemented SqliteAuthService
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -25,8 +25,10 @@ public abstract class AuthServiceTest {
|
|||||||
public void testAuthorize() {
|
public void testAuthorize() {
|
||||||
var authorizationService = authorizationService();
|
var authorizationService = authorizationService();
|
||||||
var userId1 = uuid();
|
var userId1 = uuid();
|
||||||
var expiration = Instant.now().plusSeconds(3600).truncatedTo(SECONDS);
|
var expiration = Instant.now();
|
||||||
authorizationService.authorize(userId1, CLIENT1, SCOPES1, expiration);
|
authorizationService.authorize(userId1, CLIENT1, SCOPES1, expiration);
|
||||||
|
expiration = Instant.now().plusSeconds(3600).truncatedTo(SECONDS); // test overwrite
|
||||||
|
authorizationService.authorize(userId1, CLIENT1, SCOPES1, expiration); // test overwrite
|
||||||
var authorization = authorizationService.getAuthorization(userId1, CLIENT1, Set.of(OPENID));
|
var authorization = authorizationService.getAuthorization(userId1, CLIENT1, Set.of(OPENID));
|
||||||
assertEquals(1, authorization.authorizedScopes().scopes().size());
|
assertEquals(1, authorization.authorizedScopes().scopes().size());
|
||||||
assertTrue(authorization.authorizedScopes().scopes().contains(OPENID));
|
assertTrue(authorization.authorizedScopes().scopes().contains(OPENID));
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ public class Application {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AuthorizationService setupAuthService(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider) {
|
private static AuthorizationService setupAuthService(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider) throws SQLException {
|
||||||
var authServiceLocation = new File(config.getOrDefault("auth_store",defaultFile));
|
var authServiceLocation = new File(config.getOrDefault("auth_store",defaultFile));
|
||||||
return switch (extension(authServiceLocation)){
|
return switch (extension(authServiceLocation)){
|
||||||
case "db", "sqlite", "sqlite3" -> new SqliteAuthService(connectionProvider.get(authServiceLocation));
|
case "db", "sqlite", "sqlite3" -> new SqliteAuthService(connectionProvider.get(authServiceLocation));
|
||||||
|
|||||||
@@ -1,30 +1,138 @@
|
|||||||
/* © SRSoftware 2024 */
|
/* © SRSoftware 2024 */
|
||||||
package de.srsoftware.oidc.datastore.sqlite;
|
package de.srsoftware.oidc.datastore.sqlite;
|
||||||
|
|
||||||
|
import static de.srsoftware.utils.Optionals.nullable;
|
||||||
|
import static de.srsoftware.utils.Strings.uuid;
|
||||||
|
|
||||||
import de.srsoftware.oidc.api.AuthorizationService;
|
import de.srsoftware.oidc.api.AuthorizationService;
|
||||||
import de.srsoftware.oidc.api.data.AuthResult;
|
import de.srsoftware.oidc.api.data.AuthResult;
|
||||||
import de.srsoftware.oidc.api.data.Authorization;
|
import de.srsoftware.oidc.api.data.Authorization;
|
||||||
|
import de.srsoftware.oidc.api.data.AuthorizedScopes;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Collection;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.Optional;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class SqliteAuthService implements AuthorizationService {
|
public class SqliteAuthService extends SqliteStore implements AuthorizationService {
|
||||||
public SqliteAuthService(Connection connection) {
|
private static final String STORE_VERSION = "auth_store_version";
|
||||||
|
private static final String CREATE_STORE_VERSION = "INSERT INTO metainfo (key,value) VALUES ('" + STORE_VERSION + "','0')";
|
||||||
|
private static final String SELECT_STORE_VERSION = "SELECT * FROM metainfo WHERE key = '" + STORE_VERSION + "'";
|
||||||
|
private static final String SET_STORE_VERSION = "UPDATE metainfo SET value = ? WHERE key = '" + STORE_VERSION + "'";
|
||||||
|
|
||||||
|
private static final String CREATE_AUTHSTORE_TABLE = "CREATE TABLE IF NOT EXISTS authorizations(userId VARCHAR(255), clientId VARCHAR(255), scope VARCHAR(255), expiration LONG, PRIMARY KEY(userId, clientId, scope));";
|
||||||
|
private static final String SAVE_AUTHORIZATION = "INSERT INTO authorizations(userId, clientId, scope, expiration) VALUES (?,?,?,?) ON CONFLICT DO UPDATE SET expiration = ?";
|
||||||
|
private static final String SELECT_AUTH = "SELECT * FROM authorizations WHERE userid = ? AND clientId = ? AND scope IN";
|
||||||
|
private Map<String, Authorization> authCodes = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
public SqliteAuthService(Connection connection) throws SQLException {
|
||||||
|
super(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createStoreTables() throws SQLException {
|
||||||
|
conn.prepareStatement(CREATE_AUTHSTORE_TABLE).execute();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
protected void initTables() throws SQLException {
|
||||||
|
var rs = conn.prepareStatement(SELECT_STORE_VERSION).executeQuery();
|
||||||
|
int availableVersion = 1;
|
||||||
|
int currentVersion;
|
||||||
|
if (rs.next()) {
|
||||||
|
currentVersion = rs.getInt("value");
|
||||||
|
rs.close();
|
||||||
|
} else {
|
||||||
|
rs.close();
|
||||||
|
conn.prepareStatement(CREATE_STORE_VERSION).execute();
|
||||||
|
currentVersion = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.setAutoCommit(false);
|
||||||
|
var stmt = conn.prepareStatement(SET_STORE_VERSION);
|
||||||
|
while (currentVersion < availableVersion) {
|
||||||
|
try {
|
||||||
|
switch (currentVersion) {
|
||||||
|
case 0:
|
||||||
|
createStoreTables();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
stmt.setInt(1, ++currentVersion);
|
||||||
|
stmt.execute();
|
||||||
|
conn.commit();
|
||||||
|
} catch (Exception e) {
|
||||||
|
conn.rollback();
|
||||||
|
LOG.log(System.Logger.Level.ERROR, "Failed to update at {} = {}", STORE_VERSION, currentVersion);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn.setAutoCommit(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String authCode(Authorization authorization) {
|
||||||
|
var code = uuid();
|
||||||
|
authCodes.put(code, authorization);
|
||||||
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthorizationService authorize(String userId, String clientId, Collection<String> scopes, Instant expiration) {
|
public AuthorizationService authorize(String userId, String clientId, Collection<String> scopes, Instant expiration) {
|
||||||
return null;
|
try {
|
||||||
|
conn.setAutoCommit(false);
|
||||||
|
var stmt = conn.prepareStatement(SAVE_AUTHORIZATION);
|
||||||
|
stmt.setString(1, userId);
|
||||||
|
stmt.setString(2, clientId);
|
||||||
|
stmt.setLong(4, expiration.getEpochSecond());
|
||||||
|
stmt.setLong(5, expiration.getEpochSecond());
|
||||||
|
for (var scope : scopes) {
|
||||||
|
stmt.setString(3, scope);
|
||||||
|
stmt.execute();
|
||||||
|
}
|
||||||
|
conn.commit();
|
||||||
|
conn.setAutoCommit(true);
|
||||||
|
return this;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Authorization> consumeAuthorization(String authCode) {
|
public Optional<Authorization> consumeAuthorization(String authCode) {
|
||||||
return Optional.empty();
|
return nullable(authCodes.remove(authCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthResult getAuthorization(String userId, String clientId, Collection<String> scopes) {
|
public AuthResult getAuthorization(String userId, String clientId, Collection<String> scopes) {
|
||||||
return null;
|
try {
|
||||||
|
var scopeList = "(" + scopes.stream().map(s -> "?").collect(Collectors.joining(", ")) + ")";
|
||||||
|
var sql = SELECT_AUTH + scopeList;
|
||||||
|
var stmt = conn.prepareStatement(sql);
|
||||||
|
stmt.setString(1, userId);
|
||||||
|
stmt.setString(2, clientId);
|
||||||
|
int i = 3;
|
||||||
|
for (var scope : scopes) stmt.setString(i++, scope);
|
||||||
|
var rs = stmt.executeQuery();
|
||||||
|
var unauthorized = new HashSet<String>(scopes);
|
||||||
|
var authorized = new HashSet<String>();
|
||||||
|
var now = Instant.now();
|
||||||
|
Instant earliestExp = null;
|
||||||
|
while (rs.next()) {
|
||||||
|
long expiration = rs.getLong("expiration");
|
||||||
|
String scope = rs.getString("scope");
|
||||||
|
Instant ex = Instant.ofEpochSecond(expiration).truncatedTo(ChronoUnit.SECONDS);
|
||||||
|
if (ex.isAfter(now)) {
|
||||||
|
unauthorized.remove(scope);
|
||||||
|
authorized.add(scope);
|
||||||
|
if (earliestExp == null || ex.isBefore(earliestExp)) earliestExp = ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rs.close();
|
||||||
|
if (authorized.isEmpty()) return new AuthResult(null, unauthorized, null);
|
||||||
|
var authorizedScopes = new AuthorizedScopes(authorized, earliestExp);
|
||||||
|
var authorization = new Authorization(clientId, userId, authorizedScopes);
|
||||||
|
return new AuthResult(authorizedScopes, unauthorized, authCode(authorization));
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/* © SRSoftware 2024 */
|
||||||
|
package de.srsoftware.oidc.datastore.sqlite;
|
||||||
|
|
||||||
|
import static de.srsoftware.utils.Strings.uuid;
|
||||||
|
|
||||||
|
import de.srsoftware.oidc.api.AuthServiceTest;
|
||||||
|
import de.srsoftware.oidc.api.AuthorizationService;
|
||||||
|
import java.io.File;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
|
||||||
|
public class SqliteAuthServiceTest extends AuthServiceTest {
|
||||||
|
private AuthorizationService authorizationService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AuthorizationService authorizationService() {
|
||||||
|
return authorizationService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setup() throws SQLException {
|
||||||
|
var dbFile = new File("/tmp/" + uuid() + ".sqlite");
|
||||||
|
var conn = new ConnectionProvider().get(dbFile);
|
||||||
|
authorizationService = new SqliteAuthService(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user