implemented SqliteClientService
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -8,6 +8,6 @@ import java.util.Optional;
|
|||||||
public interface ClientService {
|
public interface ClientService {
|
||||||
Optional<Client> getClient(String clientId);
|
Optional<Client> getClient(String clientId);
|
||||||
List<Client> listClients();
|
List<Client> listClients();
|
||||||
ClientService remove(Client client);
|
ClientService remove(String clientId);
|
||||||
ClientService save(Client client);
|
ClientService save(Client client);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,20 @@
|
|||||||
/* © SRSoftware 2024 */
|
/* © SRSoftware 2024 */
|
||||||
package de.srsoftware.oidc.datastore.file;
|
package de.srsoftware.oidc.api;
|
||||||
|
|
||||||
import static de.srsoftware.utils.Strings.uuid;
|
import static de.srsoftware.utils.Strings.uuid;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import de.srsoftware.oidc.api.ClientService;
|
|
||||||
import de.srsoftware.oidc.api.data.Client;
|
import de.srsoftware.oidc.api.data.Client;
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
public class ClientServiceTest {
|
public abstract class ClientServiceTest {
|
||||||
private static ClientService clientService;
|
private static final String NAME = "client-1";
|
||||||
private static final String NAME = "client-1";
|
private static final String URI = "uri-1";
|
||||||
private static final String URI = "uri-1";
|
private static final String URI2 = "uri-2";
|
||||||
private static final String URI2 = "uri-2";
|
|
||||||
@BeforeEach
|
|
||||||
public void setup() throws IOException {
|
|
||||||
var storage = new File("/tmp/" + UUID.randomUUID());
|
|
||||||
if (storage.exists()) storage.delete();
|
|
||||||
clientService = new FileStore(storage, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ClientService clientService() {
|
protected abstract ClientService clientService();
|
||||||
return clientService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSaveAndList() {
|
public void testSaveAndList() {
|
||||||
@@ -40,7 +26,7 @@ public class ClientServiceTest {
|
|||||||
var list = cs.save(client).listClients();
|
var list = cs.save(client).listClients();
|
||||||
assertEquals(1, list.size());
|
assertEquals(1, list.size());
|
||||||
assertTrue(list.contains(client));
|
assertTrue(list.contains(client));
|
||||||
cs.remove(client);
|
cs.remove(clientId);
|
||||||
assertTrue(cs.listClients().isEmpty());
|
assertTrue(cs.listClients().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ public class Application {
|
|||||||
server.start();
|
server.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ClientService setupClientService(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider) {
|
private static ClientService setupClientService(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider) throws SQLException {
|
||||||
var clientStore = new File(config.getOrDefault("client_store", defaultFile));
|
var clientStore = new File(config.getOrDefault("client_store", defaultFile));
|
||||||
return switch (extension(clientStore)) {
|
return switch (extension(clientStore)) {
|
||||||
case "db", "sqlite", "sqlite3" -> new SqliteClientService(connectionProvider.get(clientStore));
|
case "db", "sqlite", "sqlite3" -> new SqliteClientService(connectionProvider.get(clientStore));
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ public class ClientController extends Controller {
|
|||||||
if (!optUser.get().hasPermission(MANAGE_CLIENTS)) return badRequest(ex, "NOT ALLOWED");
|
if (!optUser.get().hasPermission(MANAGE_CLIENTS)) return badRequest(ex, "NOT ALLOWED");
|
||||||
var json = json(ex);
|
var json = json(ex);
|
||||||
var id = json.getString(CLIENT_ID);
|
var id = json.getString(CLIENT_ID);
|
||||||
clients.getClient(id).ifPresent(clients::remove);
|
clients.remove(id);
|
||||||
return sendEmptyResponse(HTTP_OK, ex);
|
return sendEmptyResponse(HTTP_OK, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -287,10 +287,10 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileStore remove(Client client) {
|
public FileStore remove(String clientId) {
|
||||||
if (!json.has(CLIENTS)) return this;
|
if (!json.has(CLIENTS)) return this;
|
||||||
var clients = json.getJSONObject(CLIENTS);
|
var clients = json.getJSONObject(CLIENTS);
|
||||||
if (clients.has(client.id())) clients.remove(client.id());
|
if (clients.has(clientId)) clients.remove(clientId);
|
||||||
return save();
|
return save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/* © SRSoftware 2024 */
|
||||||
|
package de.srsoftware.oidc.datastore.file;
|
||||||
|
|
||||||
|
|
||||||
|
import de.srsoftware.oidc.api.ClientService;
|
||||||
|
import de.srsoftware.oidc.api.ClientServiceTest;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
|
||||||
|
public class FileStoreClientServiceTest extends ClientServiceTest {
|
||||||
|
private static ClientService clientService;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setup() throws IOException {
|
||||||
|
var storage = new File("/tmp/" + UUID.randomUUID());
|
||||||
|
if (storage.exists()) storage.delete();
|
||||||
|
clientService = new FileStore(storage, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ClientService clientService() {
|
||||||
|
return clientService;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,33 +1,173 @@
|
|||||||
/* © SRSoftware 2024 */
|
/* © SRSoftware 2024 */
|
||||||
package de.srsoftware.oidc.datastore.sqlite;
|
package de.srsoftware.oidc.datastore.sqlite;
|
||||||
|
|
||||||
|
import static de.srsoftware.oidc.api.Constants.NAME;
|
||||||
|
import static de.srsoftware.oidc.api.Constants.SECRET;
|
||||||
|
|
||||||
import de.srsoftware.oidc.api.ClientService;
|
import de.srsoftware.oidc.api.ClientService;
|
||||||
import de.srsoftware.oidc.api.data.Client;
|
import de.srsoftware.oidc.api.data.Client;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.util.List;
|
import java.sql.SQLException;
|
||||||
import java.util.Optional;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class SqliteClientService implements ClientService {
|
public class SqliteClientService extends SqliteStore implements ClientService {
|
||||||
public SqliteClientService(Connection connection) {
|
private static final String STORE_VERSION = "client_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_CLIENT_TABLE = "CREATE TABLE IF NOT EXISTS clients(id VARCHAR(255) NOT NULL PRIMARY KEY, name VARCHAR(255), secret VARCHAR(255));";
|
||||||
|
private static final String CREATE_REDIRECT_TABLE = "CREATE TABLE IF NOT EXISTS client_redirects(clientId VARCHAR(255), uri VARCHAR(255), PRIMARY KEY(clientId, uri));";
|
||||||
|
private static final String SAVE_CLIENT = "INSERT INTO clients (id, name, secret) VALUES (?,?,?) ON CONFLICT DO UPDATE SET name = ?, secret = ?;";
|
||||||
|
private static final String SAVE_REDIRECT = "INSERT OR IGNORE INTO client_redirects(clientId, uri) VALUES (?, ?)";
|
||||||
|
private static final String DROP_OTHER_REDIRECTS = "DELETE FROM client_redirects WHERE clientId = ? AND uri NOT IN";
|
||||||
|
private static final String SELECT_CLIENT = "SELECT * FROM clients WHERE id = ?";
|
||||||
|
private static final String SELECT_CLIENT_REDIRECTS = "SELECT uri FROM client_redirects WHERE clientId = ?";
|
||||||
|
private static final String LIST_CLIENT_REDIRECTS = "SELECT * FROM client_redirects";
|
||||||
|
private static final String LIST_CLIENTS = "SELECT * FROM clients";
|
||||||
|
private static final String DELETE_CLIENT = "DELETE FROM clients WHERE id = ?";
|
||||||
|
private static final String DELETE_CLIENT_REDIRECTS = "DELETE FROM client_redirects WHERE clientId = ?";
|
||||||
|
|
||||||
|
public SqliteClientService(Connection connection) throws SQLException {
|
||||||
|
super(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createStoreTables() throws SQLException {
|
||||||
|
conn.prepareStatement(CREATE_CLIENT_TABLE).execute();
|
||||||
|
conn.prepareStatement(CREATE_REDIRECT_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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Client> getClient(String clientId) {
|
public Optional<Client> getClient(String clientId) {
|
||||||
return Optional.empty();
|
Optional<Client> result = Optional.empty();
|
||||||
|
try {
|
||||||
|
var stmt = conn.prepareStatement(SELECT_CLIENT_REDIRECTS);
|
||||||
|
stmt.setString(1, clientId);
|
||||||
|
var rs = stmt.executeQuery();
|
||||||
|
var uris = new HashSet<String>();
|
||||||
|
while (rs.next()) uris.add(rs.getString("uri"));
|
||||||
|
rs.close();
|
||||||
|
stmt = conn.prepareStatement(SELECT_CLIENT);
|
||||||
|
stmt.setString(1, clientId);
|
||||||
|
rs = stmt.executeQuery();
|
||||||
|
if (rs.next()) {
|
||||||
|
var name = rs.getString(NAME);
|
||||||
|
var secret = rs.getString(SECRET);
|
||||||
|
result = Optional.of(new Client(clientId, name, secret, uris));
|
||||||
|
}
|
||||||
|
rs.close();
|
||||||
|
return result;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Client> listClients() {
|
public List<Client> listClients() {
|
||||||
return List.of();
|
try {
|
||||||
|
var stmt = conn.prepareStatement(LIST_CLIENT_REDIRECTS);
|
||||||
|
var rs = stmt.executeQuery();
|
||||||
|
var redirects = new HashMap<String, Set<String>>();
|
||||||
|
while (rs.next()) {
|
||||||
|
var clientId = rs.getString("clientId");
|
||||||
|
var uri = rs.getString("uri");
|
||||||
|
var set = redirects.computeIfAbsent(clientId, k -> new HashSet<>());
|
||||||
|
set.add(uri);
|
||||||
|
}
|
||||||
|
rs.close();
|
||||||
|
stmt = conn.prepareStatement(LIST_CLIENTS);
|
||||||
|
rs = stmt.executeQuery();
|
||||||
|
var result = new ArrayList<Client>();
|
||||||
|
while (rs.next()) {
|
||||||
|
var id = rs.getString("id");
|
||||||
|
var name = rs.getString(NAME);
|
||||||
|
var secret = rs.getString(SECRET);
|
||||||
|
result.add(new Client(id, name, secret, redirects.get(id)));
|
||||||
|
}
|
||||||
|
rs.close();
|
||||||
|
return result;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClientService remove(Client client) {
|
public ClientService remove(String clientId) {
|
||||||
return null;
|
try {
|
||||||
|
var stmt = conn.prepareStatement(DELETE_CLIENT);
|
||||||
|
stmt.setString(1, clientId);
|
||||||
|
stmt.execute();
|
||||||
|
stmt = conn.prepareStatement(DELETE_CLIENT_REDIRECTS);
|
||||||
|
stmt.setString(1, clientId);
|
||||||
|
stmt.execute();
|
||||||
|
return this;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClientService save(Client client) {
|
public ClientService save(Client client) {
|
||||||
return null;
|
try {
|
||||||
|
var stmt = conn.prepareStatement(SAVE_CLIENT);
|
||||||
|
stmt.setString(1, client.id());
|
||||||
|
stmt.setString(2, client.name());
|
||||||
|
stmt.setString(3, client.secret());
|
||||||
|
stmt.setString(4, client.name());
|
||||||
|
stmt.setString(5, client.secret());
|
||||||
|
stmt.execute();
|
||||||
|
stmt = conn.prepareStatement(SAVE_REDIRECT);
|
||||||
|
stmt.setString(1, client.id());
|
||||||
|
for (var redirect : client.redirectUris()) {
|
||||||
|
stmt.setString(2, redirect);
|
||||||
|
stmt.execute();
|
||||||
|
}
|
||||||
|
var where = "(" + client.redirectUris().stream().map(u -> "?").collect(Collectors.joining(", ")) + ")";
|
||||||
|
var sql = DROP_OTHER_REDIRECTS + where;
|
||||||
|
stmt = conn.prepareStatement(sql);
|
||||||
|
stmt.setString(1, client.id());
|
||||||
|
int i = 2;
|
||||||
|
for (var redirect : client.redirectUris()) stmt.setString(i++, redirect);
|
||||||
|
stmt.execute();
|
||||||
|
return this;
|
||||||
|
} 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.ClientService;
|
||||||
|
import de.srsoftware.oidc.api.ClientServiceTest;
|
||||||
|
import java.io.File;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
|
||||||
|
public class SqlteClientServiceTest extends ClientServiceTest {
|
||||||
|
private ClientService clientService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ClientService clientService() {
|
||||||
|
return clientService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setup() throws SQLException {
|
||||||
|
var dbFile = new File("/tmp/" + uuid() + ".sqlite");
|
||||||
|
var conn = new ConnectionProvider().get(dbFile);
|
||||||
|
clientService = new SqliteClientService(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user