Skip to content

Commit 27b9946

Browse files
committed
KARAF-5014: make nested groups discoverable
1 parent 67f8200 commit 27b9946

2 files changed

Lines changed: 166 additions & 75 deletions

File tree

jaas/modules/src/main/java/org/apache/karaf/jaas/modules/AbstractPropertiesBackingEngine.java

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -119,22 +119,24 @@ public static List<RolePrincipal> listRoles(Properties users, String name) {
119119
List<RolePrincipal> result = new ArrayList<>();
120120

121121
String userInfo = users.get(name);
122-
String[] infos = userInfo.split(",");
123-
for (int i = getFirstRoleIndex(name); i < infos.length; i++) {
124-
String roleName = infos[i].trim();
125-
if (roleName.isEmpty()) {
126-
// ignore
127-
} else if (roleName.startsWith(GROUP_PREFIX)) {
128-
for (RolePrincipal rp : listRoles(users, roleName)) {
122+
if (userInfo != null) {
123+
String[] infos = userInfo.split(",");
124+
for (int i = getFirstRoleIndex(name); i < infos.length; i++) {
125+
String roleName = infos[i].trim();
126+
if (roleName.isEmpty()) {
127+
// ignore
128+
} else if (roleName.startsWith(GROUP_PREFIX)) {
129+
for (RolePrincipal rp : listRoles(users, roleName)) {
130+
if (!result.contains(rp)) {
131+
result.add(rp);
132+
}
133+
}
134+
} else {
135+
RolePrincipal rp = new RolePrincipal(roleName);
129136
if (!result.contains(rp)) {
130137
result.add(rp);
131138
}
132139
}
133-
} else {
134-
RolePrincipal rp = new RolePrincipal(roleName);
135-
if (!result.contains(rp)) {
136-
result.add(rp);
137-
}
138140
}
139141
}
140142
return result;
@@ -173,24 +175,22 @@ public void addRole(String username, String role) {
173175
String newUserInfos = userInfos + "," + role;
174176
users.put(username, newUserInfos);
175177
}
176-
}
177-
try {
178-
users.save();
179-
} catch (Exception ex) {
180-
LOGGER.error("Cannot update users file,", ex);
178+
try {
179+
users.save();
180+
} catch (Exception ex) {
181+
LOGGER.error("Cannot update users file,", ex);
182+
}
181183
}
182184
}
183185

184186
@Override
185187
public void deleteRole(String username, String role) {
186-
String[] infos = null;
187-
StringBuilder userInfoBuffer = new StringBuilder();
188-
189188
String userInfos = users.get(username);
190189

191190
// if user already exists, remove the role
192191
if (userInfos != null && userInfos.length() > 0) {
193-
infos = userInfos.split(",");
192+
StringBuilder userInfoBuffer = new StringBuilder();
193+
String[] infos = userInfos.split(",");
194194

195195
int firstRoleIndex = getFirstRoleIndex(username);
196196
if (firstRoleIndex == 1) { // index 0 is password
@@ -207,12 +207,11 @@ public void deleteRole(String username, String role) {
207207
}
208208
String newUserInfo = userInfoBuffer.toString();
209209
users.put(username, newUserInfo);
210-
}
211-
212-
try {
213-
users.save();
214-
} catch (Exception ex) {
215-
LOGGER.error("Cannot update users file,", ex);
210+
try {
211+
users.save();
212+
} catch (Exception ex) {
213+
LOGGER.error("Cannot update users file,", ex);
214+
}
216215
}
217216
}
218217

@@ -230,7 +229,15 @@ public static List<GroupPrincipal> listGroups(Properties users, String userName)
230229
for (int i = getFirstRoleIndex(userName); i < infos.length; i++) {
231230
String name = infos[i].trim();
232231
if (name.startsWith(GROUP_PREFIX)) {
233-
result.add(new GroupPrincipal(name.substring(GROUP_PREFIX.length())));
232+
GroupPrincipal group = new GroupPrincipal(name.substring(GROUP_PREFIX.length()));
233+
if (!result.contains(group)) {
234+
result.add(group);
235+
for (GroupPrincipal g : listGroups(users, name)) {
236+
if (!result.contains(g)) {
237+
result.add(g);
238+
}
239+
}
240+
}
234241
}
235242
}
236243
}
@@ -259,6 +266,16 @@ public void deleteGroup(String username, String group) {
259266
}
260267
}
261268
}
269+
for (GroupPrincipal g : listGroups().keySet()) {
270+
if (!group.equals(g.getName())) {
271+
for (GroupPrincipal nestedGroup : listGroups(users, GROUP_PREFIX + g.getName())) {
272+
if (group.equals(nestedGroup.getName())) {
273+
// there is another group referencing this group, nothing to clean up
274+
return;
275+
}
276+
}
277+
}
278+
}
262279

263280
// nobody is using this group any more, remove it
264281
deleteUser(GROUP_PREFIX + group);

jaas/modules/src/test/java/org/apache/karaf/jaas/modules/AbstractPropertiesBackingEngineTest.java

Lines changed: 121 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,12 @@
1414
*/
1515
package org.apache.karaf.jaas.modules;
1616

17+
import static org.apache.karaf.jaas.modules.BackingEngine.GROUP_PREFIX;
1718
import static org.apache.karaf.jaas.modules.PrincipalHelper.names;
1819
import static org.hamcrest.MatcherAssert.assertThat;
1920
import static org.hamcrest.Matchers.contains;
2021
import static org.hamcrest.Matchers.containsInAnyOrder;
21-
import static org.junit.Assert.assertEquals;
22-
import static org.junit.Assert.assertNull;
23-
import static org.junit.Assert.assertTrue;
24-
import static org.junit.Assert.fail;
22+
import static org.junit.Assert.*;
2523

2624
import org.apache.felix.utils.properties.Properties;
2725
import org.apache.karaf.jaas.boot.principal.GroupPrincipal;
@@ -52,31 +50,34 @@ public void start() throws IOException {
5250
public void addUserInternalTest() {
5351
// non-existing user
5452
engine.addUserInternal("a", "");
53+
List<String> uaInfo = List.of(p.get("a").split(","));
54+
assertEquals(1, uaInfo.size());
55+
assertEquals("", uaInfo.get(0));
5556

5657
// update empty password on existing user with no roles
5758
engine.addUserInternal("a", "pass1");
58-
assertThat(List.of(p.get("a").split(",")), contains("pass1"));
59-
UserPrincipal upa = PrincipalHelper.getUser(engine, "a");
60-
assertTrue(engine.listGroups(upa).isEmpty());
61-
assertTrue(engine.listRoles(upa).isEmpty());
59+
uaInfo = List.of(p.get("a").split(","));
60+
assertEquals(1, uaInfo.size());
61+
assertEquals("pass1", uaInfo.get(0));
6262

6363
// update password on existing user with no roles
6464
engine.addUserInternal("a", "pass2");
65-
assertThat(List.of(p.get("a").split(",")), contains("pass2"));
66-
upa = PrincipalHelper.getUser(engine, "a");
67-
assertTrue(engine.listGroups(upa).isEmpty());
68-
assertTrue(engine.listRoles(upa).isEmpty());
65+
uaInfo = List.of(p.get("a").split(","));
66+
assertEquals(1, uaInfo.size());
67+
assertEquals("pass2", uaInfo.get(0));
6968

70-
// update password on existing user with roles
69+
// update password on existing user with roles and groups
7170
engine.addRole("a", "role1");
7271
engine.addGroup("a", "g1");
7372
engine.addGroupRole("g1", "role2");
7473
engine.addUserInternal("a", "pass3");
75-
assertThat(List.of(p.get("a").split(",")),
76-
contains("pass3", "role1", PropertiesBackingEngine.GROUP_PREFIX + "g1"));
77-
upa = PrincipalHelper.getUser(engine, "a");
78-
assertThat(names(engine.listGroups(upa)), containsInAnyOrder("g1"));
79-
assertThat(names(engine.listRoles(upa)), containsInAnyOrder("role1", "role2"));
74+
uaInfo = List.of(p.get("a").split(","));
75+
assertEquals(3, uaInfo.size());
76+
assertThat(uaInfo, contains("pass3", "role1", getGroupRef("g1")));
77+
}
78+
79+
private String getGroupRef(String groupName) {
80+
return GROUP_PREFIX + groupName;
8081
}
8182

8283
@Test
@@ -91,77 +92,150 @@ public void lookupUserTest() {
9192

9293
@Test
9394
public void listRolesTest() {
95+
// non-existing user
96+
assertTrue(engine.listRoles(p, "a").isEmpty());
97+
9498
// empty roles in groups
95-
p.put(PropertiesBackingEngine.GROUP_PREFIX + "g1", ",,,"); // simulate manual editing of the properties file
99+
p.put(getGroupRef("g1"), "");
96100
GroupPrincipal gpg1 = PrincipalHelper.getGroup(engine, "g1");
97101
assertTrue(engine.listRoles(gpg1).isEmpty());
102+
p.put(getGroupRef("g1"), ",,,");
103+
assertTrue(engine.listRoles(gpg1).isEmpty());
98104

99105
// empty roles in users
100-
p.put("a", "pass1,,,");
106+
p.put("a", "pass1");
101107
UserPrincipal upa = PrincipalHelper.getUser(engine, "a");
102108
assertTrue(engine.listRoles(upa).isEmpty());
109+
p.put("a", "pass1,,,");
110+
assertTrue(engine.listRoles(upa).isEmpty());
103111

104112
// duplicate role
105113
p.put("a", "pass1,role1,role1");
106-
upa = PrincipalHelper.getUser(engine, "a");
107114
List<RolePrincipal> roles = engine.listRoles(upa);
108115
assertEquals(1, roles.size());
109116
assertEquals("role1", roles.get(0).getName());
117+
118+
// roles in nested group
119+
p.put("a", "pass1,role1,role1," + getGroupRef("g1"));
120+
p.put(getGroupRef("g1"), getGroupRef("g2"));
121+
p.put(getGroupRef("g2"), "role2");
122+
roles = engine.listRoles(upa);
123+
assertEquals(2, roles.size());
124+
assertThat(names(roles), containsInAnyOrder("role1", "role2"));
110125
}
111126

112127
@Test
113128
public void addRoleTest() {
114-
// empty info in groups
115-
engine.createGroup("g1");
116-
engine.addGroupRole("g1", "role1");
117-
GroupPrincipal gpg1 = PrincipalHelper.getGroup(engine, "g1");
118-
assertThat(names(engine.listRoles(gpg1)), contains("role1"));
129+
// non-existing user
130+
engine.addRole("a", "role1");
131+
assertNull(p.get("a"));
119132

120133
// empty info in users
121134
engine.addUser("a", "");
122-
engine.addRole("a", "role2");
123-
UserPrincipal upa = PrincipalHelper.getUser(engine, "a");
124-
assertThat(names(engine.listRoles(upa)), contains("role2"));
135+
engine.addRole("a", "role1");
125136
// user empty password is preserved
126-
assertThat(List.of(p.get("a").split(",")), containsInAnyOrder("", "role2"));
137+
List<String> uaInfo = List.of(p.get("a").split(","));
138+
assertEquals(2, uaInfo.size());
139+
assertThat(uaInfo, containsInAnyOrder("", "role1"));
127140

128141
// duplicate role
129-
engine.addRole("a", "role2");
130-
upa = PrincipalHelper.getUser(engine, "a");
131-
assertThat(names(engine.listRoles(upa)), contains("role2"));
132-
assertThat(List.of(p.get("a").split(",")), containsInAnyOrder("", "role2"));
142+
engine.addRole("a", "role1");
143+
uaInfo = List.of(p.get("a").split(","));
144+
assertEquals(2, uaInfo.size());
145+
assertThat(uaInfo, containsInAnyOrder("", "role1"));
133146

134-
// duplicate group
135-
engine.addUser("b", "");
136-
engine.addGroup("b", "g2");
137-
engine.addGroup("b", "g2");
138-
UserPrincipal upb = PrincipalHelper.getUser(engine, "b");
139-
assertThat(names(engine.listGroups(upb)), contains("g2"));
140-
assertThat(List.of(p.get("b").split(",")),
141-
containsInAnyOrder("", PropertiesBackingEngine.GROUP_PREFIX + "g2"));
147+
// empty info in groups
148+
engine.createGroup("g1");
149+
engine.addGroupRole("g1", "role2");
150+
List<String> g1Info = List.of(p.get(getGroupRef("g1")).split(","));
151+
assertEquals(1, g1Info.size());
152+
assertEquals("role2", g1Info.get(0));
142153
}
143154

144155
@Test
145156
public void deleteRoleTest() {
157+
// non-existing user
158+
engine.deleteRole("a", "role1");
159+
assertNull(p.get("a"));
160+
146161
// delete in group
147162
engine.createGroup("g1");
148163
engine.addGroupRole("g1", "role1");
149164
engine.addGroupRole("g1", "role2");
150165
engine.addGroupRole("g1", "role3");
151166
engine.deleteGroupRole("g1", "role1");
152-
GroupPrincipal gpg1 = PrincipalHelper.getGroup(engine, "g1");
153-
assertThat(names(engine.listRoles(gpg1)), containsInAnyOrder("role2", "role3"));
167+
List<String> g1Info = List.of(p.get(getGroupRef("g1")).split(","));
168+
assertEquals(2, g1Info.size());
169+
assertThat(g1Info, containsInAnyOrder("role2", "role3"));
154170

155171
// delete in user
156172
engine.addUser("a", "");
157173
engine.addRole("a", "role4");
158174
engine.addRole("a", "role5");
159175
engine.addRole("a", "role6");
160176
engine.deleteRole("a", "role4");
161-
UserPrincipal upa = PrincipalHelper.getUser(engine, "a");
162-
assertThat(names(engine.listRoles(upa)), containsInAnyOrder("role5", "role6"));
163177
// user empty password is preserved
164-
assertThat(List.of(p.get("a").split(",")), containsInAnyOrder("", "role5", "role6"));
178+
List<String> uaInfo = List.of(p.get("a").split(","));
179+
assertEquals(3, uaInfo.size());
180+
assertThat(uaInfo, containsInAnyOrder("", "role5", "role6"));
181+
}
182+
183+
@Test
184+
public void listGroupsTest() {
185+
// non-existing user
186+
assertTrue(engine.listGroups(p, "a").isEmpty());
187+
188+
// duplicate group
189+
p.put("a", "pass1," + getGroupRef("g1") + "," + getGroupRef("g1"));
190+
UserPrincipal upa = PrincipalHelper.getUser(engine, "a");
191+
List<GroupPrincipal> groups = engine.listGroups(upa);
192+
assertEquals(1, groups.size());
193+
assertEquals("g1", groups.get(0).getName());
194+
195+
// nested group
196+
p.put(getGroupRef("g1"), getGroupRef("g2"));
197+
groups = engine.listGroups(upa);
198+
assertEquals(2, groups.size());
199+
assertThat(names(groups), containsInAnyOrder("g1", "g2"));
200+
201+
// duplicate nested group
202+
p.put(getGroupRef("g2"), getGroupRef("g3") + "," + getGroupRef("g3"));
203+
groups = engine.listGroups(upa);
204+
assertEquals(3, groups.size());
205+
assertThat(names(groups), containsInAnyOrder("g1", "g2", "g3"));
206+
}
207+
208+
@Test
209+
public void addGroupTest() {
210+
// duplicate group
211+
engine.addUser("a", "");
212+
engine.addGroup("a", "g1");
213+
engine.addGroup("a", "g1");
214+
List<String> uaInfo = List.of(p.get("a").split(","));
215+
assertEquals(2, uaInfo.size());
216+
assertThat(uaInfo, containsInAnyOrder("", getGroupRef("g1")));
217+
}
218+
219+
@Test
220+
public void deleteGroupTest() {
221+
// group has only 1 user reference
222+
engine.addUser("a", "");
223+
engine.addGroup("a", "g1");
224+
engine.deleteGroup("a", "g1");
225+
assertNull(p.get(getGroupRef("g1")));
226+
227+
// group is referenced by multiple users
228+
engine.addGroup("a", "g1");
229+
engine.addUser("b", "");
230+
engine.addGroup("b", "g1");
231+
engine.deleteGroup("a", "g1");
232+
assertNotNull(p.get(getGroupRef("g1")));
233+
234+
// group is referenced by other groups
235+
engine.addGroup("a", "g2");
236+
p.put(getGroupRef("g3"), getGroupRef("g2"));
237+
engine.deleteGroup("a", "g2");
238+
assertNotNull(p.get(getGroupRef("g2")));
165239
}
166240

167241
@After

0 commit comments

Comments
 (0)