-
Notifications
You must be signed in to change notification settings - Fork 276
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
[Feature/Extension] Extension token handler #3034
Closed
RyanL1997
wants to merge
6
commits into
opensearch-project:feature/extensions
from
RyanL1997:ext-token-handler
Closed
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
09b51fd
Implements token handler
RyanL1997 f6f368e
Fix the NoSuchAlgo Exception
RyanL1997 b935c32
Add issueOBOToken in the Security Token Manager
RyanL1997 1be2551
Add test cases and modify the checks of obo validation
RyanL1997 69dfb67
Add license header for security token manager tests
RyanL1997 03a7875
Add library fix
RyanL1997 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,7 @@ | |
|
||
import java.io.IOException; | ||
import java.nio.file.Path; | ||
import java.security.NoSuchAlgorithmException; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
|
@@ -159,6 +160,8 @@ protected void handlePut(RestChannel channel, final RestRequest request, final C | |
return; | ||
} catch (IOException ex) { | ||
throw new IOException(ex); | ||
} catch (NoSuchAlgorithmException e) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why add this catch block to then throw RuntimeException? |
||
throw new RuntimeException(e); | ||
} | ||
|
||
// for existing users, hash is optional | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
221 changes: 221 additions & 0 deletions
221
src/main/java/org/opensearch/security/identity/SecurityTokenManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
/* | ||
* 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. | ||
* | ||
* Modifications Copyright OpenSearch Contributors. See | ||
* GitHub history for details. | ||
*/ | ||
|
||
package org.opensearch.security.identity; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.Set; | ||
|
||
import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; | ||
import org.apache.cxf.rs.security.jose.jwt.JwtConstants; | ||
import org.apache.cxf.rs.security.jose.jwt.JwtToken; | ||
import org.greenrobot.eventbus.Subscribe; | ||
import org.opensearch.OpenSearchSecurityException; | ||
import org.opensearch.client.Client; | ||
import org.opensearch.cluster.service.ClusterService; | ||
import org.opensearch.common.inject.Inject; | ||
import org.opensearch.common.settings.Settings; | ||
import org.opensearch.core.common.transport.TransportAddress; | ||
import org.opensearch.identity.tokens.AuthToken; | ||
import org.opensearch.identity.tokens.BasicAuthToken; | ||
import org.opensearch.identity.tokens.BearerAuthToken; | ||
import org.opensearch.identity.tokens.TokenManager; | ||
import org.opensearch.security.authtoken.jwt.JwtVendor; | ||
import org.opensearch.security.configuration.ConfigurationRepository; | ||
import org.opensearch.security.securityconf.ConfigModel; | ||
import org.opensearch.security.securityconf.DynamicConfigFactory; | ||
import org.opensearch.security.securityconf.DynamicConfigModel; | ||
import org.opensearch.security.securityconf.impl.CType; | ||
import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; | ||
import org.opensearch.security.support.ConfigConstants; | ||
import org.opensearch.security.user.InternalUserTokenHandler; | ||
import org.opensearch.security.user.User; | ||
import org.opensearch.security.user.UserService; | ||
import org.opensearch.security.user.UserServiceException; | ||
import org.opensearch.security.user.UserTokenHandler; | ||
import org.opensearch.threadpool.ThreadPool; | ||
import org.apache.cxf.rs.security.jose.jwt.JwtConstants; | ||
|
||
/** | ||
* This class serves as a funneling implementation of the TokenManager interface. | ||
* The class allows the Security Plugin to implement two separate types of token managers without requiring specific interfaces | ||
* in the IdentityPlugin. | ||
*/ | ||
public class SecurityTokenManager implements TokenManager { | ||
|
||
Settings settings; | ||
|
||
ThreadPool threadPool; | ||
|
||
ClusterService clusterService; | ||
Client client; | ||
ConfigurationRepository configurationRepository; | ||
UserService userService; | ||
UserTokenHandler userTokenHandler; | ||
InternalUserTokenHandler internalUserTokenHandler; | ||
|
||
public final String TOKEN_NOT_SUPPORTED_MESSAGE = "The provided token type is not supported by the Security Plugin."; | ||
private ConfigModel configModel; | ||
private DynamicConfigModel dcm; | ||
private JwtVendor vendor; | ||
|
||
@Inject | ||
public SecurityTokenManager( | ||
ThreadPool threadPool, | ||
ClusterService clusterService, | ||
ConfigurationRepository configurationRepository, | ||
Client client, | ||
Settings settings, | ||
UserService userService | ||
) { | ||
this.threadPool = threadPool; | ||
this.clusterService = clusterService; | ||
this.client = client; | ||
this.configurationRepository = configurationRepository; | ||
this.settings = settings; | ||
this.userService = userService; | ||
userTokenHandler = new UserTokenHandler(threadPool, clusterService, configurationRepository, client); | ||
internalUserTokenHandler = new InternalUserTokenHandler(settings, userService); | ||
|
||
} | ||
|
||
@Override | ||
public AuthToken issueOnBehalfOfToken(Map<String, Object> claims) { | ||
String oboToken; | ||
|
||
User user = threadPool.getThreadContext().getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); | ||
if (user == null && !claims.containsKey(JwtConstants.CLAIM_AUDIENCE)) { | ||
throw new OpenSearchSecurityException("On-behalf-of Token cannot be issued due to the missing of subject/audience."); | ||
} | ||
|
||
final TransportAddress caller = threadPool.getThreadContext().getTransient(ConfigConstants.OPENDISTRO_SECURITY_REMOTE_ADDRESS); | ||
|
||
String issuer = clusterService.getClusterName().value(); | ||
String subject = user.getName(); | ||
String audience = (String) claims.get(JwtConstants.CLAIM_AUDIENCE); | ||
Integer expirySeconds = null; | ||
List<String> roles = new ArrayList<>(mapRoles(user, caller)); | ||
List<String> backendRoles = new ArrayList<>(user.getRoles()); | ||
|
||
try { | ||
oboToken = vendor.createJwt(issuer, subject, audience, expirySeconds, roles, backendRoles); | ||
} catch (Exception e) { | ||
throw new RuntimeException(e); | ||
} | ||
|
||
return new BearerAuthToken(oboToken); | ||
} | ||
|
||
// TODO: IMPLEMENT ISSUE SERVICE ACCOUNT INTERFACE FIRST | ||
@Override | ||
public AuthToken issueServiceAccountToken(String pluginOrExtensionPrincipal) { | ||
return null; | ||
} | ||
|
||
public boolean validateToken(AuthToken authToken) { | ||
|
||
if (authToken instanceof BearerAuthToken) { | ||
String encodedTokenString = authToken.getTokenValue(); | ||
JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(encodedTokenString); | ||
JwtToken jwt = jwtConsumer.getJwtToken(); | ||
if ("obo".equals(jwt.getClaim("typ"))) { | ||
return userTokenHandler.validateJustInTimeToken(authToken); | ||
} | ||
return userTokenHandler.validateToken(authToken); | ||
} | ||
|
||
if (authToken instanceof BasicAuthToken) { | ||
return internalUserTokenHandler.validateToken(authToken); | ||
} | ||
throw new UserServiceException(TOKEN_NOT_SUPPORTED_MESSAGE); | ||
} | ||
|
||
public String getTokenInfo(AuthToken authToken) { | ||
|
||
if (authToken instanceof BearerAuthToken) { | ||
return userTokenHandler.getTokenInfo(authToken); | ||
} | ||
if (authToken instanceof BasicAuthToken) { | ||
return internalUserTokenHandler.getTokenInfo(authToken); | ||
} | ||
throw new UserServiceException(TOKEN_NOT_SUPPORTED_MESSAGE); | ||
} | ||
|
||
public void revokeToken(AuthToken authToken) { | ||
if (authToken instanceof BearerAuthToken) { | ||
userTokenHandler.revokeToken(authToken); | ||
return; | ||
} | ||
if (authToken instanceof BasicAuthToken) { | ||
internalUserTokenHandler.revokeToken(authToken); | ||
return; | ||
} | ||
throw new UserServiceException(TOKEN_NOT_SUPPORTED_MESSAGE); | ||
} | ||
|
||
/** | ||
* Only for testing | ||
*/ | ||
public void setJwtVendor(JwtVendor vendor) { | ||
this.vendor = vendor; | ||
} | ||
|
||
/** | ||
* Only for testing | ||
*/ | ||
public void setInternalUserTokenHandler(InternalUserTokenHandler handler) { | ||
this.internalUserTokenHandler = handler; | ||
} | ||
|
||
/** | ||
* Only for testing | ||
*/ | ||
public void setUserTokenHandler(UserTokenHandler handler) { | ||
this.userTokenHandler = handler; | ||
} | ||
|
||
/** | ||
* Load data for a given CType | ||
* @param config CType whose data is to be loaded in-memory | ||
* @return configuration loaded with given CType data | ||
*/ | ||
protected final SecurityDynamicConfiguration<?> load(final CType config, boolean logComplianceEvent) { | ||
SecurityDynamicConfiguration<?> loaded = configurationRepository.getConfigurationsFromIndex( | ||
Collections.singleton(config), | ||
logComplianceEvent | ||
).get(config).deepClone(); | ||
return DynamicConfigFactory.addStatics(loaded); | ||
} | ||
|
||
public Set<String> mapRoles(final User user, final TransportAddress caller) { | ||
return this.configModel.mapSecurityRoles(user, caller); | ||
} | ||
|
||
@Subscribe | ||
public void onConfigModelChanged(ConfigModel configModel) { | ||
this.configModel = configModel; | ||
} | ||
|
||
@Subscribe | ||
public void onDynamicConfigModelChanged(DynamicConfigModel dcm) { | ||
this.dcm = dcm; | ||
if (dcm.getDynamicOnBehalfOfSettings().get("signing_key") != null | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should extract: dcm.getDynamicOnBehalfOfSettings() to a variable |
||
&& dcm.getDynamicOnBehalfOfSettings().get("encryption_key") != null) { | ||
this.vendor = new JwtVendor(dcm.getDynamicOnBehalfOfSettings(), Optional.empty()); | ||
} else { | ||
this.vendor = null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be removed. Check this PR for more details: https://github.com/opensearch-project/security/pull/3066/files