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 Sep 15, 2023
1 parent 3dc896a commit 785cbb3
Show file tree
Hide file tree
Showing 5 changed files with 306 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
*/
package org.wildfly.security.auth.server;

import java.net.URI;

/**
* Information about the current mechanism being used for authentication.
*
Expand Down Expand Up @@ -53,6 +55,10 @@ public interface MechanismInformation {
*/
String getProtocol();

default URI getRequestURI() {
return null;
}

MechanismInformation DEFAULT = new MechanismInformation() {

@Override
Expand Down
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 @@ -1108,6 +1109,10 @@ private void handleOne(final Callback[] callbacks, final int idx) throws IOExcep
mi.getMechanismType(), mi.getMechanismName(), mi.getHostName(), mi.getProtocol());
}
setMechanismInformation(mi);
Attributes runtimeAttributes = new MapAttributes();
String requestUri = mi.getRequestURI() != null ? mi.getRequestURI().toString() : null;
runtimeAttributes.addFirst(REQUEST_URI, requestUri);
addRuntimeAttributes(runtimeAttributes);
handleOne(callbacks, idx + 1);
} catch (Exception e) {
throw new IOException(e);
Expand Down Expand Up @@ -2107,15 +2112,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
Expand Up @@ -20,6 +20,7 @@
import static org.wildfly.security.http.HttpConstants.HOST;
import static org.wildfly.security.http.util.ElytronMessages.log;

import java.net.URI;
import java.util.Map;

import javax.security.auth.callback.Callback;
Expand Down Expand Up @@ -73,6 +74,7 @@ public String getMechanismName() {
public void evaluateRequest(HttpServerRequest request) throws HttpAuthenticationException {
String host = request.getFirstRequestHeaderValue(HOST);
String resolvedHostName = null;
URI requestedUri = request.getRequestURI();
if (host != null) {
if (host.startsWith("[")) {
int close = host.indexOf(']');
Expand Down Expand Up @@ -110,6 +112,11 @@ public String getMechanismName() {
public String getHostName() {
return hostName;
}
@Override
public URI getRequestURI() {
return requestedUri;
}

})});

} catch (Throwable e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* 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.net.URI;
import java.net.URISyntaxException;
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;
}

@Override
public URI getRequestURI() {
try {
return new URI("http://www.testURI.com");
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
};

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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* 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 TestCustomRealm {

public static class CustomRealm implements SecurityRealm {

// 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) {
if (event instanceof RealmSuccessfulAuthenticationEvent) {
assertEquals("10.12.14.16", ((RealmSuccessfulAuthenticationEvent) event).getAuthorizationIdentity().getRuntimeAttributes().get("Source-Address").get(0));
assertEquals("testURI", ((RealmSuccessfulAuthenticationEvent) event).getAuthorizationIdentity().getRuntimeAttributes().get("Request-URI").get(0));
assertEquals("myadmin", ((RealmSuccessfulAuthenticationEvent) event).getRealmIdentity().getRealmIdentityPrincipal());
}
if (event instanceof RealmFailedAuthenticationEvent) {
try {
assertEquals("10.12.14.16", ((RealmFailedAuthenticationEvent) event).getRealmIdentity().getAuthorizationIdentity().getRuntimeAttributes().get("Source-Address").get(0));
assertEquals("testURI", ((RealmFailedAuthenticationEvent) event).getRealmIdentity().getAuthorizationIdentity().getRuntimeAttributes().get("Request-URI").get(0));
assertEquals("myadmin", ((RealmSuccessfulAuthenticationEvent) event).getRealmIdentity().getRealmIdentityPrincipal());
} catch (RealmUnavailableException e) {
fail("RealmFailedAuthenticationEvent should have runtime attributes associated");
}
}
}
}
}

0 comments on commit 785cbb3

Please sign in to comment.