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

[ELY-2583] Make requestURI and Source-Address available from RealmSuccessfulAuthenticationEvent and RealmFailedAuthenticationEvent #1952

Merged
merged 1 commit into from
Jan 10, 2024
Merged
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
@@ -0,0 +1,54 @@
/*
* 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.callback;

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 {

/**
* 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 @@ -30,7 +30,9 @@
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.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.Predicate;
Expand Down Expand Up @@ -63,6 +65,7 @@
import org.wildfly.security.auth.callback.MechanismInformationCallback;
import org.wildfly.security.auth.callback.IdentityCredentialCallback;
import org.wildfly.security.auth.callback.PeerPrincipalCallback;
import org.wildfly.security.auth.callback.RequestInformationCallback;
import org.wildfly.security.auth.callback.SSLCallback;
import org.wildfly.security.auth.callback.SecurityIdentityCallback;
import org.wildfly.security.auth.callback.ServerCredentialCallback;
Expand Down Expand Up @@ -1162,6 +1165,16 @@ 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();
Attributes runtimeAttributes = new MapAttributes();
for (Map.Entry<String, Object> entry : props.entrySet()) {
if (entry.getValue() != null) {
runtimeAttributes.addFirst(entry.getKey(), entry.getValue().toString());
}
}
addRuntimeAttributes(runtimeAttributes);
handleOne(callbacks, idx + 1);
}
else {
CallbackUtil.unsupported(callback);
Expand Down Expand Up @@ -2107,15 +2120,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,94 @@
/*
* 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.callback.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 java.util.function.Function;

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;
private final HashMap<String, Function<HttpServerRequest, String>> httpServerRequestInformationMap;

/**
* Construct a wrapping mechanism factory instance.
*
* @param delegate the wrapped mechanism factory
*/
public SetRequestInformationCallbackMechanismFactory(final HttpServerAuthenticationMechanismFactory delegate, HashMap<String, Function<HttpServerRequest, String>> httpServerRequestInformationMap) {
this.delegate = checkNotNullParam("delegate", delegate);
this.httpServerRequestInformationMap = checkNotNullParam("httpServerRequestInformationMap", httpServerRequestInformationMap);
}

@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<>();
for (Map.Entry<String, Function<HttpServerRequest, String>> entry : httpServerRequestInformationMap.entrySet()) {
props.put(entry.getKey(), entry.getValue().apply(request));
}
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
Loading