|
14 | 14 |
|
15 | 15 | package com.predic8.membrane.core.interceptor.apikey.stores; |
16 | 16 |
|
| 17 | +import com.predic8.membrane.annot.MCAttribute; |
17 | 18 | import com.predic8.membrane.annot.MCChildElement; |
18 | 19 | import com.predic8.membrane.annot.MCElement; |
19 | 20 | import com.predic8.membrane.core.Router; |
|
22 | 23 | import org.jetbrains.annotations.NotNull; |
23 | 24 |
|
24 | 25 | import java.sql.*; |
25 | | -import java.util.ArrayList; |
26 | | -import java.util.List; |
27 | | -import java.util.Optional; |
| 26 | +import java.util.*; |
28 | 27 |
|
| 28 | +/** |
| 29 | + * @description <p>JDBC database store for API keys and their associated scopes.</p> |
| 30 | + * @topic 3. Security and Validation |
| 31 | + */ |
29 | 32 | @MCElement(name = "databaseApiKeyStore") |
30 | 33 | public class JDBCApiKeyStore extends AbstractJdbcSupport implements ApiKeyStore { |
31 | 34 |
|
32 | 35 | private KeyTable keyTable; |
33 | 36 | private ScopeTable scopeTable; |
| 37 | + private boolean autoCreate = true; |
| 38 | + |
| 39 | + private static final String CREATE_SCOPE_TABLE = """ |
| 40 | + CREATE TABLE %s ( |
| 41 | + apikey VARCHAR(255) NOT NULL REFERENCES %s (apikey), |
| 42 | + scope VARCHAR(255) NOT NULL |
| 43 | + ) |
| 44 | + """; |
| 45 | + private static final String CREATE_KEY_TABLE = """ |
| 46 | + CREATE TABLE %s ( |
| 47 | + apikey VARCHAR(255) NOT NULL PRIMARY KEY |
| 48 | + ) |
| 49 | + """; |
34 | 50 |
|
35 | 51 | @Override |
36 | 52 | public void init(Router router) { |
@@ -74,46 +90,75 @@ private void checkApiKey(String apiKey) throws Exception { |
74 | 90 | } |
75 | 91 |
|
76 | 92 | private void createTablesIfNotExist() { |
| 93 | + if (!autoCreate) { |
| 94 | + return; |
| 95 | + } |
| 96 | + |
77 | 97 | try (Connection connection = getDatasource().getConnection()) { |
78 | | - if (tableExists(connection, keyTable.getName())) { |
79 | | - connection.createStatement().executeUpdate(String.format(""" |
80 | | - CREATE TABLE %s ( |
81 | | - apikey VARCHAR(255) NOT NULL PRIMARY KEY |
82 | | - ) |
83 | | - """, keyTable.getName())); |
| 98 | + if (!tableExists(connection, keyTable.getName())) { |
| 99 | + createKeyTable(connection); |
84 | 100 | } |
85 | | - if (tableExists(connection, scopeTable.getName())) { |
86 | | - connection.createStatement().executeUpdate(String.format(""" |
87 | | - CREATE TABLE %s ( |
88 | | - apikey VARCHAR(255) NOT NULL REFERENCES %s (apikey), |
89 | | - scope VARCHAR(255) NOT NULL |
90 | | - ) |
91 | | - """, scopeTable.getName(), keyTable.getName())); |
| 101 | + if (!tableExists(connection, scopeTable.getName())) { |
| 102 | + createScopeTable(connection); |
92 | 103 | } |
93 | 104 | } catch (Exception e) { |
94 | | - throw new ConfigurationException("Failed to create tables for API Keys %s and %s: ".formatted(keyTable.getName(),scopeTable.getName()), e); |
| 105 | + throw new ConfigurationException("Failed to create tables for API Keys %s and %s: ".formatted(keyTable.getName(), scopeTable.getName()), e); |
95 | 106 | } |
96 | 107 | } |
97 | 108 |
|
| 109 | + private void createKeyTable(Connection connection) throws SQLException { |
| 110 | + connection.createStatement().executeUpdate(CREATE_KEY_TABLE.formatted(keyTable.getName())); |
| 111 | + } |
| 112 | + |
| 113 | + private void createScopeTable(Connection connection) throws SQLException { |
| 114 | + connection.createStatement().executeUpdate(CREATE_SCOPE_TABLE.formatted(scopeTable.getName(), keyTable.getName())); |
| 115 | + } |
| 116 | + |
98 | 117 | private boolean tableExists(Connection connection, String tableName) throws SQLException { |
99 | | - return !connection.getMetaData().getTables(null, null, tableName.toUpperCase(), null).next(); |
| 118 | + try (ResultSet rs = connection.getMetaData().getTables(null, connection.getSchema(), "%", new String[]{"TABLE"})) { |
| 119 | + while (rs.next()) { |
| 120 | + if (tableName.equalsIgnoreCase(rs.getString("TABLE_NAME"))) { |
| 121 | + return true; |
| 122 | + } |
| 123 | + } |
| 124 | + } |
| 125 | + return false; |
100 | 126 | } |
101 | 127 |
|
| 128 | + /** |
| 129 | + * @descriptio Table with the scopes. |
| 130 | + */ |
102 | 131 | @MCChildElement(order = 0) |
103 | 132 | public void setKeyTable(KeyTable keyTable) { |
104 | 133 | this.keyTable = keyTable; |
105 | 134 | } |
106 | 135 |
|
| 136 | + /** |
| 137 | + * @description The table mapping API keys to their scopes. |
| 138 | + */ |
107 | 139 | @MCChildElement(order = 1) |
108 | 140 | public void setScopeTable(ScopeTable scopeTable) { |
109 | 141 | this.scopeTable = scopeTable; |
110 | 142 | } |
111 | 143 |
|
| 144 | + /** |
| 145 | + * @description Whether the required tables should be created automatically on startup. |
| 146 | + * @default true |
| 147 | + */ |
| 148 | + @MCAttribute |
| 149 | + public void setAutoCreate(boolean autoCreate) { |
| 150 | + this.autoCreate = autoCreate; |
| 151 | + } |
| 152 | + |
112 | 153 | public KeyTable getKeyTable() { |
113 | 154 | return keyTable; |
114 | 155 | } |
115 | 156 |
|
116 | 157 | public ScopeTable getScopeTable() { |
117 | 158 | return scopeTable; |
118 | 159 | } |
| 160 | + |
| 161 | + public boolean isAutoCreate() { |
| 162 | + return autoCreate; |
| 163 | + } |
119 | 164 | } |
0 commit comments