This is an automated email from the ASF dual-hosted git repository.
smolnar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git
The following commit(s) were added to refs/heads/master by this push:
new dd716ea KNOX-2602 - Added the enabled flag and related token service
API into the tokens' metadata. (#447)
dd716ea is described below
commit dd716eafc5da45ae39f85300ab62c63ef15ee55a
Author: Sandor Molnar <[email protected]>
AuthorDate: Wed May 5 15:49:02 2021 +0200
KNOX-2602 - Added the enabled flag and related token service API into the
tokens' metadata. (#447)
Instead of adding another column in KNOX_TABLES, a brand new DB table was
introduced where an arbitrary number of token metadata can be stored
(identified by the given token's ID and the metadata name).
---
.../services/token/impl/JDBCTokenStateService.java | 30 +++++++++++-
.../services/token/impl/TokenStateDatabase.java | 50 ++++++++++++++------
.../token/impl/TokenStateServiceMessages.java | 2 +-
.../token/impl/state/FileTokenStateJournal.java | 18 +++++---
.../resources/createKnoxTokenDatabaseTable.sql | 2 -
...ql => createKnoxTokenMetadataDatabaseTable.sql} | 12 ++---
.../token/impl/DefaultTokenStateServiceTest.java | 6 ++-
.../token/impl/JDBCTokenStateServiceTest.java | 30 +++++++++---
.../token/impl/ZookeeperTokenStateServiceTest.java | 3 +-
.../impl/state/FileTokenStateJournalTest.java | 17 +++++--
.../gateway/service/knoxtoken/TokenResource.java | 43 ++++++++++++++++++
.../service/knoxtoken/TokenServiceMessages.java | 3 ++
.../services/security/token/TokenMetadata.java | 53 ++++++++++++++++------
13 files changed, 209 insertions(+), 60 deletions(-)
diff --git
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateService.java
b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateService.java
index 472200d..4907188 100644
---
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateService.java
+++
b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateService.java
@@ -24,6 +24,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
+import org.apache.commons.lang3.StringUtils;
import org.apache.knox.gateway.config.GatewayConfig;
import org.apache.knox.gateway.services.ServiceLifecycleException;
import org.apache.knox.gateway.services.security.AliasService;
@@ -38,6 +39,7 @@ public class JDBCTokenStateService extends
DefaultTokenStateService {
private TokenStateDatabase tokenDatabase;
private AtomicBoolean initialized = new AtomicBoolean(false);
private Lock initLock = new ReentrantLock(true);
+ private Lock addMetadataLock = new ReentrantLock(true);
public void setAliasService(AliasService aliasService) {
this.aliasService = aliasService;
@@ -194,7 +196,8 @@ public class JDBCTokenStateService extends
DefaultTokenStateService {
@Override
public void addMetadata(String tokenId, TokenMetadata metadata) {
try {
- final boolean added = tokenDatabase.addMetadata(tokenId, metadata);
+ boolean added = saveMetadataMapInDatabase(tokenId,
metadata.getMetadataMap());
+
if (added) {
log.updatedMetadataInDatabase(Tokens.getTokenIDDisplayText(tokenId));
@@ -210,6 +213,31 @@ public class JDBCTokenStateService extends
DefaultTokenStateService {
}
}
+ private boolean saveMetadataMapInDatabase(String tokenId, Map<String,
String> metadataMap) throws SQLException {
+ addMetadataLock.lock();
+ try {
+ boolean saved = false;
+ for (Map.Entry<String, String> metadataMapEntry :
metadataMap.entrySet()) {
+ if (StringUtils.isNotBlank(metadataMapEntry.getValue())) {
+ if (upsertTokenMetadata(tokenId, metadataMapEntry.getKey(),
metadataMapEntry.getValue())) {
+ saved = true;
+ }
+ }
+ }
+ return saved;
+ } finally {
+ addMetadataLock.unlock();
+ }
+ }
+
+ private boolean upsertTokenMetadata(String tokenId, String metadataName,
String metadataValue) throws SQLException {
+ if (!tokenDatabase.updateMetadata(tokenId, metadataName, metadataValue)) {
+ return tokenDatabase.addMetadata(tokenId, metadataName, metadataValue);
+ } else {
+ return true; //successfully updated
+ }
+ }
+
@Override
public TokenMetadata getTokenMetadata(String tokenId) throws
UnknownTokenException {
TokenMetadata tokenMetadata = null;
diff --git
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateDatabase.java
b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateDatabase.java
index 2693093..88bd68d 100644
---
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateDatabase.java
+++
b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateDatabase.java
@@ -18,6 +18,7 @@
package org.apache.knox.gateway.services.token.impl;
import static java.nio.charset.StandardCharsets.UTF_8;
+
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
@@ -25,7 +26,9 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
+import java.util.HashMap;
import java.util.Locale;
+import java.util.Map;
import javax.sql.DataSource;
@@ -34,34 +37,38 @@ import
org.apache.knox.gateway.services.security.token.TokenMetadata;
public class TokenStateDatabase {
private static final String TOKENS_TABLE_CREATE_SQL_FILE_NAME =
"createKnoxTokenDatabaseTable.sql";
+ private static final String TOKEN_METADATA_TABLE_CREATE_SQL_FILE_NAME =
"createKnoxTokenMetadataDatabaseTable.sql";
static final String TOKENS_TABLE_NAME = "KNOX_TOKENS";
+ static final String TOKEN_METADATA_TABLE_NAME = "KNOX_TOKEN_METADATA";
private static final String ADD_TOKEN_SQL = "INSERT INTO " +
TOKENS_TABLE_NAME + "(token_id, issue_time, expiration, max_lifetime) VALUES(?,
?, ?, ?)";
private static final String REMOVE_TOKEN_SQL = "DELETE FROM " +
TOKENS_TABLE_NAME + " WHERE token_id = ?";
private static final String REMOVE_EXPIRED_TOKENS_SQL = "DELETE FROM " +
TOKENS_TABLE_NAME + " WHERE expiration < ?";
static final String GET_TOKEN_EXPIRATION_SQL = "SELECT expiration FROM " +
TOKENS_TABLE_NAME + " WHERE token_id = ?";
private static final String UPDATE_TOKEN_EXPIRATION_SQL = "UPDATE " +
TOKENS_TABLE_NAME + " SET expiration = ? WHERE token_id = ?";
static final String GET_MAX_LIFETIME_SQL = "SELECT max_lifetime FROM " +
TOKENS_TABLE_NAME + " WHERE token_id = ?";
- private static final String ADD_METADATA_SQL = "UPDATE " + TOKENS_TABLE_NAME
+ " SET username = ?, comment = ? WHERE token_id = ?";
- private static final String GET_METADATA_SQL = "SELECT username, comment
FROM " + TOKENS_TABLE_NAME + " WHERE token_id = ?";
+ private static final String ADD_METADATA_SQL = "INSERT INTO " +
TOKEN_METADATA_TABLE_NAME + "(token_id, md_name, md_value) VALUES(?, ?, ?)";
+ private static final String UPDATE_METADATA_SQL = "UPDATE " +
TOKEN_METADATA_TABLE_NAME + " SET md_value = ? WHERE token_id = ? AND md_name =
?";
+ private static final String GET_METADATA_SQL = "SELECT md_name, md_value
FROM " + TOKEN_METADATA_TABLE_NAME + " WHERE token_id = ?";
private final DataSource dataSource;
TokenStateDatabase(DataSource dataSource) throws Exception {
this.dataSource = dataSource;
- createKnoxTokensTableIfNotExists();
+ createTableIfNotExists(TOKENS_TABLE_NAME,
TOKENS_TABLE_CREATE_SQL_FILE_NAME);
+ createTableIfNotExists(TOKEN_METADATA_TABLE_NAME,
TOKEN_METADATA_TABLE_CREATE_SQL_FILE_NAME);
}
- private void createKnoxTokensTableIfNotExists() throws Exception {
- if (!isKnoxTokensTableExist()) {
- createKnoxTokensTable();
+ private void createTableIfNotExists(String tableName, String
createSqlFileName) throws Exception {
+ if (!isTableExists(tableName)) {
+ createTable(createSqlFileName);
}
}
- private boolean isKnoxTokensTableExist() throws SQLException {
+ private boolean isTableExists(String tableName) throws SQLException {
boolean exists = false;
try (Connection connection = dataSource.getConnection()) {
final DatabaseMetaData dbMetadata = connection.getMetaData();
- final String tableNameToCheck = dbMetadata.storesUpperCaseIdentifiers()
? TOKENS_TABLE_NAME : TOKENS_TABLE_NAME.toLowerCase(Locale.ROOT);
+ final String tableNameToCheck = dbMetadata.storesUpperCaseIdentifiers()
? tableName : tableName.toLowerCase(Locale.ROOT);
try (ResultSet tables = dbMetadata.getTables(connection.getCatalog(),
null, tableNameToCheck, null)) {
exists = tables.next();
}
@@ -69,8 +76,8 @@ public class TokenStateDatabase {
return exists;
}
- private void createKnoxTokensTable() throws Exception {
- final InputStream is =
TokenStateDatabase.class.getClassLoader().getResourceAsStream(TOKENS_TABLE_CREATE_SQL_FILE_NAME);
+ private void createTable(String createSqlFileName) throws Exception {
+ final InputStream is =
TokenStateDatabase.class.getClassLoader().getResourceAsStream(createSqlFileName);
final String createTableSql = IOUtils.toString(is, UTF_8);
try (Connection connection = dataSource.getConnection(); Statement
createTableStatment = connection.createStatement();) {
createTableStatment.execute(createTableSql);
@@ -127,11 +134,20 @@ public class TokenStateDatabase {
}
}
- boolean addMetadata(String tokenId, TokenMetadata metadata) throws
SQLException {
+ boolean updateMetadata(String tokenId, String metadataName, String
metadataValue) throws SQLException {
+ try (Connection connection = dataSource.getConnection(); PreparedStatement
updateMetadataStatement = connection.prepareStatement(UPDATE_METADATA_SQL)) {
+ updateMetadataStatement.setString(1, metadataValue);
+ updateMetadataStatement.setString(2, tokenId);
+ updateMetadataStatement.setString(3, metadataName);
+ return updateMetadataStatement.executeUpdate() == 1;
+ }
+ }
+
+ boolean addMetadata(String tokenId, String metadataName, String
metadataValue) throws SQLException {
try (Connection connection = dataSource.getConnection(); PreparedStatement
addMetadataStatement = connection.prepareStatement(ADD_METADATA_SQL)) {
- addMetadataStatement.setString(1, metadata.getUserName());
- addMetadataStatement.setString(2, metadata.getComment());
- addMetadataStatement.setString(3, tokenId);
+ addMetadataStatement.setString(1, tokenId);
+ addMetadataStatement.setString(2, metadataName);
+ addMetadataStatement.setString(3, metadataValue);
return addMetadataStatement.executeUpdate() == 1;
}
}
@@ -140,7 +156,11 @@ public class TokenStateDatabase {
try (Connection connection = dataSource.getConnection(); PreparedStatement
getMaxLifetimeStatement = connection.prepareStatement(GET_METADATA_SQL)) {
getMaxLifetimeStatement.setString(1, tokenId);
try (ResultSet rs = getMaxLifetimeStatement.executeQuery()) {
- return rs.next() ? new TokenMetadata(rs.getString(1), rs.getString(2))
: null;
+ final Map<String, String> metadataMap = new HashMap<>();
+ while (rs.next()) {
+ metadataMap.put(rs.getString(1), rs.getString(2));
+ }
+ return metadataMap.isEmpty() ? null : new TokenMetadata(metadataMap);
}
}
}
diff --git
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateServiceMessages.java
b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateServiceMessages.java
index a5eeece..101c3cc 100644
---
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateServiceMessages.java
+++
b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/TokenStateServiceMessages.java
@@ -223,7 +223,7 @@ public interface TokenStateServiceMessages {
@Message(level = MessageLevel.DEBUG, text = "Updated metadata for {0} in the
database")
void updatedMetadataInDatabase(String tokenId);
- @Message(level = MessageLevel.DEBUG, text = "Failed to update metadata for
{0} in the database")
+ @Message(level = MessageLevel.DEBUG, text = "Failed to save/update metadata
for {0} in the database")
void failedToUpdateMetadataInDatabase(String tokenId);
@Message(level = MessageLevel.ERROR, text = "An error occurred while
updating metadata for {0} in the database : {1}")
diff --git
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/state/FileTokenStateJournal.java
b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/state/FileTokenStateJournal.java
index 966766d..cdccad6 100644
---
a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/state/FileTokenStateJournal.java
+++
b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/state/FileTokenStateJournal.java
@@ -50,8 +50,9 @@ abstract class FileTokenStateJournal implements
TokenStateJournal {
protected static final int INDEX_ISSUE_TIME = 1;
protected static final int INDEX_EXPIRATION = 2;
protected static final int INDEX_MAX_LIFETIME = 3;
- protected static final int INDEX_USERNAME = 4;
- protected static final int INDEX_COMMENT = 5;
+ protected static final int INDEX_ENABLED = 4;
+ protected static final int INDEX_USERNAME = 5;
+ protected static final int INDEX_COMMENT = 6;
protected static final TokenStateServiceMessages log =
MessagesFactory.get(TokenStateServiceMessages.class);
@@ -190,7 +191,7 @@ abstract class FileTokenStateJournal implements
TokenStateJournal {
@Override
public String toString() {
- String[] elements = new String[6];
+ String[] elements = new String[7];
elements[INDEX_TOKEN_ID] = getTokenId();
@@ -203,6 +204,9 @@ abstract class FileTokenStateJournal implements
TokenStateJournal {
String maxLifetime = getMaxLifetime();
elements[INDEX_MAX_LIFETIME] = (maxLifetime != null) ? maxLifetime
: "";
+ String enabled = getTokenMetadata() == null ? "" :
String.valueOf(getTokenMetadata().isEnabled());
+ elements[INDEX_ENABLED] = enabled;
+
String userName = getTokenMetadata() == null ? "" :
(getTokenMetadata().getUserName() == null ? "" :
getTokenMetadata().getUserName());
elements[INDEX_USERNAME] = userName;
@@ -210,11 +214,12 @@ abstract class FileTokenStateJournal implements
TokenStateJournal {
elements[INDEX_COMMENT] = comment;
return String.format(Locale.ROOT,
- "%s,%s,%s,%s,%s,%s",
+ "%s,%s,%s,%s,%s,%s,%s",
elements[INDEX_TOKEN_ID],
elements[INDEX_ISSUE_TIME],
elements[INDEX_EXPIRATION],
elements[INDEX_MAX_LIFETIME],
+ elements[INDEX_ENABLED],
elements[INDEX_USERNAME],
elements[INDEX_COMMENT]);
}
@@ -228,7 +233,7 @@ abstract class FileTokenStateJournal implements
TokenStateJournal {
*/
static FileJournalEntry parse(final String entry) {
String[] elements = entry.split(",", -1);
- if (elements.length < 6) {
+ if (elements.length < 7) {
throw new IllegalArgumentException("Invalid journal entry: " +
entry);
}
@@ -236,6 +241,7 @@ abstract class FileTokenStateJournal implements
TokenStateJournal {
String issueTime = elements[INDEX_ISSUE_TIME].trim();
String expiration = elements[INDEX_EXPIRATION].trim();
String maxLifetime = elements[INDEX_MAX_LIFETIME].trim();
+ String enabled = elements[INDEX_ENABLED].trim();
String userName = elements[INDEX_USERNAME].trim();
String comment = elements[INDEX_COMMENT].trim();
@@ -243,7 +249,7 @@ abstract class FileTokenStateJournal implements
TokenStateJournal {
issueTime.isEmpty() ? null : issueTime,
expiration.isEmpty() ? null :
expiration,
maxLifetime.isEmpty() ? null :
maxLifetime,
- new TokenMetadata(userName.isEmpty() ?
null : userName, comment.isEmpty() ? null : comment));
+ new TokenMetadata(userName.isEmpty() ?
null : userName, comment.isEmpty() ? null : comment,
Boolean.parseBoolean(enabled)));
}
}
diff --git a/gateway-server/src/main/resources/createKnoxTokenDatabaseTable.sql
b/gateway-server/src/main/resources/createKnoxTokenDatabaseTable.sql
index faeca79..598ed7e 100644
--- a/gateway-server/src/main/resources/createKnoxTokenDatabaseTable.sql
+++ b/gateway-server/src/main/resources/createKnoxTokenDatabaseTable.sql
@@ -18,7 +18,5 @@ CREATE TABLE KNOX_TOKENS (
issue_time bigint NOT NULL,
expiration bigint NOT NULL,
max_lifetime bigint NOT NULL,
- username varchar(128),
- comment varchar(256),
PRIMARY KEY (token_id)
)
\ No newline at end of file
diff --git a/gateway-server/src/main/resources/createKnoxTokenDatabaseTable.sql
b/gateway-server/src/main/resources/createKnoxTokenMetadataDatabaseTable.sql
similarity index 77%
copy from gateway-server/src/main/resources/createKnoxTokenDatabaseTable.sql
copy to
gateway-server/src/main/resources/createKnoxTokenMetadataDatabaseTable.sql
index faeca79..9a11380 100644
--- a/gateway-server/src/main/resources/createKnoxTokenDatabaseTable.sql
+++ b/gateway-server/src/main/resources/createKnoxTokenMetadataDatabaseTable.sql
@@ -13,12 +13,10 @@
-- License for the specific language governing permissions and limitations
under
-- the License.
-CREATE TABLE KNOX_TOKENS (
+CREATE TABLE KNOX_TOKEN_METADATA (
token_id varchar(128) NOT NULL,
- issue_time bigint NOT NULL,
- expiration bigint NOT NULL,
- max_lifetime bigint NOT NULL,
- username varchar(128),
- comment varchar(256),
- PRIMARY KEY (token_id)
+ md_name varchar(32) NOT NULL,
+ md_value varchar(256) NOT NULL,
+ PRIMARY KEY (token_id, md_name),
+ CONSTRAINT fk_token_id FOREIGN KEY(token_id) REFERENCES
KNOX_TOKENS(token_id) ON DELETE CASCADE
)
\ No newline at end of file
diff --git
a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateServiceTest.java
b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateServiceTest.java
index f11aa81..8d9836d 100644
---
a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateServiceTest.java
+++
b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenStateServiceTest.java
@@ -19,6 +19,7 @@ package org.apache.knox.gateway.services.token.impl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -292,12 +293,13 @@ public class DefaultTokenStateServiceTest {
tss.addMetadata(token.getClaim(JWTToken.KNOX_ID_CLAIM), new
TokenMetadata(userName));
assertNotNull(tss.getTokenMetadata(tokenId));
assertEquals(tss.getTokenMetadata(tokenId).getUserName(), userName);
- assertTrue(tss.getTokenMetadata(tokenId).getComment().isEmpty());
+ assertNull(tss.getTokenMetadata(tokenId).getComment());
final String comment = "this is my test comment";
- tss.addMetadata(token.getClaim(JWTToken.KNOX_ID_CLAIM), new
TokenMetadata(userName, comment));
+ tss.addMetadata(token.getClaim(JWTToken.KNOX_ID_CLAIM), new
TokenMetadata(userName, comment, true));
assertNotNull(tss.getTokenMetadata(tokenId));
assertEquals(tss.getTokenMetadata(tokenId).getComment(), comment);
+ assertTrue(tss.getTokenMetadata(tokenId).isEnabled());
}
protected static JWTToken createMockToken(final long expiration) {
diff --git
a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateServiceTest.java
b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateServiceTest.java
index b9ef2b7..7658e4f 100644
---
a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateServiceTest.java
+++
b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/JDBCTokenStateServiceTest.java
@@ -18,6 +18,7 @@
package org.apache.knox.gateway.services.token.impl;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.nio.file.Path;
@@ -47,9 +48,8 @@ import org.junit.rules.TemporaryFolder;
public class JDBCTokenStateServiceTest {
private static final String GET_TOKENS_COUNT_SQL = "SELECT count(*) FROM " +
TokenStateDatabase.TOKENS_TABLE_NAME;
- private static final String GET_USERNAME_SQL = "SELECT username FROM " +
TokenStateDatabase.TOKENS_TABLE_NAME + " WHERE token_id = ?";
- private static final String GET_COMMENT_SQL = "SELECT comment FROM " +
TokenStateDatabase.TOKENS_TABLE_NAME + " WHERE token_id = ?";
- private static final String TRUNCATE_KNOX_TOKENS_SQL = "TRUNCATE TABLE " +
TokenStateDatabase.TOKENS_TABLE_NAME;
+ private static final String TRUNCATE_KNOX_TOKENS_SQL = "DELETE FROM " +
TokenStateDatabase.TOKENS_TABLE_NAME;
+ private static final String TRUNCATE_KNOX_TOKEN_METADATA_SQL = "DELETE FROM
" + TokenStateDatabase.TOKEN_METADATA_TABLE_NAME;
@ClassRule
public static final TemporaryFolder testFolder = new TemporaryFolder();
@@ -143,15 +143,25 @@ public class JDBCTokenStateServiceTest {
@Test(expected = UnknownTokenException.class)
public void testAddMetadata() throws Exception {
final String tokenId = UUID.randomUUID().toString();
+ final TokenMetadata tokenMetadata = new TokenMetadata("sampleUser", "my
test comment", false);
jdbcTokenStateService.addToken(tokenId, 1, 1, 1);
- jdbcTokenStateService.addMetadata(tokenId, new TokenMetadata("sampleUser",
"my test comment"));
+ jdbcTokenStateService.addMetadata(tokenId, tokenMetadata);
assertEquals("sampleUser",
jdbcTokenStateService.getTokenMetadata(tokenId).getUserName());
assertEquals("my test comment",
jdbcTokenStateService.getTokenMetadata(tokenId).getComment());
+ assertFalse(jdbcTokenStateService.getTokenMetadata(tokenId).isEnabled());
- assertEquals("sampleUser", getStringTokenAttributeFromDatabase(tokenId,
GET_USERNAME_SQL));
- assertEquals("my test comment",
getStringTokenAttributeFromDatabase(tokenId, GET_COMMENT_SQL));
+ assertEquals("sampleUser", getStringTokenAttributeFromDatabase(tokenId,
getSelectMetadataSql(TokenMetadata.USER_NAME)));
+ assertEquals("my test comment",
getStringTokenAttributeFromDatabase(tokenId,
getSelectMetadataSql(TokenMetadata.COMMENT)));
+ assertEquals("false", getStringTokenAttributeFromDatabase(tokenId,
getSelectMetadataSql(TokenMetadata.ENABLED)));
+ //enable the token (it was disabled)
+ tokenMetadata.setEnabled(true);
+ jdbcTokenStateService.addMetadata(tokenId, tokenMetadata);
+ assertTrue(jdbcTokenStateService.getTokenMetadata(tokenId).isEnabled());
+ assertEquals("true", getStringTokenAttributeFromDatabase(tokenId,
getSelectMetadataSql(TokenMetadata.ENABLED)));
+
+ //remove and get -> expect UnknownTokenException
jdbcTokenStateService.removeToken(tokenId);
jdbcTokenStateService.getTokenMetadata(tokenId);
}
@@ -190,9 +200,17 @@ public class JDBCTokenStateServiceTest {
}
private void truncateDatabase() throws SQLException {
+ try (Connection conn = derbyDatabase.getConnection(); PreparedStatement
stmt = conn.prepareStatement(TRUNCATE_KNOX_TOKEN_METADATA_SQL)) {
+ stmt.executeUpdate();
+ }
+
try (Connection conn = derbyDatabase.getConnection(); PreparedStatement
stmt = conn.prepareStatement(TRUNCATE_KNOX_TOKENS_SQL)) {
stmt.executeUpdate();
}
}
+ private String getSelectMetadataSql(String metadataName) {
+ return "SELECT md_value FROM " +
TokenStateDatabase.TOKEN_METADATA_TABLE_NAME + " WHERE token_id = ? AND md_name
= '" + metadataName + "'";
+ }
+
}
diff --git
a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/ZookeeperTokenStateServiceTest.java
b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/ZookeeperTokenStateServiceTest.java
index b446651..ee89293 100644
---
a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/ZookeeperTokenStateServiceTest.java
+++
b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/ZookeeperTokenStateServiceTest.java
@@ -122,10 +122,11 @@ public class ZookeeperTokenStateServiceTest {
final String userName = "testUser";
final String comment = "This is my test comment";
- zktokenStateServiceNode1.addMetadata(tokenId, new TokenMetadata(userName,
comment));
+ zktokenStateServiceNode1.addMetadata(tokenId, new TokenMetadata(userName,
comment, true));
Thread.sleep(LONG_TOKEN_STATE_ALIAS_PERSISTENCE_INTERVAL * 1000);
assertEquals(userName,
zktokenStateServiceNode2.getTokenMetadata(tokenId).getUserName());
assertEquals(comment,
zktokenStateServiceNode2.getTokenMetadata(tokenId).getComment());
+ assertTrue(zktokenStateServiceNode2.getTokenMetadata(tokenId).isEnabled());
}
@Test
diff --git
a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/state/FileTokenStateJournalTest.java
b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/state/FileTokenStateJournalTest.java
index 2fe601d..9d99ff2 100644
---
a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/state/FileTokenStateJournalTest.java
+++
b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/state/FileTokenStateJournalTest.java
@@ -25,6 +25,7 @@ import static org.junit.Assert.assertNull;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
+import org.apache.commons.lang3.StringUtils;
import org.apache.knox.gateway.services.token.state.JournalEntry;
import org.junit.Test;
@@ -47,7 +48,7 @@ public class FileTokenStateJournalTest {
final Long expiration = issueTime + TimeUnit.HOURS.toMillis(1);
final Long maxLifetime = null;
- doTestParseJournalEntry(tokenId, issueTime, expiration, maxLifetime,
"user", null);
+ doTestParseJournalEntry(tokenId, issueTime, expiration, maxLifetime,
Boolean.TRUE, "user", null);
}
@Test
@@ -77,29 +78,32 @@ public class FileTokenStateJournalTest {
@Test
public void tesParseTokenMetadata() throws Exception {
- doTestParseJournalEntry("", "", "", "", "userName", "");
- doTestParseJournalEntry("", "", "", "", "", "comment");
+ doTestParseJournalEntry("", "", "", "", "", "userName", "");
+ doTestParseJournalEntry("", "", "", "", "", "", "comment");
+ doTestParseJournalEntry("", "", "", "", "false", "", "");
}
@Test
public void testParseJournalEntry_AllMissing() {
- doTestParseJournalEntry(null, null, null, " ", null, null);
+ doTestParseJournalEntry(null, null, null, " ", null, null, null);
}
private void doTestParseJournalEntry(final String tokenId, final Long
issueTime, final Long expiration, final Long maxLifetime) {
- doTestParseJournalEntry(tokenId, issueTime, expiration, maxLifetime,
null, null);
+ doTestParseJournalEntry(tokenId, issueTime, expiration, maxLifetime,
null, null, null);
}
private void doTestParseJournalEntry(final String tokenId,
final Long issueTime,
final Long expiration,
final Long maxLifetime,
+ final Boolean enabled,
final String userName,
final String comment) {
doTestParseJournalEntry(tokenId,
(issueTime != null ? issueTime.toString() :
null),
(expiration != null ? expiration.toString() :
null),
(maxLifetime != null ? maxLifetime.toString()
: null),
+ (enabled != null ? enabled.toString() : null),
userName, comment);
}
@@ -107,6 +111,7 @@ public class FileTokenStateJournalTest {
final String issueTime,
final String expiration,
final String maxLifetime,
+ final String enabled,
final String userName,
final String comment) {
StringBuilder entryStringBuilder =
@@ -116,6 +121,7 @@ public class FileTokenStateJournalTest {
.append(expiration != null ? expiration : "")
.append(',')
.append(maxLifetime != null ? maxLifetime : "")
+
.append(",").append(enabled != null ? enabled : "")
.append(",").append(userName == null ? "" : userName)
.append(",").append(comment == null ? "" : comment);
@@ -125,6 +131,7 @@ public class FileTokenStateJournalTest {
assertJournalEntryField(issueTime, entry.getIssueTime());
assertJournalEntryField(expiration, entry.getExpiration());
assertJournalEntryField(maxLifetime, entry.getMaxLifetime());
+ assertJournalEntryField(StringUtils.isBlank(enabled) ? "false" :
enabled, String.valueOf(entry.getTokenMetadata().isEnabled()));
assertJournalEntryField(userName,
entry.getTokenMetadata().getUserName());
assertJournalEntryField(comment,
entry.getTokenMetadata().getComment());
}
diff --git
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
index 3c78c6b..e484f56 100644
---
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
+++
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
@@ -103,6 +103,8 @@ public class TokenResource {
static final String GET_TSS_STATUS_PATH = "/getTssStatus";
static final String RENEW_PATH = "/renew";
static final String REVOKE_PATH = "/revoke";
+ static final String ENABLE_PATH = "/enable";
+ static final String DISABLE_PATH = "/disable";
private static final String TARGET_ENDPOINT_PULIC_CERT_PEM =
"knox.token.target.endpoint.cert.pem";
private static TokenServiceMessages log =
MessagesFactory.get(TokenServiceMessages.class);
private long tokenTTL = TOKEN_TTL_DEFAULT;
@@ -410,6 +412,47 @@ public class TokenResource {
return resp;
}
+ @POST
+ @Path(ENABLE_PATH)
+ @Produces({ APPLICATION_JSON })
+ public Response enable(String tokenId) {
+ return setTokenEnabledFlag(tokenId, true);
+ }
+
+ @POST
+ @Path(DISABLE_PATH)
+ @Produces({ APPLICATION_JSON })
+ public Response disable(String tokenId) {
+ return setTokenEnabledFlag(tokenId, false);
+ }
+
+ private Response setTokenEnabledFlag(String tokenId, boolean enabled) {
+ String error = "";
+ if (tokenStateService == null) {
+ error = "Unable to " + (enabled ? "enable" : "disable") + " tokens
because token management is not configured";
+ } else {
+ try {
+ final TokenMetadata tokenMetadata =
tokenStateService.getTokenMetadata(tokenId);
+ if (enabled && tokenMetadata.isEnabled()) {
+ error = "Token is already enabled";
+ } else if (!enabled && !tokenMetadata.isEnabled()) {
+ error = "Token is already disabled";
+ } else {
+ tokenMetadata.setEnabled(enabled);
+ tokenStateService.addMetadata(tokenId, tokenMetadata);
+ }
+ } catch (UnknownTokenException e) {
+ error = safeGetMessage(e);
+ }
+ }
+ if (error.isEmpty()) {
+ return Response.status(Response.Status.OK).entity("{\n
\"setEnabledFlag\": \"true\",\n \"isEnabled\": \"" + enabled +
"\"\n}\n").build();
+ } else {
+ log.badSetEnabledFlagRequest(getTopologyName(),
Tokens.getTokenIDDisplayText(tokenId), error);
+ return Response.status(Response.Status.BAD_REQUEST).entity("{\n
\"setEnabledFlag\": \"false\",\n \"error\": \"" + error + "\"\n}\n").build();
+ }
+ }
+
private X509Certificate extractCertificate(HttpServletRequest req) {
X509Certificate[] certs = (X509Certificate[])
req.getAttribute("javax.servlet.request.X509Certificate");
if (null != certs && certs.length > 0) {
diff --git
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceMessages.java
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceMessages.java
index 14ef1dc..f2a65e5 100644
---
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceMessages.java
+++
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceMessages.java
@@ -71,6 +71,9 @@ public interface TokenServiceMessages {
@Message( level = MessageLevel.ERROR, text = "Knox Token service ({0})
rejected a bad revocation request for token {1}: {2}")
void badRevocationRequest(String topologyName, String tokenDisplayText,
String error);
+ @Message( level = MessageLevel.ERROR, text = "Knox Token service ({0})
rejected a bad set enabled flag request for token {1}: {2}")
+ void badSetEnabledFlagRequest(String topologyName, String tokenId, String
error);
+
@Message( level = MessageLevel.DEBUG, text = "Knox Token service ({0})
stored state for token {1} ({2})")
void storedToken(String topologyName, String tokenDisplayText, String
tokenId);
diff --git
a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenMetadata.java
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenMetadata.java
index f8a6bd3..97455a5 100644
---
a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenMetadata.java
+++
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/TokenMetadata.java
@@ -19,6 +19,7 @@ package org.apache.knox.gateway.services.security.token;
import java.util.HashMap;
import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
@@ -26,41 +27,65 @@ import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.knox.gateway.util.JsonUtils;
public class TokenMetadata {
- private static final String JSON_ELEMENT_USER_NAME = "userName";
- private static final String JSON_ELEMENT_COMMENT = "comment";
- private static final String EMPTY_COMMENT = "";
+ public static final String USER_NAME = "userName";
+ public static final String COMMENT = "comment";
+ public static final String ENABLED = "enabled";
- private final String userName;
- private final String comment;
+ private final Map<String, String> metadataMap = new HashMap<>();
public TokenMetadata(String userName) {
- this(userName, EMPTY_COMMENT);
+ this(userName, null);
}
public TokenMetadata(String userName, String comment) {
- this.userName = userName;
- this.comment = comment;
+ this(userName, comment, true);
+ }
+
+ public TokenMetadata(String userName, String comment, boolean enabled) {
+ saveMetadata(USER_NAME, userName);
+ saveMetadata(COMMENT, comment);
+ setEnabled(enabled);
+ }
+
+ private void saveMetadata(String key, String value) {
+ if (StringUtils.isNotBlank(value)) {
+ this.metadataMap.put(key, value);
+ }
+ }
+
+ public TokenMetadata(Map<String, String> metadataMap) {
+ this.metadataMap.clear();
+ this.metadataMap.putAll(metadataMap);
+ }
+
+ public Map<String, String> getMetadataMap() {
+ return new HashMap<String, String>(this.metadataMap);
}
public String getUserName() {
- return userName;
+ return metadataMap.get(USER_NAME);
}
public String getComment() {
- return comment;
+ return metadataMap.get(COMMENT);
+ }
+
+ public void setEnabled(boolean enabled) {
+ saveMetadata(ENABLED, String.valueOf(enabled));
+ }
+
+ public boolean isEnabled() {
+ return Boolean.parseBoolean(metadataMap.get(ENABLED));
}
public String toJSON() {
- final Map<String, String> metadataMap = new HashMap<>();
- metadataMap.put(JSON_ELEMENT_USER_NAME, getUserName());
- metadataMap.put(JSON_ELEMENT_COMMENT, getComment() == null ? EMPTY_COMMENT
: getComment());
return JsonUtils.renderAsJsonString(metadataMap);
}
public static TokenMetadata fromJSON(String json) {
final Map<String, String> metadataMap =
JsonUtils.getMapFromJsonString(json);
if (metadataMap != null) {
- return new TokenMetadata(metadataMap.get(JSON_ELEMENT_USER_NAME),
metadataMap.get(JSON_ELEMENT_COMMENT));
+ return new TokenMetadata(metadataMap);
}
throw new IllegalArgumentException("Invalid metadata JSON: " + json);
}