ARTEMIS-3168 - add PrincipalConversionLoginModule feature
This commit is contained in:
parent
02bb7031c2
commit
06461f146c
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.activemq.artemis.spi.core.security.jaas;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import java.security.Principal;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* populate an empty (no UserPrincipal) subject with UserPrincipal seeded from existing principal
|
||||
* Useful when a third party login module generated principal needs to be accepted as-is by the broker
|
||||
*/
|
||||
public class PrincipalConversionLoginModule implements AuditLoginModule {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(PrincipalConversionLoginModule.class);
|
||||
|
||||
public static final String PRINCIPAL_CLASS_LIST = "principalClassList";
|
||||
private Subject subject;
|
||||
private String principalClazzList;
|
||||
private Principal principal;
|
||||
|
||||
@Override
|
||||
public void initialize(Subject subject,
|
||||
CallbackHandler callbackHandler,
|
||||
Map<String, ?> sharedState,
|
||||
Map<String, ?> options) {
|
||||
this.subject = subject;
|
||||
this.principalClazzList = (String) options.get(PRINCIPAL_CLASS_LIST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean login() throws LoginException {
|
||||
logger.debug("login ok!");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean commit() throws LoginException {
|
||||
|
||||
if (subject == null || !subject.getPrincipals(UserPrincipal.class).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (principalClazzList != null) {
|
||||
String[] principalClasses = principalClazzList.split(",");
|
||||
for (String principalClass : principalClasses) {
|
||||
String trimmedCandidateClassName = principalClass.trim();
|
||||
for (Principal candidatePrincipal : subject.getPrincipals()) {
|
||||
logger.debug("Checking principal class name:" + candidatePrincipal.getClass().getName() + ", " + candidatePrincipal);
|
||||
if (candidatePrincipal.getClass().getName().equals(trimmedCandidateClassName)) {
|
||||
logger.debug("converting: " + candidatePrincipal);
|
||||
principal = new UserPrincipal(candidatePrincipal.getName());
|
||||
subject.getPrincipals().add(principal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.debug("commit, result: " + (principal != null));
|
||||
return principal != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean abort() throws LoginException {
|
||||
registerFailureForAudit(principal != null ? principal.getName() : null);
|
||||
clear();
|
||||
logger.debug("abort");
|
||||
return true;
|
||||
}
|
||||
|
||||
private void clear() {
|
||||
principal = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean logout() throws LoginException {
|
||||
if (subject != null) {
|
||||
subject.getPrincipals().remove(principal);
|
||||
}
|
||||
clear();
|
||||
logger.debug("logout");
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You 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.apache.activemq.artemis.spi.core.security.jaas;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import java.security.Principal;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class PrincipalConversionLoginModuleTest {
|
||||
|
||||
@Test
|
||||
public void loginOk() throws Exception {
|
||||
PrincipalConversionLoginModule underTest = new PrincipalConversionLoginModule();
|
||||
|
||||
final Subject subject = new Subject();
|
||||
underTest.initialize(subject,null, null, new HashMap<>());
|
||||
|
||||
assertTrue(underTest.login());
|
||||
assertFalse(underTest.commit());
|
||||
assertTrue(subject.getPrincipals().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginOkOnNullSubject() throws Exception {
|
||||
PrincipalConversionLoginModule underTest = new PrincipalConversionLoginModule();
|
||||
|
||||
underTest.initialize(null,null, null, new HashMap<>());
|
||||
|
||||
assertTrue(underTest.login());
|
||||
assertFalse(underTest.commit());
|
||||
assertTrue(underTest.logout());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginConvert() throws Exception {
|
||||
PrincipalConversionLoginModule underTest = new PrincipalConversionLoginModule();
|
||||
|
||||
final Subject subject = new Subject();
|
||||
final HashMap<String, String> options = new HashMap<>();
|
||||
options.put(PrincipalConversionLoginModule.PRINCIPAL_CLASS_LIST, RolePrincipal.class.getCanonicalName());
|
||||
subject.getPrincipals().add(new RolePrincipal("BLA"));
|
||||
underTest.initialize(subject, null, null, options);
|
||||
|
||||
assertTrue(underTest.login());
|
||||
assertTrue(underTest.commit());
|
||||
assertEquals(1, subject.getPrincipals(UserPrincipal.class).size());
|
||||
assertEquals("BLA", ((Principal)subject.getPrincipals(UserPrincipal.class).iterator().next()).getName());
|
||||
|
||||
underTest.logout();
|
||||
assertEquals(0, subject.getPrincipals(UserPrincipal.class).size());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void loginNoOverwriteExistingUserPrincipal() throws Exception {
|
||||
PrincipalConversionLoginModule underTest = new PrincipalConversionLoginModule();
|
||||
|
||||
final Subject subject = new Subject();
|
||||
final HashMap<String, String> options = new HashMap<>();
|
||||
options.put(PrincipalConversionLoginModule.PRINCIPAL_CLASS_LIST, RolePrincipal.class.getCanonicalName());
|
||||
subject.getPrincipals().add(new RolePrincipal("BLA"));
|
||||
subject.getPrincipals().add(new UserPrincipal("Important"));
|
||||
|
||||
underTest.initialize(subject, null, null, options);
|
||||
|
||||
assertTrue(underTest.login());
|
||||
assertFalse(underTest.commit());
|
||||
assertEquals(1, subject.getPrincipals(UserPrincipal.class).size());
|
||||
assertEquals("Important", ((Principal)subject.getPrincipals(UserPrincipal.class).iterator().next()).getName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
static final class TestPrincipal implements Principal {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object another) {
|
||||
return this == another;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "TestPrincipal";
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginConvertList() throws Exception {
|
||||
PrincipalConversionLoginModule underTest = new PrincipalConversionLoginModule();
|
||||
|
||||
final Subject subject = new Subject();
|
||||
final HashMap<String, String> options = new HashMap<>();
|
||||
String multiOptionList = TestPrincipal.class.getTypeName() + ", " + RolePrincipal.class.getTypeName();
|
||||
options.put(PrincipalConversionLoginModule.PRINCIPAL_CLASS_LIST, multiOptionList);
|
||||
subject.getPrincipals().add(new TestPrincipal());
|
||||
underTest.initialize(subject, null, null, options);
|
||||
|
||||
assertTrue(underTest.login());
|
||||
assertTrue(underTest.commit());
|
||||
assertEquals(1, subject.getPrincipals(UserPrincipal.class).size());
|
||||
assertEquals("TestPrincipal", ((Principal)subject.getPrincipals(UserPrincipal.class).iterator().next()).getName());
|
||||
}
|
||||
|
||||
}
|
|
@ -1025,6 +1025,19 @@ do role mapping for the TLS client certificate.
|
|||
org.apache.activemq.artemis.spi.core.security.jaas.ExternalCertificateLoginModule required
|
||||
;
|
||||
```
|
||||
|
||||
#### PrincipalConversionLoginModule
|
||||
|
||||
The principal conversion login module is used to convert an existing validated Principal
|
||||
into a JAAS UserPrincipal. The module is configured with a list of class names used to
|
||||
match existing Principals. If no UserPrincipal exists, the first matching Principal
|
||||
will be added as a UserPrincipal of the same Name.
|
||||
|
||||
```
|
||||
org.apache.activemq.artemis.spi.core.security.jaas.PrincipalConversionLoginModule required
|
||||
principalClassList=org.apache.x.Principal,org.apache.y.Principal
|
||||
;
|
||||
```
|
||||
|
||||
|
||||
The simplest way to make the login configuration available to JAAS is to add
|
||||
|
|
Loading…
Reference in New Issue