diff --git a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/AuthTokenHandler.java b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/AuthTokenHandler.java deleted file mode 100644 index 14801b665f14f..0000000000000 --- a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/AuthTokenHandler.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -package org.opensearch.identity.shiro; - -import java.util.Optional; - -import org.apache.shiro.authc.AuthenticationToken; -import org.apache.shiro.authc.UsernamePasswordToken; -import org.opensearch.identity.tokens.BasicAuthToken; - -/** - * Extracts Shiro's {@link AuthenticationToken} from different types of auth headers - * - * @opensearch.experimental - */ -class AuthTokenHandler { - - /** - * Translates into shiro auth token from the given header token - * @param authenticationToken the token from which to translate - * @return An optional of the shiro auth token for login - */ - public Optional translateAuthToken(org.opensearch.identity.tokens.AuthToken authenticationToken) { - if (authenticationToken instanceof BasicAuthToken) { - final BasicAuthToken basicAuthToken = (BasicAuthToken) authenticationToken; - return Optional.of(new UsernamePasswordToken(basicAuthToken.getUser(), basicAuthToken.getPassword())); - } - - return Optional.empty(); - } -} diff --git a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroIdentityPlugin.java b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroIdentityPlugin.java index eee5dd8ce0bd4..3cfc2fffe9f48 100644 --- a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroIdentityPlugin.java +++ b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroIdentityPlugin.java @@ -26,7 +26,7 @@ public final class ShiroIdentityPlugin extends Plugin implements IdentityPlugin private Logger log = LogManager.getLogger(this.getClass()); private final Settings settings; - private final AuthTokenHandler authTokenHandler; + private final ShiroTokenHandler authTokenHandler; /** * Create a new instance of the Shiro Identity Plugin @@ -35,7 +35,7 @@ public final class ShiroIdentityPlugin extends Plugin implements IdentityPlugin */ public ShiroIdentityPlugin(final Settings settings) { this.settings = settings; - authTokenHandler = new AuthTokenHandler(); + authTokenHandler = new ShiroTokenHandler(); SecurityManager securityManager = new ShiroSecurityManager(); SecurityUtils.setSecurityManager(securityManager); diff --git a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroSubject.java b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroSubject.java index 3208d2bb5d8ca..414e2aa921d29 100644 --- a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroSubject.java +++ b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroSubject.java @@ -20,7 +20,7 @@ * @opensearch.experimental */ public class ShiroSubject implements Subject { - private final AuthTokenHandler authTokenHandler; + private final ShiroTokenHandler authTokenHandler; private final org.apache.shiro.subject.Subject shiroSubject; /** @@ -29,7 +29,7 @@ public class ShiroSubject implements Subject { * @param authTokenHandler Used to extract auth header info * @param subject The specific subject being authc/z'd */ - public ShiroSubject(final AuthTokenHandler authTokenHandler, final org.apache.shiro.subject.Subject subject) { + public ShiroSubject(final ShiroTokenHandler authTokenHandler, final org.apache.shiro.subject.Subject subject) { this.authTokenHandler = Objects.requireNonNull(authTokenHandler); this.shiroSubject = Objects.requireNonNull(subject); } diff --git a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroTokenHandler.java b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroTokenHandler.java new file mode 100644 index 0000000000000..95fdad904eca2 --- /dev/null +++ b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroTokenHandler.java @@ -0,0 +1,94 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.identity.shiro; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Optional; + +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.opensearch.identity.Subject; +import org.opensearch.identity.tokens.AuthToken; +import org.opensearch.identity.tokens.BasicAuthToken; +import org.opensearch.identity.tokens.TokenManager; + +/** + * Extracts Shiro's {@link AuthenticationToken} from different types of auth headers + * + * @opensearch.experimental + */ +class ShiroTokenHandler implements TokenManager { + + /** + * Translates into shiro auth token from the given header token + * @param authenticationToken the token from which to translate + * @return An optional of the shiro auth token for login + */ + public Optional translateAuthToken(org.opensearch.identity.tokens.AuthToken authenticationToken) { + if (authenticationToken instanceof BasicAuthToken) { + final BasicAuthToken basicAuthToken = (BasicAuthToken) authenticationToken; + return Optional.of(new UsernamePasswordToken(basicAuthToken.getUser(), basicAuthToken.getPassword())); + } + + return Optional.empty(); + } + + @Override + public AuthToken generateToken() { + + Subject subject = new ShiroSubject(this, SecurityUtils.getSubject()); + final byte[] rawEncoded = Base64.getEncoder().encode((subject.getPrincipal().getName() + ":" + generatePassword()).getBytes()); + final String usernamePassword = new String(rawEncoded, StandardCharsets.UTF_8); + final String header = "Basic " + usernamePassword; + + return new BasicAuthToken(header); + } + + @Override + public boolean validateToken(AuthToken token) { + if (token instanceof BasicAuthToken) { + final BasicAuthToken basicAuthToken = (BasicAuthToken) token; + if (basicAuthToken.getUser().equals(SecurityUtils.getSubject()) && basicAuthToken.getPassword().equals(generatePassword())) { + return true; + } + } + return false; + } + + @Override + public String getTokenInfo(AuthToken token) { + if (token instanceof BasicAuthToken) { + final BasicAuthToken basicAuthToken = (BasicAuthToken) token; + return basicAuthToken.toString(); + } + throw new UnsupportedAuthenticationToken(); + } + + @Override + public void revokeToken(AuthToken token) { + if (token instanceof BasicAuthToken) { + final BasicAuthToken basicAuthToken = (BasicAuthToken) token; + basicAuthToken.revoke(); + return; + } + throw new UnsupportedAuthenticationToken(); + } + + @Override + public void refreshToken(AuthToken token) { + + } + + public String generatePassword() { + return "superSecurePassword1!"; + } + +} diff --git a/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/AuthTokenHandlerTests.java b/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/AuthTokenHandlerTests.java index 942d777df2086..0bc198ce45ce6 100644 --- a/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/AuthTokenHandlerTests.java +++ b/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/AuthTokenHandlerTests.java @@ -10,7 +10,9 @@ import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.UsernamePasswordToken; +import org.opensearch.identity.tokens.AuthToken; import org.opensearch.identity.tokens.BasicAuthToken; +import org.opensearch.identity.tokens.NoopToken; import org.opensearch.test.OpenSearchTestCase; import org.junit.Before; @@ -23,11 +25,11 @@ public class AuthTokenHandlerTests extends OpenSearchTestCase { - private AuthTokenHandler authTokenHandler; + private ShiroTokenHandler authTokenHandler; @Before public void testSetup() { - authTokenHandler = new AuthTokenHandler(); + authTokenHandler = new ShiroTokenHandler(); } public void testShouldExtractBasicAuthTokenSuccessfully() { @@ -59,4 +61,34 @@ public void testShouldReturnNullWhenExtractingNullToken() { assertThat(translatedToken.isEmpty(), is(true)); } + + public void testShouldRevokeTokenSuccessfully() { + final BasicAuthToken authToken = new BasicAuthToken("Basic dGVzdDp0ZTpzdA=="); + assertTrue(authToken.toString().equals("Basic auth token with user=test, password=te:st")); + authTokenHandler.revokeToken(authToken); + assert(authToken.toString().equals("Basic auth token with user=, password=")); + } + + public void testShouldFailWhenRevokeToken() { + final NoopToken authToken = new NoopToken(); + assert(authToken.getTokenIdentifier().equals("Noop")); + assertThrows(UnsupportedAuthenticationToken.class, () -> authTokenHandler.revokeToken(authToken)); + } + + public void testShouldGetTokenInfoSuccessfully() { + final BasicAuthToken authToken = new BasicAuthToken("Basic dGVzdDp0ZTpzdA=="); + assert(authToken.toString().equals(authTokenHandler.getTokenInfo(authToken))); + } + + public void testShouldFailGetTokenInfo() { + final NoopToken authToken = new NoopToken(); + assert(authToken.getTokenIdentifier().equals("Noop")); + assertThrows(UnsupportedAuthenticationToken.class, () -> authTokenHandler.getTokenInfo(authToken)); + } + + + public void testShouldFailValidateToken() { + final AuthToken authToken = new NoopToken(); + assertFalse(authTokenHandler.validateToken(authToken)); + } } diff --git a/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/ShiroSubjectTests.java b/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/ShiroSubjectTests.java index baf6090f79ff3..311df738108c6 100644 --- a/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/ShiroSubjectTests.java +++ b/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/ShiroSubjectTests.java @@ -25,13 +25,13 @@ public class ShiroSubjectTests extends OpenSearchTestCase { private org.apache.shiro.subject.Subject shiroSubject; - private AuthTokenHandler authTokenHandler; + private ShiroTokenHandler authTokenHandler; private ShiroSubject subject; @Before public void setup() { shiroSubject = mock(org.apache.shiro.subject.Subject.class); - authTokenHandler = mock(AuthTokenHandler.class); + authTokenHandler = mock(ShiroTokenHandler.class); subject = new ShiroSubject(authTokenHandler, shiroSubject); } diff --git a/server/src/main/java/org/opensearch/identity/noop/NoopTokenManager.java b/server/src/main/java/org/opensearch/identity/noop/NoopTokenManager.java new file mode 100644 index 0000000000000..f8bb009a30175 --- /dev/null +++ b/server/src/main/java/org/opensearch/identity/noop/NoopTokenManager.java @@ -0,0 +1,43 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.identity.noop; + +import org.opensearch.identity.tokens.AuthToken; +import org.opensearch.identity.tokens.NoopToken; +import org.opensearch.identity.tokens.TokenManager; + +public class NoopTokenManager implements TokenManager { + @Override + public AuthToken generateToken() { + return new NoopToken(); + } + + @Override + public boolean validateToken(AuthToken token) { + if (token instanceof NoopToken){ + return true; + } + return false; + } + + @Override + public String getTokenInfo(AuthToken token) { + return "Token is NoopToken"; + } + + @Override + public void revokeToken(AuthToken token) { + + } + + @Override + public void refreshToken(AuthToken token) { + + } +} diff --git a/server/src/main/java/org/opensearch/identity/tokens/BasicAuthToken.java b/server/src/main/java/org/opensearch/identity/tokens/BasicAuthToken.java index e5000cb3d6965..59db713bb4f0e 100644 --- a/server/src/main/java/org/opensearch/identity/tokens/BasicAuthToken.java +++ b/server/src/main/java/org/opensearch/identity/tokens/BasicAuthToken.java @@ -16,13 +16,13 @@ */ public final class BasicAuthToken implements AuthToken { - public final static String TOKEN_IDENIFIER = "Basic"; + public final static String TOKEN_IDENTIFIER = "Basic"; private String user; private String password; public BasicAuthToken(final String headerValue) { - final String base64Encoded = headerValue.substring(TOKEN_IDENIFIER.length()).trim(); + final String base64Encoded = headerValue.substring(TOKEN_IDENTIFIER.length()).trim(); final byte[] rawDecoded = Base64.getDecoder().decode(base64Encoded); final String usernamepassword = new String(rawDecoded, StandardCharsets.UTF_8); @@ -41,4 +41,14 @@ public String getUser() { public String getPassword() { return password; } + + @Override + public String toString(){ + return "Basic auth token with user=" + user + ", password=" + password; + } + + public void revoke() { + this.password=""; + this.user=""; + } } diff --git a/server/src/main/java/org/opensearch/identity/tokens/NoopToken.java b/server/src/main/java/org/opensearch/identity/tokens/NoopToken.java new file mode 100644 index 0000000000000..c566d350c1446 --- /dev/null +++ b/server/src/main/java/org/opensearch/identity/tokens/NoopToken.java @@ -0,0 +1,18 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.identity.tokens; + +public class NoopToken implements AuthToken { + public final static String TOKEN_IDENTIFIER = "Noop"; + + public String getTokenIdentifier() { + return TOKEN_IDENTIFIER; + } + +} diff --git a/server/src/main/java/org/opensearch/identity/tokens/RestTokenExtractor.java b/server/src/main/java/org/opensearch/identity/tokens/RestTokenExtractor.java index eaeacdb240fd9..ae200c7461a60 100644 --- a/server/src/main/java/org/opensearch/identity/tokens/RestTokenExtractor.java +++ b/server/src/main/java/org/opensearch/identity/tokens/RestTokenExtractor.java @@ -40,7 +40,7 @@ public static AuthToken extractToken(final RestRequest request) { if (authHeaderValue.isPresent()) { final String authHeaderValueStr = authHeaderValue.get(); - if (authHeaderValueStr.startsWith(BasicAuthToken.TOKEN_IDENIFIER)) { + if (authHeaderValueStr.startsWith(BasicAuthToken.TOKEN_IDENTIFIER)) { return new BasicAuthToken(authHeaderValueStr); } else { if (logger.isDebugEnabled()) { diff --git a/server/src/main/java/org/opensearch/identity/tokens/TokenManager.java b/server/src/main/java/org/opensearch/identity/tokens/TokenManager.java new file mode 100644 index 0000000000000..00cf7adf12737 --- /dev/null +++ b/server/src/main/java/org/opensearch/identity/tokens/TokenManager.java @@ -0,0 +1,22 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.identity.tokens; + +public interface TokenManager { + + public AuthToken generateToken(); + + public boolean validateToken(AuthToken token); + + public String getTokenInfo(AuthToken token); + + public void revokeToken(AuthToken token); + + public void refreshToken(AuthToken token); +}