altered AuthorizationService to use User and Client references instead of full objects. Added tests.
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -3,14 +3,12 @@ package de.srsoftware.oidc.api;
|
|||||||
|
|
||||||
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.Client;
|
|
||||||
import de.srsoftware.oidc.api.data.User;
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface AuthorizationService {
|
public interface AuthorizationService {
|
||||||
AuthorizationService authorize(User user, Client client, Collection<String> scopes, Instant expiration);
|
AuthorizationService authorize(String userId, String clientId, Collection<String> scopes, Instant expiration);
|
||||||
Optional<Authorization> consumeAuthorization(String authCode);
|
Optional<Authorization> consumeAuthorization(String authCode);
|
||||||
AuthResult getAuthorization(User user, Client client, Collection<String> scopes);
|
AuthResult getAuthorization(String userId, String clientId, Collection<String> scopes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<String> 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -79,10 +79,10 @@ public class ClientController extends Controller {
|
|||||||
var days = authorized.getInt("days");
|
var days = authorized.getInt("days");
|
||||||
var list = new ArrayList<String>();
|
var list = new ArrayList<String>();
|
||||||
authorized.getJSONArray("scopes").forEach(scope -> list.add(scope.toString()));
|
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()) {
|
if (!authResult.unauthorizedScopes().isEmpty()) {
|
||||||
return sendContent(ex, Map.of("unauthorized_scopes", authResult.unauthorizedScopes(), "rp", client.name()));
|
return sendContent(ex, Map.of("unauthorized_scopes", authResult.unauthorizedScopes(), "rp", client.name()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -315,20 +315,20 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*** ClaimAuthorizationService methods ***/
|
/*** ClaimAuthorizationService methods ***/
|
||||||
private String authCode(User user, Client client, Authorization authorization) {
|
private String authCode(Authorization authorization) {
|
||||||
var code = uuid();
|
var code = uuid();
|
||||||
authCodes.put(code, authorization);
|
authCodes.put(code, authorization);
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthorizationService authorize(User user, Client client, Collection<String> scopes, Instant expiration) {
|
public AuthorizationService authorize(String userId, String clientId, Collection<String> scopes, Instant expiration) {
|
||||||
if (!json.has(AUTHORIZATIONS)) json.put(AUTHORIZATIONS, new JSONObject());
|
if (!json.has(AUTHORIZATIONS)) json.put(AUTHORIZATIONS, new JSONObject());
|
||||||
var authorizations = json.getJSONObject(AUTHORIZATIONS);
|
var authorizations = json.getJSONObject(AUTHORIZATIONS);
|
||||||
if (!authorizations.has(user.uuid())) authorizations.put(user.uuid(), new JSONObject());
|
if (!authorizations.has(userId)) authorizations.put(userId, new JSONObject());
|
||||||
var userAuthorizations = authorizations.getJSONObject(user.uuid());
|
var userAuthorizations = authorizations.getJSONObject(userId);
|
||||||
if (!userAuthorizations.has(client.id())) userAuthorizations.put(client.id(), new JSONObject());
|
if (!userAuthorizations.has(clientId)) userAuthorizations.put(clientId, new JSONObject());
|
||||||
var clientScopes = userAuthorizations.getJSONObject(client.id());
|
var clientScopes = userAuthorizations.getJSONObject(clientId);
|
||||||
for (var scope : scopes) clientScopes.put(scope, expiration.getEpochSecond());
|
for (var scope : scopes) clientScopes.put(scope, expiration.getEpochSecond());
|
||||||
save();
|
save();
|
||||||
return this;
|
return this;
|
||||||
@@ -341,12 +341,12 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthResult getAuthorization(User user, Client client, Collection<String> scopes) {
|
public AuthResult getAuthorization(String userId, String clientId, Collection<String> scopes) {
|
||||||
if (!json.has(AUTHORIZATIONS)) return unauthorized(scopes);
|
if (!json.has(AUTHORIZATIONS)) return unauthorized(scopes);
|
||||||
var authorizations = json.getJSONObject(AUTHORIZATIONS);
|
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);
|
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);
|
if (clientScopes == null) return unauthorized(scopes);
|
||||||
var now = Instant.now();
|
var now = Instant.now();
|
||||||
var authorizedScopes = new HashSet<String>();
|
var authorizedScopes = new HashSet<String>();
|
||||||
@@ -354,18 +354,19 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe
|
|||||||
Instant earliestExpiration = null;
|
Instant earliestExpiration = null;
|
||||||
for (var scope : scopes) {
|
for (var scope : scopes) {
|
||||||
if (clientScopes.has(scope)) {
|
if (clientScopes.has(scope)) {
|
||||||
var expiration = Instant.ofEpochSecond(clientScopes.getLong(scope));
|
var expiration = Instant.ofEpochSecond(clientScopes.getLong(scope)).truncatedTo(SECONDS);
|
||||||
if (expiration.isAfter(now)) {
|
if (expiration.isAfter(now)) {
|
||||||
authorizedScopes.add(scope);
|
authorizedScopes.add(scope);
|
||||||
if (earliestExpiration == null || expiration.isBefore(earliestExpiration)) earliestExpiration = expiration;
|
if (earliestExpiration == null || expiration.isBefore(earliestExpiration)) earliestExpiration = expiration;
|
||||||
} else {
|
continue;
|
||||||
unauthorizedScopes.add(scope);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
unauthorizedScopes.add(scope);
|
||||||
}
|
}
|
||||||
|
if (authorizedScopes.isEmpty()) return unauthorized(scopes);
|
||||||
|
|
||||||
var authorization = new Authorization(client.id(), user.uuid(), new AuthorizedScopes(authorizedScopes, earliestExpiration));
|
var authorization = new Authorization(clientId, userId, new AuthorizedScopes(authorizedScopes, earliestExpiration));
|
||||||
return new AuthResult(authorization.scopes(), unauthorizedScopes, authCode(user, client, authorization));
|
return new AuthResult(authorization.scopes(), unauthorizedScopes, authCode(authorization));
|
||||||
}
|
}
|
||||||
|
|
||||||
private AuthResult unauthorized(Collection<String> scopes) {
|
private AuthResult unauthorized(Collection<String> scopes) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,8 +4,6 @@ package de.srsoftware.oidc.datastore.sqlite;
|
|||||||
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.Client;
|
|
||||||
import de.srsoftware.oidc.api.data.User;
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -16,7 +14,7 @@ public class SqliteAuthService implements AuthorizationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthorizationService authorize(User user, Client client, Collection<String> scopes, Instant expiration) {
|
public AuthorizationService authorize(String userId, String clientId, Collection<String> scopes, Instant expiration) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +24,7 @@ public class SqliteAuthService implements AuthorizationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthResult getAuthorization(User user, Client client, Collection<String> scopes) {
|
public AuthResult getAuthorization(String userId, String clientId, Collection<String> scopes) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user