From 0b96aeb63daccea192cedff957544ddc8cd99e1a Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Sun, 15 Sep 2024 10:38:55 +0200 Subject: [PATCH] altered AuthorizationService to use User and Client references instead of full objects. Added tests. Signed-off-by: Stephan Richter --- .../oidc/api/AuthorizationService.java | 6 +- .../srsoftware/oidc/api/AuthServiceTest.java | 73 +++++++++++++++++++ .../oidc/backend/ClientController.java | 4 +- .../oidc/datastore/file/FileStore.java | 29 ++++---- .../file/FileStoreAuthServiceTest.java | 27 +++++++ .../datastore/sqlite/SqliteAuthService.java | 6 +- 6 files changed, 121 insertions(+), 24 deletions(-) create mode 100644 de.srsoftware.oidc.api/src/test/java/de/srsoftware/oidc/api/AuthServiceTest.java create mode 100644 de.srsoftware.oidc.datastore.file/src/test/java/de/srsoftware/oidc/datastore/file/FileStoreAuthServiceTest.java diff --git a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/AuthorizationService.java b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/AuthorizationService.java index 788a360..126cf51 100644 --- a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/AuthorizationService.java +++ b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/AuthorizationService.java @@ -3,14 +3,12 @@ package de.srsoftware.oidc.api; import de.srsoftware.oidc.api.data.AuthResult; import de.srsoftware.oidc.api.data.Authorization; -import de.srsoftware.oidc.api.data.Client; -import de.srsoftware.oidc.api.data.User; import java.time.Instant; import java.util.Collection; import java.util.Optional; public interface AuthorizationService { - AuthorizationService authorize(User user, Client client, Collection scopes, Instant expiration); + AuthorizationService authorize(String userId, String clientId, Collection scopes, Instant expiration); Optional consumeAuthorization(String authCode); - AuthResult getAuthorization(User user, Client client, Collection scopes); + AuthResult getAuthorization(String userId, String clientId, Collection scopes); } diff --git a/de.srsoftware.oidc.api/src/test/java/de/srsoftware/oidc/api/AuthServiceTest.java b/de.srsoftware.oidc.api/src/test/java/de/srsoftware/oidc/api/AuthServiceTest.java new file mode 100644 index 0000000..720fa3c --- /dev/null +++ b/de.srsoftware.oidc.api/src/test/java/de/srsoftware/oidc/api/AuthServiceTest.java @@ -0,0 +1,73 @@ +/* © SRSoftware 2024 */ +package de.srsoftware.oidc.api; + +import static de.srsoftware.oidc.api.Constants.OPENID; +import static de.srsoftware.utils.Strings.uuid; +import static java.time.temporal.ChronoUnit.SECONDS; +import static org.junit.jupiter.api.Assertions.*; + +import java.time.Instant; +import java.util.Set; +import org.junit.jupiter.api.Test; + +public abstract class AuthServiceTest { + private static final String CLIENT1 = "client1"; + private static final Set SCOPES1 = Set.of(OPENID, Constants.EMAIL, "ranzpappe"); + private static final String INVALID = "invalid"; + private static final String PASS1 = "grunzwanzling"; + private static final String USERNAME = "arthurdent"; + private static final String REALNAME = "Arthur Dent"; + private static final String EMAIL = "arthur@herzaus.gold"; + + protected abstract AuthorizationService authorizationService(); + + @Test + public void testAuthorize() { + var authorizationService = authorizationService(); + var userId1 = uuid(); + var expiration = Instant.now().plusSeconds(3600).truncatedTo(SECONDS); + authorizationService.authorize(userId1, CLIENT1, SCOPES1, expiration); + var authorization = authorizationService.getAuthorization(userId1, CLIENT1, Set.of(OPENID)); + assertEquals(1, authorization.authorizedScopes().scopes().size()); + assertTrue(authorization.authorizedScopes().scopes().contains(OPENID)); + assertEquals(expiration, authorization.authorizedScopes().expiration()); + + authorization = authorizationService.getAuthorization(userId1, CLIENT1, Set.of(INVALID)); + assertNull(authorization.authorizedScopes()); + assertNull(authorization.authCode()); + assertTrue(authorization.unauthorizedScopes().contains(INVALID)); + + authorization = authorizationService.getAuthorization(userId1, CLIENT1, Set.of(INVALID, OPENID)); + assertEquals(1, authorization.authorizedScopes().scopes().size()); + assertTrue(authorization.authorizedScopes().scopes().contains(OPENID)); + assertEquals(expiration, authorization.authorizedScopes().expiration()); + + assertEquals(1, authorization.unauthorizedScopes().size()); + assertTrue(authorization.unauthorizedScopes().contains(INVALID)); + } + + @Test + public void testConsume() { + var authorizationService = authorizationService(); + + var userId1 = uuid(); + var expiration = Instant.now().plusSeconds(3600).truncatedTo(SECONDS); + authorizationService.authorize(userId1, CLIENT1, SCOPES1, expiration); + var authResult = authorizationService.getAuthorization(userId1, CLIENT1, Set.of(OPENID)); + var authCode = authResult.authCode(); + assertNotNull(authCode); + + var optAuth = authorizationService.consumeAuthorization(authCode); + assertTrue(optAuth.isPresent()); + var authorization = optAuth.get(); + assertEquals(CLIENT1, authorization.clientId()); + assertEquals(userId1, authorization.userId()); + var scopes = authorization.scopes(); + assertEquals(expiration, scopes.expiration()); + assertEquals(1, scopes.scopes().size()); + assertTrue(scopes.scopes().contains(OPENID)); + + optAuth = authorizationService.consumeAuthorization(authCode); + assertTrue(optAuth.isEmpty()); + } +} diff --git a/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/ClientController.java b/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/ClientController.java index ed8046b..0a8f099 100644 --- a/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/ClientController.java +++ b/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/ClientController.java @@ -79,10 +79,10 @@ public class ClientController extends Controller { var days = authorized.getInt("days"); var list = new ArrayList(); authorized.getJSONArray("scopes").forEach(scope -> list.add(scope.toString())); - authorizations.authorize(user, client, list, Instant.now().plus(days, ChronoUnit.DAYS)); + authorizations.authorize(user.uuid(), client.id(), list, Instant.now().plus(days, ChronoUnit.DAYS)); } - var authResult = authorizations.getAuthorization(user, client, scopes); + var authResult = authorizations.getAuthorization(user.uuid(), client.id(), scopes); if (!authResult.unauthorizedScopes().isEmpty()) { return sendContent(ex, Map.of("unauthorized_scopes", authResult.unauthorizedScopes(), "rp", client.name())); } diff --git a/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java b/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java index f62bf59..e6f8ea5 100644 --- a/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java +++ b/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java @@ -315,20 +315,20 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe } /*** ClaimAuthorizationService methods ***/ - private String authCode(User user, Client client, Authorization authorization) { + private String authCode(Authorization authorization) { var code = uuid(); authCodes.put(code, authorization); return code; } @Override - public AuthorizationService authorize(User user, Client client, Collection scopes, Instant expiration) { + public AuthorizationService authorize(String userId, String clientId, Collection scopes, Instant expiration) { if (!json.has(AUTHORIZATIONS)) json.put(AUTHORIZATIONS, new JSONObject()); var authorizations = json.getJSONObject(AUTHORIZATIONS); - if (!authorizations.has(user.uuid())) authorizations.put(user.uuid(), new JSONObject()); - var userAuthorizations = authorizations.getJSONObject(user.uuid()); - if (!userAuthorizations.has(client.id())) userAuthorizations.put(client.id(), new JSONObject()); - var clientScopes = userAuthorizations.getJSONObject(client.id()); + if (!authorizations.has(userId)) authorizations.put(userId, new JSONObject()); + var userAuthorizations = authorizations.getJSONObject(userId); + if (!userAuthorizations.has(clientId)) userAuthorizations.put(clientId, new JSONObject()); + var clientScopes = userAuthorizations.getJSONObject(clientId); for (var scope : scopes) clientScopes.put(scope, expiration.getEpochSecond()); save(); return this; @@ -341,12 +341,12 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe } @Override - public AuthResult getAuthorization(User user, Client client, Collection scopes) { + public AuthResult getAuthorization(String userId, String clientId, Collection scopes) { if (!json.has(AUTHORIZATIONS)) return unauthorized(scopes); var authorizations = json.getJSONObject(AUTHORIZATIONS); - var userAuthorizations = authorizations.has(user.uuid()) ? authorizations.getJSONObject(user.uuid()) : null; + var userAuthorizations = authorizations.has(userId) ? authorizations.getJSONObject(userId) : null; if (userAuthorizations == null) return unauthorized(scopes); - var clientScopes = userAuthorizations.has(client.id()) ? userAuthorizations.getJSONObject(client.id()) : null; + var clientScopes = userAuthorizations.has(clientId) ? userAuthorizations.getJSONObject(clientId) : null; if (clientScopes == null) return unauthorized(scopes); var now = Instant.now(); var authorizedScopes = new HashSet(); @@ -354,18 +354,19 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe Instant earliestExpiration = null; for (var scope : scopes) { if (clientScopes.has(scope)) { - var expiration = Instant.ofEpochSecond(clientScopes.getLong(scope)); + var expiration = Instant.ofEpochSecond(clientScopes.getLong(scope)).truncatedTo(SECONDS); if (expiration.isAfter(now)) { authorizedScopes.add(scope); if (earliestExpiration == null || expiration.isBefore(earliestExpiration)) earliestExpiration = expiration; - } else { - unauthorizedScopes.add(scope); + continue; } } + unauthorizedScopes.add(scope); } + if (authorizedScopes.isEmpty()) return unauthorized(scopes); - var authorization = new Authorization(client.id(), user.uuid(), new AuthorizedScopes(authorizedScopes, earliestExpiration)); - return new AuthResult(authorization.scopes(), unauthorizedScopes, authCode(user, client, authorization)); + var authorization = new Authorization(clientId, userId, new AuthorizedScopes(authorizedScopes, earliestExpiration)); + return new AuthResult(authorization.scopes(), unauthorizedScopes, authCode(authorization)); } private AuthResult unauthorized(Collection scopes) { diff --git a/de.srsoftware.oidc.datastore.file/src/test/java/de/srsoftware/oidc/datastore/file/FileStoreAuthServiceTest.java b/de.srsoftware.oidc.datastore.file/src/test/java/de/srsoftware/oidc/datastore/file/FileStoreAuthServiceTest.java new file mode 100644 index 0000000..c283e64 --- /dev/null +++ b/de.srsoftware.oidc.datastore.file/src/test/java/de/srsoftware/oidc/datastore/file/FileStoreAuthServiceTest.java @@ -0,0 +1,27 @@ +/* © SRSoftware 2024 */ +package de.srsoftware.oidc.datastore.file; + +import static org.junit.jupiter.api.Assertions.*; + +import de.srsoftware.oidc.api.AuthServiceTest; +import de.srsoftware.oidc.api.AuthorizationService; +import java.io.File; +import java.io.IOException; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; + +public class FileStoreAuthServiceTest extends AuthServiceTest { + private File storage = new File("/tmp/" + UUID.randomUUID()); + private AuthorizationService authorizationService; + + @Override + protected AuthorizationService authorizationService() { + return authorizationService; + } + + @BeforeEach + public void setup() throws IOException { + if (storage.exists()) storage.delete(); + authorizationService = new FileStore(storage, null); + } +} diff --git a/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteAuthService.java b/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteAuthService.java index a737d36..17d233d 100644 --- a/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteAuthService.java +++ b/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteAuthService.java @@ -4,8 +4,6 @@ package de.srsoftware.oidc.datastore.sqlite; import de.srsoftware.oidc.api.AuthorizationService; import de.srsoftware.oidc.api.data.AuthResult; import de.srsoftware.oidc.api.data.Authorization; -import de.srsoftware.oidc.api.data.Client; -import de.srsoftware.oidc.api.data.User; import java.sql.Connection; import java.time.Instant; import java.util.Collection; @@ -16,7 +14,7 @@ public class SqliteAuthService implements AuthorizationService { } @Override - public AuthorizationService authorize(User user, Client client, Collection scopes, Instant expiration) { + public AuthorizationService authorize(String userId, String clientId, Collection scopes, Instant expiration) { return null; } @@ -26,7 +24,7 @@ public class SqliteAuthService implements AuthorizationService { } @Override - public AuthResult getAuthorization(User user, Client client, Collection scopes) { + public AuthResult getAuthorization(String userId, String clientId, Collection scopes) { return null; } }