Skip to content

Commit

Permalink
Merge pull request #5861 from fjuma/WFCORE-6411
Browse files Browse the repository at this point in the history
[WFCORE-6411] Make it possible to use JaasSecurityRealm via a custom-realm resource
  • Loading branch information
yersan committed Feb 15, 2024
2 parents e06f936 + f516d31 commit 6bba569
Show file tree
Hide file tree
Showing 10 changed files with 498 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>

<!--
~ JBoss, Home of Professional Open Source.
~ Copyright 2023, Red Hat, Inc., and individual contributors
~ as indicated by the @author tags. See the copyright.txt file in the
~ distribution for a full listing of individual contributors.
~
~ This is free software; you can redistribute it and/or modify it
~ under the terms of the GNU Lesser General Public License as
~ published by the Free Software Foundation; either version 2.1 of
~ the License, or (at your option) any later version.
~
~ This software is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
~ Lesser General Public License for more details.
~
~ You should have received a copy of the GNU Lesser General Public
~ License along with this software; if not, write to the Free
~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->

<module xmlns="urn:jboss:module:1.9" name="org.wildfly.extension.elytron.jaas-realm">

<!-- This module is deprecated, and you should use the jaas-realm resource instead of this module. -->
<properties>
<property name="jboss.api" value="deprecated"/>
</properties>

<exports>
<exclude path="org/wildfly/extension/elytron/JaasCustomSecurityRealmWrapper"/>
</exports>

<resources>
<artifact name="${org.wildfly.core:wildfly-elytron-integration}"/>
</resources>

<dependencies>
<module name="java.logging"/>
<module name="java.xml"/>
<module name="org.jboss.as.controller"/>
<module name="org.jboss.as.server"/>
<module name="org.jboss.modules"/>
<module name="org.jboss.msc"/>
<module name="org.jboss.logging"/>
<module name="org.jboss.logmanager"/>
<module name="org.wildfly.common"/>
<module name="org.wildfly.security.elytron-private" export="true"/>
</dependencies>
</module>
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<feature-group name="standalone-elytron"/>
<packages>
<!-- required by default configuration-->
<package name="org.wildfly.extension.elytron.jaas-realm"/>
<package name="org.wildfly.openssl"/>
</packages>
</layer-spec>
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* 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.extension.elytron;

import org.jboss.as.controller.services.path.PathManager;
import org.jboss.msc.service.StartException;
import org.jboss.msc.value.InjectedValue;
import org.wildfly.security.auth.SupportLevel;
import org.wildfly.security.auth.realm.JaasSecurityRealm;
import org.wildfly.security.auth.server.RealmIdentity;
import org.wildfly.security.auth.server.RealmUnavailableException;
import org.wildfly.security.auth.server.SecurityRealm;
import org.wildfly.security.auth.server.event.RealmEvent;
import org.wildfly.security.credential.Credential;
import org.wildfly.security.evidence.Evidence;

import javax.security.auth.callback.CallbackHandler;
import java.io.File;
import java.security.Principal;
import java.security.PrivilegedExceptionAction;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Map;
import java.util.function.Function;

import static org.wildfly.extension.elytron.ClassLoadingAttributeDefinitions.resolveClassLoader;
import static org.wildfly.extension.elytron.FileAttributeDefinitions.pathResolver;
import static org.wildfly.extension.elytron.SecurityActions.doPrivileged;
import static org.wildfly.extension.elytron._private.ElytronSubsystemMessages.ROOT_LOGGER;

/**
* Wrapper for JAAS REALM so it can be defined as a custom realm resource
*
* @deprecated Use a jaas-realm resource instead
*/
@Deprecated
public class JaasCustomSecurityRealmWrapper implements SecurityRealm {
private JaasSecurityRealm jaasSecurityRealm;

// receiving configuration from subsystem
public void initialize(Map<String, String> configuration) throws StartException {

String entry = configuration.get("entry");
if (entry == null || entry.isEmpty()) {
throw ROOT_LOGGER.jaasEntryNotDefined();
}
String pathParam = configuration.get("path");
String relativeToParam = configuration.get("relative-to");
String moduleNameParam = configuration.get("module");
String callbackHandlerName = configuration.get("callbackHandlerName");

String rootPath = null;
FileAttributeDefinitions.PathResolver pathResolver;
InjectedValue<PathManager> pathManagerInjector = new InjectedValue<>();
if (pathParam != null) {
pathResolver = pathResolver();
File jaasConfigFile = pathResolver.path(pathParam).relativeTo(relativeToParam, pathManagerInjector.getOptionalValue()).resolve();
if (!jaasConfigFile.exists()) {
throw ROOT_LOGGER.jaasFileDoesNotExist(jaasConfigFile.getPath());
}
rootPath = jaasConfigFile.getPath();
}

CallbackHandler callbackhandler = null;
ClassLoader classLoader;
try {
classLoader = doPrivileged((PrivilegedExceptionAction<ClassLoader>) () -> resolveClassLoader(moduleNameParam));
if (callbackHandlerName != null) {
Class<?> typeClazz = classLoader.loadClass(callbackHandlerName);
callbackhandler = (CallbackHandler) typeClazz.getDeclaredConstructor().newInstance();
}
} catch (Exception e) {
throw ROOT_LOGGER.failedToLoadCallbackhandlerFromProvidedModule();
}
this.jaasSecurityRealm = new JaasSecurityRealm(entry, rootPath, classLoader, callbackhandler);
}

@Override
public RealmIdentity getRealmIdentity(Principal principal) throws RealmUnavailableException {
return jaasSecurityRealm.getRealmIdentity(principal);
}

@Override
public RealmIdentity getRealmIdentity(Evidence evidence) throws RealmUnavailableException {
return jaasSecurityRealm.getRealmIdentity(evidence);
}

@Override
public RealmIdentity getRealmIdentity(Evidence evidence, Function<Principal, Principal> principalTransformer) throws RealmUnavailableException {
return jaasSecurityRealm.getRealmIdentity(evidence, principalTransformer);
}

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

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

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

@Override
public void handleRealmEvent(RealmEvent event) {
jaasSecurityRealm.handleRealmEvent(event);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,9 @@ public interface ElytronSubsystemMessages extends BasicLogger {
@Message(id = 48, value = "A string representation of an X.500 distinguished name is required: %s")
IllegalArgumentException representationOfX500IsRequired(String causeMessage);

@Message(id = 49, value = "Entry is not defined.")
StartException jaasEntryNotDefined();

/*
* Credential Store Section.
*/
Expand Down
7 changes: 6 additions & 1 deletion testsuite/elytron/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,12 @@
<executions>
<execution>
<id>prepare-elytron-config-file</id>
<phase>none</phase>
<phase>none</phase><configuration>
<system-properties>
<maven.repo.local>${settings.localRepository}</maven.repo.local>
<module.path>${wildfly.home}/modules</module.path>
</system-properties>
</configuration>
</execution>
</executions>
</plugin>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* Copyright 2023 Red Hat, Inc.
*
* 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.test.security.common.elytron;

import org.jboss.as.cli.Util;
import org.jboss.as.test.integration.management.util.CLIWrapper;
import org.jboss.as.test.shared.TestSuiteEnvironment;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.exporter.ZipExporter;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.wildfly.core.testrunner.WildflyTestRunner;

import java.io.File;
import java.nio.file.Files;

/**
* Tests testing JaasSecurityRealm via custom realm resource
*/
@RunWith(WildflyTestRunner.class)
public class JaasCustomRealmWrapperTest {

@ClassRule
public static TemporaryFolder tmpDir = new TemporaryFolder();

@BeforeClass
public static void setup() throws Exception {
JavaArchive jar = ShrinkWrap.create(JavaArchive.class, "testJaasCustomRealm.jar")
.addAsResource(new StringAsset("Dependencies: org.wildfly.security"), "META-INF/MANIFEST.MF")
.addClasses(TestLoginModule.class, TestCallbackHandler.class);
File jarFile = new File(tmpDir.getRoot(), "testJaasCustomRealm.jar");
jar.as(ZipExporter.class).exportTo(jarFile, true);
CLIWrapper cli = new CLIWrapper(true);
try {
if (!isTsBootable()) {
cli.sendLine("module add --name=jaasLoginModule --resources=" + jarFile.getAbsolutePath() +
" --dependencies=org.wildfly.security.elytron,org.wildfly.extension.elytron");
} else {
cli.sendLine("module add --module-root-dir=" + getBootableJarModulesPath() + " --name=jaasLoginModule --resources=" + jarFile.getAbsolutePath() +
" --dependencies=org.wildfly.security.elytron,org.wildfly.extension.elytron");
}
} finally {
Files.deleteIfExists(new File(tmpDir.getRoot(), "testJaasCustomRealm.jar").toPath());
}
}

@Test
public void testAddJaasRealmAsCustomRealm() throws Exception {
CLIWrapper cli = new CLIWrapper(true);
cli.sendLine("/subsystem=elytron/custom-realm=customJaasWrapperRealm:add(module=org.wildfly.extension.elytron.jaas-realm, class-name=org.wildfly.extension.elytron.JaasCustomSecurityRealmWrapper," +
"configuration={entry=Entry1,module=jaasLoginModule, callback-handler=org.wildfly.test.integration.elytron.realm.TestCallbackHandler, path=" +
JaasCustomRealmWrapperTest.class.getResource("jaas-login.config").getFile() + "})");
Assert.assertTrue(cli.readAllAsOpResult().isIsOutcomeSuccess());
cli.sendLine("/subsystem=elytron/security-domain=jaasTestDomain:add(realms=[{realm=customJaasWrapperRealm}], default-realm=customJaasWrapperRealm, permission-mapper=default-permission-mapper)");
Assert.assertTrue(cli.readAllAsOpResult().isIsOutcomeSuccess());
cli.sendLine("/subsystem=elytron/security-domain=jaasTestDomain:remove");
cli.sendLine("/subsystem=elytron/custom-realm=customJaasWrapperRealm:remove");
}

@Test
public void testJaasRealmHasToContainEntry() {
CLIWrapper cli = null;
try {
cli = new CLIWrapper(true);
} catch (Exception e) {
throw new RuntimeException(e);
}
try {
cli.sendLine("/subsystem=elytron/custom-realm=customJaasWrapperRealm:add(module=org.wildfly.extension.elytron.jaas-realm," +
"class-name=org.wildfly.extension.elytron.JaasCustomSecurityRealmWrapper," +
"configuration={module=jaasLoginModule,callback-handler=org.wildfly.test.integration.elytron.realm.TestCallbackHandler, path=" +
JaasCustomRealmWrapperTest.class.getResource("jaas-login.config").getFile() + "})");
Assert.fail();
} catch (AssertionError e) {
if (!e.getMessage().contains(" Entry is not defined.")) {
Assert.fail();
}
}
}

@AfterClass
public static void cleanUp() throws Exception {
CLIWrapper cli = new CLIWrapper(true);
try {
if (!isTsBootable()) {
cli.sendLine("module remove --name=jaasLoginModule");
} else {
cli.sendLine("module remove --module-root-dir=" + getBootableJarModulesPath() + " --name=jaasLoginModule");
}
} catch (AssertionError e) {
// ignore failure on Windows, cannot remove module on running server due to file locks
if (!Util.isWindows())
throw e;
}
cli.sendLine("reload");
}

private static boolean isTsBootable() {
String bootableJar = System.getProperty("wildfly.bootable.jar");
return bootableJar != null && bootableJar.equals("true");
}

private static String getBootableJarModulesPath() {
return TestSuiteEnvironment.getSystemProperty("wildfly.bootable.jar.install.dir") + "/modules";
}
}
Loading

0 comments on commit 6bba569

Please sign in to comment.