Skip to content

Commit

Permalink
[ELY-2583] Make requestURI and Source-Address available from RealmSuc…
Browse files Browse the repository at this point in the history
…cessfulAuthenticationEvent and RealmFailedAuthenticationEvent
  • Loading branch information
Skyllarr committed Nov 20, 2023
1 parent 3dc896a commit 9f0768b
Show file tree
Hide file tree
Showing 5 changed files with 425 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2023 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wildfly.security.auth.server;

import org.wildfly.security.auth.callback.ExtendedCallback;

import javax.security.auth.callback.Callback;
import java.net.URI;

/**
* A {@link javax.security.auth.callback.Callback} to inform a server authentication context about current authentication request.
*
*/
public class RequestInformationCallback implements ExtendedCallback {

/**
* request URI
*/
private final URI requestUri;

/**
* Construct a new instance of this {@link Callback}.
*
* @param requestUri URI of the current authentication request
*/
public RequestInformationCallback(URI requestUri) {
this.requestUri = requestUri;
}

/**
* Get the URI of this request.
*
* @return the URI of the current authentication request
*/
public URI getRequestUri() {
return this.requestUri;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@
*/
public final class ServerAuthenticationContext implements AutoCloseable {

public static final String REQUEST_URI = "Request-URI";
private final AtomicReference<State> stateRef;

ServerAuthenticationContext(final SecurityDomain domain, final MechanismConfigurationSelector mechanismConfigurationSelector) {
Expand Down Expand Up @@ -1162,6 +1163,13 @@ private void handleOne(final Callback[] callbacks, final int idx) throws IOExcep
AuthenticationConfigurationCallback authenticationConfigurationCallback = (AuthenticationConfigurationCallback) callback;
saslSkipCertificateVerification = authenticationConfigurationCallback.getSaslSkipCertificateVerification();
handleOne(callbacks, idx + 1);
} else if (callback instanceof RequestInformationCallback) {
RequestInformationCallback requestInformationCallback = (RequestInformationCallback) callback;
String requestUri = requestInformationCallback.getRequestUri() != null ? requestInformationCallback.getRequestUri().toString() : null;
Attributes runtimeAttributes = new MapAttributes();
runtimeAttributes.addFirst(REQUEST_URI, requestUri);
addRuntimeAttributes(runtimeAttributes);
handleOne(callbacks, idx + 1);
}
else {
CallbackUtil.unsupported(callback);
Expand Down Expand Up @@ -2107,15 +2115,58 @@ void succeed() {
void fail(final boolean requireInProgress) {
final SecurityIdentity capturedIdentity = getSourceIdentity();
final AtomicReference<State> stateRef = getStateRef();
if (! stateRef.compareAndSet(this, FAILED)) {
if (!stateRef.compareAndSet(this, FAILED)) {
stateRef.get().fail(requireInProgress);
return;
}
SecurityRealm.safeHandleRealmEvent(getRealmInfo().getSecurityRealm(), new RealmFailedAuthenticationEvent(realmIdentity, null, null));
SecurityRealm.safeHandleRealmEvent(getRealmInfo().getSecurityRealm(), new RealmFailedAuthenticationEvent(getRealmIdentityWithRuntimeAttributes(), null, null));
SecurityDomain.safeHandleSecurityEvent(capturedIdentity.getSecurityDomain(), new SecurityAuthenticationFailedEvent(capturedIdentity, realmIdentity.getRealmIdentityPrincipal()));
realmIdentity.dispose();
}

private RealmIdentity getRealmIdentityWithRuntimeAttributes() {
return new RealmIdentity() {
@Override
public Principal getRealmIdentityPrincipal() {
return realmIdentity.getRealmIdentityPrincipal();
}

@Override
public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
return realmIdentity.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec);
}

@Override
public <C extends Credential> C getCredential(Class<C> credentialType) throws RealmUnavailableException {
return realmIdentity.getCredential(credentialType);
}

@Override
public SupportLevel getEvidenceVerifySupport(Class<? extends Evidence> evidenceType, String algorithmName) throws RealmUnavailableException {
return realmIdentity.getEvidenceVerifySupport(evidenceType, algorithmName);
}

@Override
public boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException {
return realmIdentity.verifyEvidence(evidence);
}

@Override
public boolean exists() throws RealmUnavailableException {
return realmIdentity.exists();
}

@Override
public AuthorizationIdentity getAuthorizationIdentity() throws RealmUnavailableException {
if (realmIdentity.exists()) {
return AuthorizationIdentity.basicIdentity(realmIdentity.getAuthorizationIdentity(), runtimeAttributes);
} else {
return AuthorizationIdentity.basicIdentity(AuthorizationIdentity.EMPTY, runtimeAttributes);
}
}
};
}

@Override
void setPrincipal(final Principal principal, final boolean exclusive) {
if (isSamePrincipal(principal)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2023 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wildfly.security.http.util;

import org.wildfly.security.auth.server.RequestInformationCallback;
import org.wildfly.security.http.HttpAuthenticationException;
import org.wildfly.security.http.HttpServerAuthenticationMechanism;
import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory;
import org.wildfly.security.http.HttpServerRequest;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import java.io.IOException;
import java.util.Map;

import static org.wildfly.common.Assert.checkNotNullParam;

/**
* A wrapper {@link HttpServerAuthenticationMechanismFactory} that sets the request information using the current authentication request.
*
* @author <a href="mailto:dvilkola@redhat.com">Diana Krepinska</a>
*/
public class SetRequestInformationCallbackServerMechanismFactory implements HttpServerAuthenticationMechanismFactory {

private final HttpServerAuthenticationMechanismFactory delegate;

/**
* Construct a wrapping mechanism factory instance.
*
* @param delegate the wrapped mechanism factory
*/
public SetRequestInformationCallbackServerMechanismFactory(final HttpServerAuthenticationMechanismFactory delegate) {
this.delegate = checkNotNullParam("delegate", delegate);
}

@Override
public String[] getMechanismNames(Map<String, ?> properties) {
return delegate.getMechanismNames(properties);
}

@Override
public HttpServerAuthenticationMechanism createAuthenticationMechanism(final String mechanismName, Map<String, ?> properties,
final CallbackHandler callbackHandler) throws HttpAuthenticationException {
final HttpServerAuthenticationMechanism mechanism = delegate.createAuthenticationMechanism(mechanismName, properties, callbackHandler);
return mechanism != null ? new HttpServerAuthenticationMechanism() {

@Override
public String getMechanismName() {
return mechanism.getMechanismName();
}

@Override
public void evaluateRequest(HttpServerRequest request) throws HttpAuthenticationException {
try {
callbackHandler.handle(new Callback[]{new RequestInformationCallback(request.getRequestURI())});
} catch (IOException | UnsupportedCallbackException e) {
throw new HttpAuthenticationException(e);
}

mechanism.evaluateRequest(request);
}

@Override
public void dispose() {
mechanism.dispose();
}

} : null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2023 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wildfly.security.auth.server;

import org.junit.Test;
import org.wildfly.security.auth.callback.MechanismInformationCallback;
import org.wildfly.security.auth.permission.LoginPermission;
import org.wildfly.security.authz.Attributes;
import org.wildfly.security.authz.MapAttributes;
import org.wildfly.security.credential.PasswordCredential;
import org.wildfly.security.password.PasswordFactory;
import org.wildfly.security.password.WildFlyElytronPasswordProvider;
import org.wildfly.security.password.interfaces.ClearPassword;
import org.wildfly.security.password.spec.ClearPasswordSpec;
import org.wildfly.security.permission.PermissionVerifier;

import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;

import static org.junit.Assert.fail;
import static org.wildfly.security.authz.RoleDecoder.KEY_SOURCE_ADDRESS;

public class RealmEventTest {

private static SecurityDomain usersDomain;
private static TestCustomRealm.CustomRealm usersRealm;
private static final Provider provider = WildFlyElytronPasswordProvider.getInstance();

private ServerAuthenticationContext setupAndGetServerAuthenticationContext() throws IOException, UnsupportedCallbackException {
Security.addProvider(provider);

usersRealm = new TestCustomRealm.CustomRealm();
SecurityDomain.Builder builder = SecurityDomain.builder();
builder.addRealm("users", usersRealm).build();
builder.setDefaultRealmName("users");
builder.setPermissionMapper((permissionMappable, roles) -> PermissionVerifier.from(new LoginPermission()));
usersDomain = builder.build();

ServerAuthenticationContext serverAuthenticationContext = usersDomain.createNewAuthenticationContext();
serverAuthenticationContext.addRuntimeAttributes(createRuntimeAttributesWithSourceAddress());

MechanismInformation mechanismInformation = new MechanismInformation() {
@Override
public String getMechanismType() {
return null;
}

@Override
public String getMechanismName() {
return null;
}

@Override
public String getHostName() {
return null;
}

@Override
public String getProtocol() {
return null;
}

};

CallbackHandler callbackHandler = serverAuthenticationContext.createCallbackHandler();
callbackHandler.handle(new MechanismInformationCallback[]{new MechanismInformationCallback(mechanismInformation)});
return serverAuthenticationContext;
}

@Test
public void testRealmSuccessfulAuthenticationEvent() throws IOException, UnsupportedCallbackException {
ServerAuthenticationContext serverAuthenticationContext = setupAndGetServerAuthenticationContext();
try {
serverAuthenticationContext.setAuthenticationName("myadmin");
serverAuthenticationContext.addPublicCredential(new PasswordCredential(
PasswordFactory.getInstance(ClearPassword.ALGORITHM_CLEAR).generatePassword(
new ClearPasswordSpec("mypassword".toCharArray()))));

serverAuthenticationContext.authorize();
} catch (RealmUnavailableException | InvalidKeySpecException | NoSuchAlgorithmException e) {
fail();
}
serverAuthenticationContext.succeed();
}

@Test
public void testRealmFailedAuthenticationEvent() throws NoSuchAlgorithmException, IOException, UnsupportedCallbackException, InvalidKeySpecException {
ServerAuthenticationContext serverAuthenticationContext = setupAndGetServerAuthenticationContext();
serverAuthenticationContext.setAuthenticationName("myadmin");
serverAuthenticationContext.addPublicCredential(new PasswordCredential(
PasswordFactory.getInstance(ClearPassword.ALGORITHM_CLEAR).generatePassword(
new ClearPasswordSpec("wrongPassword".toCharArray()))));
serverAuthenticationContext.fail();
}

private Attributes createRuntimeAttributesWithSourceAddress() {
MapAttributes runtimeAttributes = new MapAttributes();
runtimeAttributes.addFirst(KEY_SOURCE_ADDRESS, "10.12.14.16");
return runtimeAttributes;
}
}

0 comments on commit 9f0768b

Please sign in to comment.