Skip to content

Commit c7947db

Browse files
committed
KARAF-5014: consider first group role in users.properties and ignore empty roles
1 parent a8d9901 commit c7947db

18 files changed

Lines changed: 460 additions & 594 deletions

File tree

assemblies/apache-karaf/src/main/distribution/text/etc/keys.properties

Lines changed: 0 additions & 31 deletions
This file was deleted.

assemblies/features/base/src/main/resources/resources/etc/keys.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,4 @@
3333
# The user guide describes how to generate/update the key.
3434
#
3535
#karaf=AAAAB3NzaC1kc3MAAACBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAAAAFQCXYFCPFSMLzLKSuYKi64QL8Fgc9QAAAIEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoAAACBAKKSU2PFl/qOLxIwmBZPPIcJshVe7bVUpFvyl3BbJDow8rXfskl8wO63OzP/qLmcJM0+JbcRU/53JjTuyk31drV2qxhIOsLDC9dGCWj47Y7TyhPdXh/0dthTRBy6bqGtRPxGa7gJov1xm/UuYYXPIUR/3x9MAZvZ5xvE0kYXO+rx,_g_:admingroup
36-
_g_\:admingroup = group,admin,manager,viewer,systembundles,ssh
36+
_g_\:admingroup = admin,manager,viewer,systembundles,ssh

assemblies/features/base/src/main/resources/resources/etc/users.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,4 @@
3030
# with the name "karaf".
3131
#
3232
#karaf = karaf,_g_:admingroup
33-
#_g_\:admingroup = group,admin,manager,viewer,systembundles,ssh
33+
#_g_\:admingroup = admin,manager,viewer,systembundles,ssh

client/src/test/resources/etc1/users.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@
1818
################################################################################
1919

2020
karaf = karaf,_g_:admingroup
21-
_g_\:admingroup = group,admin,manager,viewer,systembundles
21+
_g_\:admingroup = admin,manager,viewer,systembundles

client/src/test/resources/etc2/users.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#
1818
################################################################################
1919

20-
_g_\:admingroup = group,admin,manager,viewer,systembundles
20+
_g_\:admingroup = admin,manager,viewer,systembundles
2121
test = admin,_g_:admingroup
2222
admin = admin,_g_:admingroup
2323
karaf = karaf,_g_:admingroup

examples/karaf-itest-example/src/test/resources/etc/users.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,4 @@
3030
# with the name "karaf".
3131
#
3232
karaf = karaf,_g_:admingroup
33-
_g_\:admingroup = group,admin,manager,viewer,systembundles,ssh
33+
_g_\:admingroup = admin,manager,viewer,systembundles,ssh

itests/test/src/test/resources/etc/users.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,4 @@
3030
# with the name "karaf".
3131
#
3232
karaf = karaf,_g_:admingroup
33-
_g_\:admingroup = group,admin,manager,viewer,systembundles,ssh
33+
_g_\:admingroup = admin,manager,viewer,systembundles,ssh
Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.apache.karaf.jaas.modules;
17+
18+
import org.apache.felix.utils.properties.Properties;
19+
import org.apache.karaf.jaas.boot.principal.GroupPrincipal;
20+
import org.apache.karaf.jaas.boot.principal.RolePrincipal;
21+
import org.apache.karaf.jaas.boot.principal.UserPrincipal;
22+
import org.slf4j.Logger;
23+
import org.slf4j.LoggerFactory;
24+
import java.security.Principal;
25+
import java.util.ArrayList;
26+
import java.util.HashMap;
27+
import java.util.List;
28+
import java.util.Map;
29+
30+
public abstract class AbstractPropertiesBackingEngine implements BackingEngine {
31+
32+
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractPropertiesBackingEngine.class);
33+
34+
private Properties users;
35+
36+
public AbstractPropertiesBackingEngine(Properties users) {
37+
this.users = users;
38+
}
39+
40+
protected void addUserInternal(String username, String encPassword) {
41+
String[] infos = null;
42+
StringBuilder userInfoBuffer = new StringBuilder();
43+
44+
String userInfos = users.get(username);
45+
46+
//If user already exists, update password
47+
if (userInfos != null && userInfos.length() > 0) {
48+
infos = userInfos.split(",");
49+
userInfoBuffer.append(encPassword);
50+
51+
for (int i = 1; i < infos.length; i++) {
52+
userInfoBuffer.append(",");
53+
userInfoBuffer.append(infos[i]);
54+
}
55+
String newUserInfo = userInfoBuffer.toString();
56+
users.put(username, newUserInfo);
57+
} else {
58+
users.put(username, encPassword);
59+
}
60+
61+
try {
62+
users.save();
63+
} catch (Exception ex) {
64+
LOGGER.error("Cannot update users file,", ex);
65+
}
66+
}
67+
68+
@Override
69+
public void deleteUser(String username) {
70+
// delete all its groups first, for garbage collection of the groups
71+
for (GroupPrincipal gp : listGroups(username)) {
72+
deleteGroup(username, gp.getName());
73+
}
74+
75+
users.remove(username);
76+
77+
try {
78+
users.save();
79+
} catch (Exception ex) {
80+
LOGGER.error("Cannot remove users file,", ex);
81+
}
82+
}
83+
84+
@Override
85+
public List<UserPrincipal> listUsers() {
86+
List<UserPrincipal> result = new ArrayList<>();
87+
88+
for (Object user : users.keySet()) {
89+
String userName = (String) user;
90+
if (userName.startsWith(GROUP_PREFIX))
91+
continue;
92+
93+
UserPrincipal userPrincipal = new UserPrincipal(userName);
94+
result.add(userPrincipal);
95+
}
96+
return result;
97+
}
98+
99+
@Override
100+
public UserPrincipal lookupUser(String username) {
101+
for (UserPrincipal userPrincipal : listUsers()) {
102+
if (userPrincipal.getName().equals(username)) {
103+
return userPrincipal;
104+
}
105+
}
106+
return null;
107+
}
108+
109+
@Override
110+
public List<RolePrincipal> listRoles(Principal principal) {
111+
String userName = principal.getName();
112+
if (principal instanceof GroupPrincipal) {
113+
userName = GROUP_PREFIX + userName;
114+
}
115+
return listRoles(userName);
116+
}
117+
118+
protected List<RolePrincipal> listRoles(String name) {
119+
120+
List<RolePrincipal> result = new ArrayList<>();
121+
String userInfo = users.get(name);
122+
String[] infos = userInfo.split(",");
123+
for (int i = JAASUtils.getFirstRoleIndex(name); i < infos.length; i++) {
124+
String roleName = infos[i];
125+
if (roleName.trim().isEmpty())
126+
continue;
127+
if (roleName.startsWith(GROUP_PREFIX)) {
128+
for (RolePrincipal rp : listRoles(roleName)) {
129+
if (!result.contains(rp)) {
130+
result.add(rp);
131+
}
132+
}
133+
} else {
134+
RolePrincipal rp = new RolePrincipal(roleName);
135+
if (!result.contains(rp)) {
136+
result.add(rp);
137+
}
138+
}
139+
}
140+
return result;
141+
}
142+
143+
@Override
144+
public void addRole(String username, String role) {
145+
String userInfos = users.get(username);
146+
if (userInfos != null) {
147+
// for groups, empty info should be replaced with role
148+
// for users, empty info means empty password and role should be appended
149+
if (userInfos.trim().isEmpty()
150+
&& username.trim().startsWith(AbstractPropertiesBackingEngine.GROUP_PREFIX)) {
151+
users.put(username, role);
152+
} else {
153+
for (RolePrincipal rp : listRoles(username)) {
154+
if (role.equals(rp.getName())) {
155+
return;
156+
}
157+
}
158+
for (GroupPrincipal gp : listGroups(username)) {
159+
if (role.equals(GROUP_PREFIX + gp.getName())) {
160+
return;
161+
}
162+
}
163+
String newUserInfos = userInfos + "," + role;
164+
users.put(username, newUserInfos);
165+
}
166+
}
167+
try {
168+
users.save();
169+
} catch (Exception ex) {
170+
LOGGER.error("Cannot update users file,", ex);
171+
}
172+
}
173+
174+
@Override
175+
public void deleteRole(String username, String role) {
176+
String[] infos = null;
177+
StringBuilder userInfoBuffer = new StringBuilder();
178+
179+
String userInfos = users.get(username);
180+
181+
//If user already exists, remove the role
182+
if (userInfos != null && userInfos.length() > 0) {
183+
infos = userInfos.split(",");
184+
185+
int firstRoleIndex = JAASUtils.getFirstRoleIndex(username);
186+
if (firstRoleIndex == 1) {// index 0 is password
187+
String password = infos[0];
188+
userInfoBuffer.append(password);
189+
}
190+
for (int i = firstRoleIndex; i < infos.length; i++) {
191+
if (infos[i] != null && !infos[i].equals(role)) {
192+
if(userInfoBuffer.length() > 0) {
193+
userInfoBuffer.append(",");
194+
}
195+
userInfoBuffer.append(infos[i]);
196+
}
197+
}
198+
String newUserInfo = userInfoBuffer.toString();
199+
users.put(username, newUserInfo);
200+
}
201+
202+
try {
203+
users.save();
204+
} catch (Exception ex) {
205+
LOGGER.error("Cannot update users file,", ex);
206+
}
207+
}
208+
209+
@Override
210+
public List<GroupPrincipal> listGroups(UserPrincipal user) {
211+
String userName = user.getName();
212+
return listGroups(userName);
213+
}
214+
215+
private List<GroupPrincipal> listGroups(String userName) {
216+
List<GroupPrincipal> result = new ArrayList<>();
217+
String userInfo = users.get(userName);
218+
if (userInfo != null) {
219+
String[] infos = userInfo.split(",");
220+
for (int i = JAASUtils.getFirstRoleIndex(userName); i < infos.length; i++) {
221+
String name = infos[i];
222+
if (name.startsWith(GROUP_PREFIX)) {
223+
result.add(new GroupPrincipal(name.substring(GROUP_PREFIX.length())));
224+
}
225+
}
226+
}
227+
return result;
228+
}
229+
230+
@Override
231+
public void addGroup(String username, String group) {
232+
String groupName = GROUP_PREFIX + group;
233+
if (users.get(groupName) == null) {
234+
addUserInternal(groupName, ""); // groups don't have password
235+
}
236+
addRole(username, groupName);
237+
}
238+
239+
@Override
240+
public void deleteGroup(String username, String group) {
241+
deleteRole(username, GROUP_PREFIX + group);
242+
243+
// garbage collection, clean up the groups if needed
244+
for (UserPrincipal user : listUsers()) {
245+
for (GroupPrincipal g : listGroups(user)) {
246+
if (group.equals(g.getName())) {
247+
// there is another user of this group, nothing to clean up
248+
return;
249+
}
250+
}
251+
}
252+
253+
// nobody is using this group any more, remove it
254+
deleteUser(GROUP_PREFIX + group);
255+
}
256+
257+
@Override
258+
public void addGroupRole(String group, String role) {
259+
addRole(GROUP_PREFIX + group, role);
260+
}
261+
262+
@Override
263+
public void deleteGroupRole(String group, String role) {
264+
deleteRole(GROUP_PREFIX + group, role);
265+
}
266+
267+
public Map<GroupPrincipal, String> listGroups() {
268+
Map<GroupPrincipal, String> result = new HashMap<>();
269+
for (String name : users.keySet()) {
270+
if (name.startsWith(GROUP_PREFIX)) {
271+
result.put(new GroupPrincipal(name.substring(GROUP_PREFIX.length())), users.get(name));
272+
}
273+
}
274+
return result;
275+
}
276+
277+
@Override
278+
public void createGroup(String group) {
279+
String groupName = GROUP_PREFIX + group;
280+
if (users.get(groupName) == null) {
281+
addUserInternal(groupName, ""); // groups don't have password
282+
} else {
283+
throw new IllegalArgumentException("Group: " + group + " already exist");
284+
}
285+
}
286+
}

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
*/
1515
package org.apache.karaf.jaas.modules;
1616

17+
import org.apache.karaf.jaas.boot.principal.RolePrincipal;
18+
import java.security.Principal;
1719
import java.util.Map;
20+
import java.util.Set;
1821

1922
public final class JAASUtils {
2023

@@ -30,4 +33,20 @@ public static String getString(Map<String, ?> options, String key) {
3033
return (String)val;
3134
}
3235

33-
}
36+
/**
37+
* Determines the starting index of role and group definitions for a given key in a file-based login module.
38+
* @param name the property key to evaluate, representing a group or a username
39+
* @return 0 if the key starts with the group prefix, otherwise 1
40+
*/
41+
public static int getFirstRoleIndex(String name) {
42+
if (name.trim().startsWith(BackingEngine.GROUP_PREFIX))
43+
return 0;
44+
return 1;
45+
}
46+
47+
public static void addRole(Set<Principal> principals, String role) {
48+
role = role.trim();
49+
if (!role.isEmpty())
50+
principals.add(new RolePrincipal(role.trim()));
51+
}
52+
}

0 commit comments

Comments
 (0)