Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 41aedd2a221cbe1309f4a61aa7b72324697aa434 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
/*******************************************************************************
 * Copyright (c) 2001, 2008 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.equinox.internal.useradmin;

import java.util.Enumeration;
import java.util.Vector;
import org.osgi.service.prefs.BackingStoreException;

/**
 * A named grouping of roles.
 * <p>
 * Whether or not a given authorization context implies a Group role
 * depends on the members of that role.
 * <p>
 * A Group role can have two kinds of member roles: <i>basic</i> and
 * <i>required</i>.
 * A Group role is implied by an authorization context if all of
 * its required member roles are implied
 * and at least one of its basic member roles is implied.
 * <p>
 * A Group role must contain at least one basic member role in order
 * to be implied. In other words, a Group without any basic member
 * roles is never implied by any authorization context.
 * <p>
 * A User role always implies itself.
 * <p>
 * No loop detection is performed when adding members to groups, which
 * means that it is possible to create circular implications. Loop
 * detection is instead done when roles are checked. The semantics is that
 * if a role depends on itself (i.e., there is an implication loop), the
 * role is not implied.
 * <p>
 * The rule that a group must have at least one basic member to be implied
 * is motivated by the following example:
 *
 * <pre>
 * group foo
 *   required members: marketing
 *   basic members: alice, bob
 * </pre>
 *
 * Privileged operations that require membership in "foo" can be performed
 * only by alice and bob, who are in marketing.
 * <p>
 * If alice and bob ever transfer to a different department, anybody in
 * marketing will be able to assume the "foo" role, which certainly must be
 * prevented.
 * Requiring that "foo" (or any Group role for that matter) must have at least
 * one basic member accomplishes that.
 * <p>
 * However, this would make it impossible for a group to be implied by just
 * its required members. An example where this implication might be useful
 * is the following declaration: "Any citizen who is an adult is allowed to
 * vote."
 * An intuitive configuration of "voter" would be:
 *
 * <pre>
 * group voter
 *   required members: citizen, adult
 *      basic members:
 * </pre>
 *
 * However, according to the above rule, the "voter" role could never be
 * assumed by anybody, since it lacks any basic members.
 * In order to address this deficiency a predefined role named
 * "user.anyone" can be specified, which is always implied.
 * The desired implication of the "voter" group can then be achieved by
 * specifying "user.anyone" as its basic member, as follows:
 *
 * <pre>
 * group voter
 *   required members: citizen, adult
 *      basic members: user.anyone
 * </pre>
 */

public class Group extends User implements org.osgi.service.useradmin.Group {

	protected Vector requiredMembers;
	protected Vector basicMembers;

	protected Group(String name, UserAdmin useradmin) {
		super(name, useradmin);
		this.useradmin = useradmin;
		basicMembers = new Vector();
		requiredMembers = new Vector();
	}

	/**
	 * Adds the specified role as a basic member to this Group.
	 *
	 * @param role The role to add as a basic member.
	 *
	 * @return <code>true</code> if the given role could be added as a basic
	 * member,
	 * and <code>false</code> if this Group already contains a role whose name
	 * matches that of the specified role.
	 *
	 * @throws SecurityException If a security manager exists and the caller
	 * does not have the <tt>UserAdminPermission</tt> with name <tt>admin</tt>.
	 */
	public boolean addMember(org.osgi.service.useradmin.Role role) {
		useradmin.checkAlive();
		useradmin.checkAdminPermission();
		//only need to check for null for the public methods
		if (role == null) {
			return (false);
		}
		synchronized (useradmin) {
			if (basicMembers.contains(role)) {
				return (false);
			}
			return (addMember(role, true));
		}
	}

	// When we are loading from storage this method is called directly.  We
	// do not want to write to storage when we are loading form storage.
	protected boolean addMember(org.osgi.service.useradmin.Role role, boolean store) {
		((org.eclipse.equinox.internal.useradmin.Role) role).addImpliedRole(this);
		if (store) {
			try {
				useradmin.userAdminStore.addMember(this, (org.eclipse.equinox.internal.useradmin.Role) role);
			} catch (BackingStoreException ex) {
				return (false);
			}
		}
		basicMembers.addElement(role);
		return (true);
	}

	/**
	 * Adds the specified role as a required member to this Group.
	 *
	 * @param role The role to add as a required member.
	 *
	 * @return <code>true</code> if the given role could be added as a required
	 * member, and <code>false</code> if this Group already contains a role
	 * whose name matches that of the specified role.
	 *
	 * @throws SecurityException If a security manager exists and the caller
	 * does not have the <tt>UserAdminPermission</tt> with name <tt>admin</tt>.
	 */
	public boolean addRequiredMember(org.osgi.service.useradmin.Role role) {
		useradmin.checkAlive();
		useradmin.checkAdminPermission();
		if (role == null) {
			return (false);
		}
		synchronized (useradmin) {
			if (requiredMembers.contains(role)) {
				return (false);
			}
			return (addRequiredMember(role, true));
		}
	}

	protected boolean addRequiredMember(org.osgi.service.useradmin.Role role, boolean store) {
		((org.eclipse.equinox.internal.useradmin.Role) role).addImpliedRole(this);
		if (store) {
			try {
				useradmin.userAdminStore.addRequiredMember(this, (org.eclipse.equinox.internal.useradmin.Role) role);
			} catch (BackingStoreException ex) {
				return (false);
			}
		}
		requiredMembers.addElement(role);
		return (true);
	}

	/**
	 * Removes the specified role from this Group.
	 *
	 * @param role The role to remove from this Group.
	 *
	 * @return <code>true</code> if the role could be removed,
	 * otherwise <code>false</code>.
	 *
	 * @throws SecurityException If a security manager exists and the caller
	 * does not have the <tt>UserAdminPermission</tt> with name <tt>admin</tt>.
	 */
	public boolean removeMember(org.osgi.service.useradmin.Role role) {
		useradmin.checkAlive();
		useradmin.checkAdminPermission();
		if (role == null) {
			return (false);
		}
		synchronized (useradmin) {
			try {
				useradmin.userAdminStore.removeMember(this, (org.eclipse.equinox.internal.useradmin.Role) role);
			} catch (BackingStoreException ex) {
				return (false);
			}
			//The role keeps track of which groups it is a member of so it can remove itself from
			//the group if it is deleted.  In this case, this group is being removed from the role's
			//list.
			((org.eclipse.equinox.internal.useradmin.Role) role).removeImpliedRole(this);

			// We don't know if the Role to be removed is a basic orrequired member, or both.  We
			// simply try to remove it from both.
			boolean removeRequired = requiredMembers.removeElement(role);
			boolean removeBasic = basicMembers.removeElement(role);
			return (removeRequired || removeBasic);
		}
	}

	/**
	 * Gets the basic members of this Group.
	 *
	 * @return The basic members of this Group, or <code>null</code> if this
	 * Group does not contain any basic members.
	 */
	public org.osgi.service.useradmin.Role[] getMembers() {
		useradmin.checkAlive();
		synchronized (useradmin) {
			if (basicMembers.isEmpty()) {
				return (null);
			}
			Role[] roles = new Role[basicMembers.size()];
			basicMembers.copyInto(roles);
			return (roles);
		}
	}

	/**
	 * Gets the required members of this Group.
	 *
	 * @return The required members of this Group, or <code>null</code> if this
	 * Group does not contain any required members.
	 */
	public org.osgi.service.useradmin.Role[] getRequiredMembers() {
		useradmin.checkAlive();
		synchronized (useradmin) {
			if (requiredMembers.isEmpty()) {
				return (null);
			}
			Role[] roles = new Role[requiredMembers.size()];
			requiredMembers.copyInto(roles);
			return (roles);
		}
	}

	/**
	 * Returns the type of this role.
	 *
	 * @return The role's type.
	 */
	public int getType() {
		useradmin.checkAlive();
		return (org.osgi.service.useradmin.Role.GROUP);
	}

	protected boolean isImpliedBy(Role role, Vector checkLoop) {
		if (checkLoop.contains(name)) {
			//we have a circular dependency
			return (false);
		}
		if (name.equals(role.getName())) //A User always implies itself.  A Group is a User.
		{
			return (true);
		}
		checkLoop.addElement(name);
		Vector requiredCheckLoop = (Vector) checkLoop.clone();
		Vector basicCheckLoop = (Vector) checkLoop.clone();
		Enumeration e = requiredMembers.elements();

		//check to see if we imply all of the 0 or more required roles
		Role requiredRole;
		while (e.hasMoreElements()) {
			requiredRole = (Role) e.nextElement();
			if (!requiredRole.isImpliedBy(role, requiredCheckLoop)) {
				return (false);
			}
		}
		//check to see if we imply any of the basic roles (there must be at least one)
		e = basicMembers.elements();
		Role basicRole;
		while (e.hasMoreElements()) {
			basicRole = (Role) e.nextElement();
			if (basicRole.isImpliedBy(role, basicCheckLoop)) {
				return (true);
			}
		}
		return (false);
	}

}

Back to the top