Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TokenManager Interface #7452

Merged
merged 31 commits into from
Jun 6, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a73d42f
TokenManager Interface
stephen-crawford May 5, 2023
6cd0353
Update javadocs
stephen-crawford May 8, 2023
53f58a3
Merge branch 'opensearch-project:main' into tokenManager
stephen-crawford May 15, 2023
ba38c92
Update changelog
stephen-crawford May 15, 2023
c2d4129
Merge branch 'main' into tokenManager
stephen-crawford May 16, 2023
3f2d228
Update code coverage
stephen-crawford May 16, 2023
1d05fc2
add getter
stephen-crawford May 16, 2023
54ef048
Swap to issue word
stephen-crawford May 16, 2023
19e51cb
Merge branch 'main' into tokenManager
stephen-crawford May 16, 2023
fb7f8aa
Increase coverage
stephen-crawford May 18, 2023
b86b24e
Spotless
stephen-crawford May 19, 2023
d90e8e1
Create bearer auth token
stephen-crawford May 19, 2023
f8647d6
java doc
stephen-crawford May 19, 2023
3fd358f
Add full token string
stephen-crawford May 19, 2023
b05964f
Add coverage for bearer token type
stephen-crawford May 19, 2023
17e5a44
spotless
stephen-crawford May 22, 2023
8102b23
Merge branch 'opensearch-project:main' into tokenManager
stephen-crawford May 23, 2023
29042b5
Merge branch 'main' into tokenManager
stephen-crawford May 24, 2023
0fc832e
Merge branch 'main' into tokenManager
stephen-crawford May 30, 2023
1e9afe3
Implement Password generation for development
stephen-crawford May 30, 2023
ff876ab
fix audit
stephen-crawford May 30, 2023
d353f62
Update plugins/identity-shiro/src/main/java/org/opensearch/identity/s…
stephen-crawford May 30, 2023
2b1a1fe
Update server/src/main/java/org/opensearch/identity/tokens/BearerAuth…
stephen-crawford May 30, 2023
1bb88a6
Merge branch 'opensearch-project:main' into tokenManager
stephen-crawford May 30, 2023
57acaca
spotless
stephen-crawford May 30, 2023
323d6f6
Swap exception type
stephen-crawford May 30, 2023
510ce2e
Merge branch 'main' into tokenManager
stephen-crawford May 31, 2023
1151c49
Merge branch 'opensearch-project:main' into tokenManager
stephen-crawford Jun 1, 2023
4e48c62
Fix changelog
stephen-crawford Jun 1, 2023
e156bfe
Update token manager
stephen-crawford Jun 6, 2023
fedcfd1
Merge branch 'opensearch-project:main' into tokenManager
stephen-crawford Jun 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- Add descending order search optimization through reverse segment read. ([#7244](https://github.com/opensearch-project/OpenSearch/pull/7244))
- Adds ExtensionsManager.lookupExtensionSettingsById ([#7466](https://github.com/opensearch-project/OpenSearch/pull/7466))
- SegRep with Remote: Add hook for publishing checkpoint notifications after segment upload to remote store ([#7394](https://github.com/opensearch-project/OpenSearch/pull/7394))
- Add TokenManager Interface ([#7452](https://github.com/opensearch-project/OpenSearch/pull/7452))

### Dependencies
- Bump `com.netflix.nebula:gradle-info-plugin` from 12.0.0 to 12.1.3 (#7564)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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;
stephen-crawford marked this conversation as resolved.
Show resolved Hide resolved

/**
* Create a new instance of the Shiro Identity Plugin
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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.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;
import static java.nio.charset.StandardCharsets.UTF_8;

/**
* Extracts Shiro's {@link AuthenticationToken} from different types of auth headers
*
* @opensearch.experimental
*/
class ShiroTokenHandler implements TokenManager {
stephen-crawford marked this conversation as resolved.
Show resolved Hide resolved

/**
* 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<AuthenticationToken> 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(UTF_8));
final String usernamePassword = new String(rawEncoded, 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 resetToken(AuthToken token) {

}

public String generatePassword() {
peternied marked this conversation as resolved.
Show resolved Hide resolved
return "superSecurePassword1!";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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() {
Expand Down Expand Up @@ -59,4 +61,33 @@ 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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* 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;

/**
* This class represents a Noop Token Manager
*/
public class NoopTokenManager implements TokenManager {

/**
* Generate a new Noop Token
* @return a new Noop Token
*/
@Override
public AuthToken generateToken() {
return new NoopToken();
}

/**
* Validate a token
* @param token The token to be validated
* @return If the token is a Noop Token, then pass with True; otherwise fail with False.
*/
@Override
public boolean validateToken(AuthToken token) {
if (token instanceof NoopToken) {
return true;
}
return false;
}

/**
* Get token info, there should not be any token info so just return whether the token is a NoopToken
* @param token The auth token to be parsed
* @return A String stating the token is a NoopToken or is not a NopToken
*/
@Override
public String getTokenInfo(AuthToken token) {
if (token instanceof NoopToken) {
return "Token is NoopToken";
}
return "Token is not a NoopToken";
}

/**
* Revoking a Noop Token should not do anything
* @param token The Auth Token to be revoked
*/
@Override
public void revokeToken(AuthToken token) {

}

/**
* Refreshing a NoopToken also not do anything
* @param token The token to be refreshed
*/
@Override
public void resetToken(AuthToken token) {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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 = "";
}
}
25 changes: 25 additions & 0 deletions server/src/main/java/org/opensearch/identity/tokens/NoopToken.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* 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;

/**
* A NoopToken is a pass-through AuthToken
*/
public class NoopToken implements AuthToken {
public final static String TOKEN_IDENTIFIER = "Noop";

/**
* Returns the TokenIdentifier of Noop
* @return The token identifier "Noop"
*/
public String getTokenIdentifier() {
return TOKEN_IDENTIFIER;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
Loading