diff --git a/CHANGELOG.md b/CHANGELOG.md index 938168fa6a..528869df11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ 1.8.2 (?/??/16) ================ - +* [#397](https://github.com/dblock/waffle/pull/397): WindowsLoginModule missing roles in Principal. [@xystra](https://github.com/xystra) * [#338](https://github.com/dblock/waffle/pull/338): Don't allow SPNEGO NegTokenArg to start re-authentication process [@AriSuutariST](https://github.com/AriSuutariST). * [#342](https://github.com/dblock/waffle/pull/342): Add tomcat 8.5.x support [@hazendaz](https://github.com/hazendaz). * [#357](https://github.com/dblock/waffle/pull/357): Fix security token handle leak in Tomcat. Issue [#355](https://github.com/dblock/waffle/issues/355) diff --git a/Source/JNA/waffle-jna/src/main/java/waffle/jaas/GroupPrincipal.java b/Source/JNA/waffle-jna/src/main/java/waffle/jaas/GroupPrincipal.java new file mode 100644 index 0000000000..9341dc0d60 --- /dev/null +++ b/Source/JNA/waffle-jna/src/main/java/waffle/jaas/GroupPrincipal.java @@ -0,0 +1,125 @@ +/** + * Waffle (https://github.com/dblock/waffle) + * + * Copyright (c) 2010-2016 Application Security, Inc. + * + * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse + * Public License v1.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html. + * + * Contributors: Application Security, Inc. + */ +package waffle.jaas; + +import java.security.Principal; +import java.security.acl.Group; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Group principal. + * + * @author rockchip[dot]tv[at]gmail[dot]com + */ +public class GroupPrincipal extends UserPrincipal implements Group { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + /** The fqn. */ + private final String fqn; + + /** A list of fqn members for this group. */ + private final Map members; + + public GroupPrincipal(final String fqn) { + super(fqn); + + this.fqn = fqn; + this.members = new HashMap<>(); + } + + /* + * (non-Javadoc) + * @see waffle.jaas.UserPrincipal#getName() + */ + @Override + public String getName() { + return fqn; + } + + /* + * (non-Javadoc) + * @see java.security.acl.Group#addMember(java.security.Principal) + */ + @Override + public boolean addMember(final Principal user) { + final boolean isMember = members.containsKey(user); + if (!isMember) { + members.put(user, user); + } + return isMember; + } + + /* + * (non-Javadoc) + * @see java.security.acl.Group#isMember(java.security.Principal) + */ + @Override + public boolean isMember(final Principal user) { + boolean isMember = members.containsKey(user); + if (!isMember) { + final Collection values = members.values(); + final Iterator iter = values.iterator(); + while (!isMember && iter.hasNext()) { + final Object next = iter.next(); + if (next instanceof Group) { + final Group group = (Group) next; + isMember = group.isMember(user); + } + } + } + return isMember; + } + + /* + * (non-Javadoc) + * @see java.security.acl.Group#members() + */ + @Override + public Enumeration members() { + return Collections.enumeration(members.values()); + } + + /* + * (non-Javadoc) + * @see java.security.acl.Group#removeMember(java.security.Principal) + */ + @Override + public boolean removeMember(final Principal user) { + final Object prev = members.remove(user); + return prev != null; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + final StringBuffer tmp = new StringBuffer(getName()); + tmp.append("(members:"); + final Iterator iter = members.keySet().iterator(); + while (iter.hasNext()) { + tmp.append(iter.next()); + tmp.append(','); + } + tmp.setCharAt(tmp.length() - 1, ')'); + return tmp.toString(); + } + +} diff --git a/Source/JNA/waffle-jna/src/main/java/waffle/jaas/WindowsLoginModule.java b/Source/JNA/waffle-jna/src/main/java/waffle/jaas/WindowsLoginModule.java index b61b29e858..12b8cb44b5 100644 --- a/Source/JNA/waffle-jna/src/main/java/waffle/jaas/WindowsLoginModule.java +++ b/Source/JNA/waffle-jna/src/main/java/waffle/jaas/WindowsLoginModule.java @@ -155,11 +155,19 @@ public boolean login() throws LoginException { } this.principals = new LinkedHashSet<>(); + // add the main user principal to the subject principals this.principals.addAll(WindowsLoginModule.getUserPrincipals(windowsIdentity, this.principalFormat)); if (this.roleFormat != PrincipalFormat.NONE) { + // create the group principal and add roles as members of the group + final GroupPrincipal groupList = new GroupPrincipal("Roles"); for (final IWindowsAccount group : windowsIdentity.getGroups()) { - this.principals.addAll(WindowsLoginModule.getRolePrincipals(group, this.roleFormat)); + for (final Principal role : WindowsLoginModule.getRolePrincipals(group, this.roleFormat)) { + WindowsLoginModule.LOGGER.debug(" group: {}", role.getName()); + groupList.addMember(new RolePrincipal(role.getName())); + } } + // add the group and roles to the subject principals + this.principals.add(groupList); } this.username = windowsIdentity.getFqn(); diff --git a/Source/JNA/waffle-jna/src/test/java/waffle/jaas/GroupPrincipalTests.java b/Source/JNA/waffle-jna/src/test/java/waffle/jaas/GroupPrincipalTests.java new file mode 100644 index 0000000000..5f62655ea5 --- /dev/null +++ b/Source/JNA/waffle-jna/src/test/java/waffle/jaas/GroupPrincipalTests.java @@ -0,0 +1,96 @@ +/** + * Waffle (https://github.com/dblock/waffle) + * + * Copyright (c) 2010-2016 Application Security, Inc. + * + * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse + * Public License v1.0 which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html. + * + * Contributors: Application Security, Inc. + */ +package waffle.jaas; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import org.assertj.core.api.Assertions; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * The Class GroupPrincipalTests. + * + * @author rockchip[dot]tv[at]gmail[dot]com + */ +public class GroupPrincipalTests { + + /** The group principal. */ + private GroupPrincipal groupPrincipal; + + /** + * Equals_other object. + */ + @Test + public void equals_otherObject() { + Assert.assertNotEquals(this.groupPrincipal, new String()); + } + + /** + * Equals_same object. + */ + @Test + public void equals_sameObject() { + Assert.assertEquals(this.groupPrincipal, this.groupPrincipal); + } + + /** + * Sets the up. + */ + @Before + public void setUp() { + this.groupPrincipal = new GroupPrincipal("localhost\\Administrator"); + } + + /** + * Test equals_ symmetric. + */ + @Test + public void testEquals_Symmetric() { + final GroupPrincipal x = new GroupPrincipal("localhost\\Administrator"); + final GroupPrincipal y = new GroupPrincipal("localhost\\Administrator"); + Assert.assertEquals(x, y); + Assert.assertEquals(x.hashCode(), y.hashCode()); + } + + /** + * Test is serializable. + * + * @throws IOException + * Signals that an I/O exception has occurred. + * @throws ClassNotFoundException + * the class not found exception + */ + @Test + public void testIsSerializable() throws IOException, ClassNotFoundException { + // serialize + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (final ObjectOutputStream oos = new ObjectOutputStream(out)) { + oos.writeObject(this.groupPrincipal); + } + Assertions.assertThat(out.toByteArray().length).isGreaterThan(0); + // deserialize + final InputStream in = new ByteArrayInputStream(out.toByteArray()); + final ObjectInputStream ois = new ObjectInputStream(in); + final GroupPrincipal copy = (GroupPrincipal) ois.readObject(); + // test + Assert.assertEquals(this.groupPrincipal, copy); + Assert.assertEquals(this.groupPrincipal.getName(), copy.getName()); + } + +} diff --git a/Source/JNA/waffle-jna/src/test/java/waffle/jaas/WindowsLoginModuleTest.java b/Source/JNA/waffle-jna/src/test/java/waffle/jaas/WindowsLoginModuleTest.java index 642e328567..dcdc32322b 100644 --- a/Source/JNA/waffle-jna/src/test/java/waffle/jaas/WindowsLoginModuleTest.java +++ b/Source/JNA/waffle-jna/src/test/java/waffle/jaas/WindowsLoginModuleTest.java @@ -127,6 +127,24 @@ public void commit_withDebug() throws LoginException { this.loginModule.commit(); } + /** + * Commit_with Roles. + * + * @throws LoginException + * the login exception + */ + @Test + public void commit_withRoles() throws LoginException { + final Set principals = new LinkedHashSet<>(); + principals.add(new UserPrincipal("FQN")); + final GroupPrincipal group = new GroupPrincipal("Roles"); + group.addMember(new RolePrincipal("WindowsGroup")); + principals.add(group); + Deencapsulation.setField(this.loginModule, principals); + this.loginModule.initialize(this.subject, this.callbackHandler, null, this.options); + this.loginModule.commit(); + } + /** * Inits the. */