Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ public void shouldNotAuthenticateForUsingOBOTokenToAccessOBOEndpoint() {

try (TestRestClient client = cluster.getRestClient(adminOboAuthHeader)) {
TestRestClient.HttpResponse response = client.postJson(CREATE_OBO_TOKEN_PATH, OBO_DESCRIPTION);
response.assertStatusCode(HttpStatus.SC_UNAUTHORIZED);
response.assertStatusCode(HttpStatus.SC_FORBIDDEN);
}
}

Expand All @@ -178,7 +178,7 @@ public void shouldNotAuthenticateForUsingOBOTokenToAccessAccountEndpoint() {

try (TestRestClient client = cluster.getRestClient(adminOboAuthHeader)) {
TestRestClient.HttpResponse response = client.putJson("_plugins/_security/api/account", CURRENT_AND_NEW_PASSWORDS);
response.assertStatusCode(HttpStatus.SC_UNAUTHORIZED);
response.assertStatusCode(HttpStatus.SC_FORBIDDEN);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import org.opensearch.rest.RestChannel;
import org.opensearch.rest.RestRequest;
import org.opensearch.security.identity.SecurityTokenManager;
import org.opensearch.security.support.ConfigConstants;
import org.opensearch.security.user.User;
import org.opensearch.transport.client.node.NodeClient;

import static org.opensearch.rest.RestRequest.Method.POST;
Expand Down Expand Up @@ -84,6 +86,14 @@ public void accept(final RestChannel channel) throws Exception {
final XContentBuilder builder = channel.newBuilder();
BytesRestResponse response;
try {
final User user = (User) client.threadPool().getThreadContext().getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER);
if (user != null && isTokenAuthenticatedUser(user)) {
channel.sendResponse(
new BytesRestResponse(RestStatus.FORBIDDEN, "Token-authenticated users cannot create new tokens")
);
return;
}

if (!securityTokenManager.issueOnBehalfOfTokenAllowed()) {
channel.sendResponse(
new BytesRestResponse(
Expand Down Expand Up @@ -163,4 +173,9 @@ private long parseAndValidateDurationSeconds(final Object durationObj) throws Il
}
throw new IllegalArgumentException("durationSeconds must be a number.");
}

private static boolean isTokenAuthenticatedUser(User user) {
String authBy = user.getAuthenticatedBy();
return "onbehalfof_jwt".equals(authBy);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,8 @@ Authenticate users injected in the thread context by internal components (plugin
Disallow superuser authentication through auth domain.
Only client cert authentication is allowed for this user.
*/
authenticatedUser.setAuthenticatedBy(authDomain.getHttpAuthenticator().getType());

if (adminDns.isAdmin(authenticatedUser)) {
log.error("Cannot authenticate user because admin user is not permitted to login via HTTP");
auditLog.logFailedLogin(authenticatedUser.getName(), true, null, request);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@
import com.nimbusds.jose.jwk.OctetSequenceKey;
import com.nimbusds.jwt.SignedJWT;

import static org.opensearch.security.util.AuthTokenUtils.isKeyNull;

public class JwtVendor {
private static final Logger logger = LogManager.getLogger(JwtVendor.class);

Expand All @@ -57,7 +55,7 @@ public JwtVendor(Settings settings) {
* */
static Tuple<JWK, JWSSigner> createJwkFromSettings(final Settings settings) {
final OctetSequenceKey key;
if (!isKeyNull(settings, "signing_key")) {
if (settings.get("signing_key") != null) {
final String signingKey = settings.get("signing_key");
key = new OctetSequenceKey.Builder(Base64.getDecoder().decode(signingKey)).algorithm(JWSAlgorithm.HS512)
.keyUse(KeyUse.SIGNATURE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.opensearch.threadpool.ThreadPool;

import static org.opensearch.security.dlic.rest.api.Responses.badRequestMessage;
import static org.opensearch.security.dlic.rest.api.Responses.forbiddenMessage;
import static org.opensearch.security.dlic.rest.api.Responses.ok;
import static org.opensearch.security.dlic.rest.api.Responses.response;
import static org.opensearch.security.dlic.rest.support.Utils.OPENDISTRO_API_DEPRECATION_MESSAGE;
Expand Down Expand Up @@ -110,16 +111,21 @@ private void accountApiRequestHandlers(RequestHandler.RequestHandlersBuilder req
userAccount(channel, user, remoteAddress, configuration);
}).error((status, toXContent) -> response(channel, status, toXContent))
)
.onChangeRequest(
Method.PUT,
request -> withUserAndRemoteAddress().map(
userAndRemoteAddress -> loadConfigurationWithRequestContent(userAndRemoteAddress.getLeft().getName(), request)
)
.map(endpointValidator::entityExists)
.map(endpointValidator::onConfigChange)
.map(this::validCurrentPassword)
.map(this::updatePassword)
);
.onChangeRequest(Method.PUT, request -> withUserAndRemoteAddress().map(userAndRemoteAddress -> {
final User user = userAndRemoteAddress.getLeft();
if ("onbehalfof_jwt".equals(user.getAuthenticatedBy())) {
return ValidationResult.error(
RestStatus.FORBIDDEN,
forbiddenMessage("Token-authenticated users cannot change passwords")
);
}
return ValidationResult.success(userAndRemoteAddress);
})
.map(userAndRemoteAddress -> loadConfigurationWithRequestContent(userAndRemoteAddress.getLeft().getName(), request))
.map(endpointValidator::entityExists)
.map(endpointValidator::onConfigChange)
.map(this::validCurrentPassword)
.map(this::updatePassword));
}

private void userAccount(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,13 @@
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.hc.core5.http.HttpHeaders;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import org.opensearch.OpenSearchException;
import org.opensearch.OpenSearchSecurityException;
import org.opensearch.common.settings.Settings;
import org.opensearch.common.util.concurrent.ThreadContext;
Expand All @@ -35,7 +33,6 @@
import org.opensearch.security.authtoken.jwt.EncryptionDecryptionUtil;
import org.opensearch.security.filter.SecurityRequest;
import org.opensearch.security.filter.SecurityResponse;
import org.opensearch.security.ssl.util.ExceptionUtils;
import org.opensearch.security.user.AuthCredentials;
import org.opensearch.security.util.KeyUtils;

Expand All @@ -44,15 +41,9 @@
import io.jsonwebtoken.JwtParserBuilder;
import io.jsonwebtoken.security.WeakKeyException;

import static org.opensearch.security.OpenSearchSecurityPlugin.LEGACY_OPENDISTRO_PREFIX;
import static org.opensearch.security.OpenSearchSecurityPlugin.PLUGINS_PREFIX;
import static org.opensearch.security.util.AuthTokenUtils.isAccessToRestrictedEndpoints;

public class OnBehalfOfAuthenticator implements HTTPAuthenticator {

private static final int MINIMUM_SIGNING_KEY_BIT_LENGTH = 512;
private static final String REGEX_PATH_PREFIX = "/(" + LEGACY_OPENDISTRO_PREFIX + "|" + PLUGINS_PREFIX + ")/" + "(.*)";
private static final Pattern PATTERN_PATH_PREFIX = Pattern.compile(REGEX_PATH_PREFIX);

protected final Logger log = LogManager.getLogger(this.getClass());

Expand Down Expand Up @@ -161,10 +152,6 @@ private AuthCredentials extractCredentials0(final SecurityRequest request) {
return null;
}

if (!isRequestAllowed(request)) {
return null;
}

try {
final Claims claims = jwtParser.parseClaimsJws(jwtToken).getBody();

Expand Down Expand Up @@ -249,17 +236,6 @@ private void logDebug(String message, Object... args) {
}
}

public Boolean isRequestAllowed(final SecurityRequest request) {
Matcher matcher = PATTERN_PATH_PREFIX.matcher(request.path());
final String suffix = matcher.matches() ? matcher.group(2) : null;
if (isAccessToRestrictedEndpoints(request, suffix)) {
final OpenSearchException exception = ExceptionUtils.invalidUsageOfOBOTokenException();
log.error(exception.toString());
return false;
}
return true;
}

@Override
public Optional<SecurityResponse> reRequestAuthentication(final SecurityRequest response, AuthCredentials creds) {
return Optional.empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@
import org.opensearch.security.securityconf.impl.v7.ConfigV7.AuthzDomain;
import org.opensearch.security.support.ReflectionHelper;

import static org.opensearch.security.util.AuthTokenUtils.isKeyNull;

public class DynamicConfigModelV7 extends DynamicConfigModel {

private final ConfigV7 config;
Expand Down Expand Up @@ -370,7 +368,7 @@ private void buildAAA() {
* order: -1 - prioritize the OBO authentication when it gets enabled
*/
Settings oboSettings = getDynamicOnBehalfOfSettings();
if (!isKeyNull(oboSettings, "signing_key")) {
if (oboSettings.get("signing_key") != null) {
final AuthDomain _ad = new AuthDomain(
new NoOpAuthenticationBackend(Settings.EMPTY, null),
new OnBehalfOfAuthenticator(getDynamicOnBehalfOfSettings(), this.cih.getClusterName()),
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/org/opensearch/security/user/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ public static User fromSerializedBase64(String serializedBase64) {
*/
private volatile transient String serializedBase64;

/**
* The type of authenticator that authenticated this user (e.g., "basic", "obo", "apitoken").
* Transient — not serialized, only used for in-process authorization decisions.
*/
private transient String authenticatedBy;

/**
* Create a new authenticated user without roles and attributes
*
Expand Down Expand Up @@ -325,6 +331,14 @@ public String getPluginName() {
return null;
}

public String getAuthenticatedBy() {
return authenticatedBy;
}

public void setAuthenticatedBy(String authenticatedBy) {
this.authenticatedBy = authenticatedBy;
}

/**
* Returns a String containing serialized form of this User object. Never returns null.
*/
Expand Down
43 changes: 0 additions & 43 deletions src/main/java/org/opensearch/security/util/AuthTokenUtils.java

This file was deleted.

This file was deleted.

Loading
Loading