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 27, 2023
1 parent 3dc896a commit 5e79fa5
Show file tree
Hide file tree
Showing 5 changed files with 431 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* 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.util.HashMap;

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

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

/**
* request URI of the current authentication request
*/
public static final String REQUEST_URI = "Request-URI";

/**
* Properties of the current authentication request
*/
private final HashMap<String, Object> props;

/**
* Construct a new instance of this {@link Callback}.
*
* @param props Properties of the current authentication request
*/
public RequestInformationCallback(HashMap<String, Object> props) {
checkNotNullParam("props", props);
this.props = props;
}

/**
* Get the properties of this request.
*
* @return properties of the current authentication request
*/
public HashMap<String, Object> getProperties() {
return this.props;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
Expand Down Expand Up @@ -1162,6 +1164,14 @@ 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) {
HashMap<String, Object> props = ((RequestInformationCallback) callback).getProperties();
Object requestUriObject = props.get(RequestInformationCallback.REQUEST_URI);
URI requestUri = requestUriObject instanceof URI ? (URI) requestUriObject : null;
Attributes runtimeAttributes = new MapAttributes();
runtimeAttributes.addFirst(RequestInformationCallback.REQUEST_URI, requestUri != null ? requestUri.toString() : null);
addRuntimeAttributes(runtimeAttributes);
handleOne(callbacks, idx + 1);
}
else {
CallbackUtil.unsupported(callback);
Expand Down Expand Up @@ -2107,15 +2117,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,89 @@
/*
* 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.HashMap;
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:[email protected]">Diana Krepinska</a>
*/
public class SetRequestInformationCallbackMechanismFactory implements HttpServerAuthenticationMechanismFactory {

private final HttpServerAuthenticationMechanismFactory delegate;

/**
* Construct a wrapping mechanism factory instance.
*
* @param delegate the wrapped mechanism factory
*/
public SetRequestInformationCallbackMechanismFactory(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 {
HashMap<String, Object> props = new HashMap<>();
props.put(RequestInformationCallback.REQUEST_URI, request.getRequestURI());
callbackHandler.handle(new Callback[]{new RequestInformationCallback(props)});
} 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,116 @@
/*
* 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.SupportLevel;
import org.wildfly.security.auth.server.event.RealmEvent;
import org.wildfly.security.auth.server.event.RealmFailedAuthenticationEvent;
import org.wildfly.security.auth.server.event.RealmSuccessfulAuthenticationEvent;
import org.wildfly.security.credential.Credential;
import org.wildfly.security.evidence.Evidence;
import org.wildfly.security.evidence.PasswordGuessEvidence;

import java.security.Principal;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

public class CustomRealm implements SecurityRealm {
public boolean wasAssertionError = false;

// this realm does not allow acquiring credentials
public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName,
AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException {
return SupportLevel.UNSUPPORTED;
}

// this realm will be able to verify password evidences only
public SupportLevel getEvidenceVerifySupport(Class<? extends Evidence> evidenceType, String algorithmName)
throws RealmUnavailableException {
return PasswordGuessEvidence.class.isAssignableFrom(evidenceType) ? SupportLevel.POSSIBLY_SUPPORTED : SupportLevel.UNSUPPORTED;
}

public RealmIdentity getRealmIdentity(final Principal principal) throws RealmUnavailableException {

if ("myadmin".equals(principal.getName())) { // identity "myadmin" will have password "mypassword"
return new RealmIdentity() {
public Principal getRealmIdentityPrincipal() {
return principal;
}

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

public <C extends Credential> C getCredential(Class<C> credentialType) throws RealmUnavailableException {
return null;
}

public SupportLevel getEvidenceVerifySupport(Class<? extends Evidence> evidenceType, String algorithmName) {
return PasswordGuessEvidence.class.isAssignableFrom(evidenceType) ? SupportLevel.SUPPORTED : SupportLevel.UNSUPPORTED;
}

// evidence will be accepted if it is password "mypassword"
public boolean verifyEvidence(Evidence evidence) {
if (evidence instanceof PasswordGuessEvidence) {
PasswordGuessEvidence guess = (PasswordGuessEvidence) evidence;
try {
return Arrays.equals("mypassword".toCharArray(), guess.getGuess());

} finally {
guess.destroy();
}
}
return false;
}

public boolean exists() {
return true;
}
};
}
return RealmIdentity.NON_EXISTENT;
}

@Override
public void handleRealmEvent(RealmEvent event) {
try {

if (event instanceof RealmSuccessfulAuthenticationEvent) {
assertEquals("10.12.14.16", ((RealmSuccessfulAuthenticationEvent) event).getAuthorizationIdentity().getRuntimeAttributes().get("Source-Address").get(0));
assertEquals("www.test-request-uri.org", ((RealmSuccessfulAuthenticationEvent) event).getAuthorizationIdentity().getRuntimeAttributes().get("Request-URI").get(0));
assertEquals("myadmin", ((RealmSuccessfulAuthenticationEvent) event).getRealmIdentity().getRealmIdentityPrincipal().getName());
}
if (event instanceof RealmFailedAuthenticationEvent) {
try {
assertEquals("10.12.14.16", ((RealmFailedAuthenticationEvent) event).getRealmIdentity().getAuthorizationIdentity().getRuntimeAttributes().get("Source-Address").get(0));
assertEquals("www.test-request-uri.org", ((RealmFailedAuthenticationEvent) event).getRealmIdentity().getAuthorizationIdentity().getRuntimeAttributes().get("Request-URI").get(0));
assertEquals("myadmin", ((RealmFailedAuthenticationEvent) event).getRealmIdentity().getRealmIdentityPrincipal().getName());
} catch (RealmUnavailableException e) {
fail("RealmFailedAuthenticationEvent should have runtime attributes associated");
}
}
} catch (AssertionError e) {
wasAssertionError = true;
}
}
}

Loading

0 comments on commit 5e79fa5

Please sign in to comment.