/
SecuritySystem.java
340 lines (309 loc) · 12.7 KB
/
SecuritySystem.java
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
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
/*
* ome.security.SecuritySystem
*
* Copyright 2006 University of Dundee. All rights reserved.
* Use is subject to license terms supplied in LICENSE.txt
*/
package ome.security;
// Java imports
// Third-party libraries
// Application-internal dependencies
import ome.conditions.ApiUsageException;
import ome.conditions.SecurityViolation;
import ome.model.IObject;
import ome.model.internal.Details;
import ome.model.internal.Permissions;
import ome.model.internal.Token;
import ome.model.meta.ExperimenterGroup;
import ome.security.policy.Policy;
import ome.system.EventContext;
import ome.system.Principal;
import ome.system.Roles;
/**
* central security interface. All queries and actions that deal with a secure
* context should pass through an implementation of this interface.
*
* @author Josh Moore, josh.moore at gmx.de
* @version $Revision$, $Date$
* @see Token
* @see Details
* @see Permissions
* @see ACLEventListener
* @since 3.0-M3
*/
public interface SecuritySystem {
// ~ Login/logout
// =========================================================================
/**
* stores this {@link Principal} instance in the current thread context for
* authenticating and authorizing all actions. This method does <em>not</em>
* make any queries and is only a conduit for login information from the
* outermost levels. Session bean implementations and other in-JVM clients
* can fill the {@link Principal}. Note, however, a call must first be made
* to {@link #loadEventContext(boolean)} for some calls to be made to the
* {@link SecuritySystem}. In general, this means that execution must pass
* through the {@link ome.security.basic.EventHandler}
*
* @param principal the new current principal
*/
void login(Principal principal);
/**
* clears the top {@link Principal} instance from the current thread
* context.
*
* @return the number of remaining instances.
*/
int logout();
/**
* Calls {@link #getEventContext(boolean)} with a false as "refresh".
* This is the previous, safer logic of the method since consumers
* are not expecting a long method run.
*
* @return the event context
*/
EventContext getEventContext();
/**
* Returns UID based on whether a share is active, etc. This is the UID
* value that should be used for writing data.
*
* The return value <em>may be</em> null if the user is currently querying
* across multiple contents. In this case another method for
* choosing the UID must be chosen, for example by taking the UID of
* another element under consideration.
*
* For example,
* <pre>
* Annotation toSave = ...;
* if (toSave.getDetails().getOwner() == null) // No owner need to find one.
* {
* Long uid = sec.getEffectiveUID();
* if (uid != null)
* {
* toSave.getDetails().setOwner(new Experimenter(uid, false));
* }
* else
* {
* toSave.getDetails().setOwner(
* image.getDetails().getOwner()); // may be null.
* }
* }
* image.linkAnnotation(toSave);
* etc.
* </pre>
*
* @return the effective user ID
*/
Long getEffectiveUID();
/**
* If refresh is false, returns the current {@link EventContext} stored
* in the session. Otherwise, reloads the context to have the most
* up-to-date information.
* @see <a href="https://trac.openmicroscopy.org/ome/ticket/4011">Trac ticket #4011</a>
*
* @param refresh if the event context should first be reloaded
* @return the event context
*/
EventContext getEventContext(boolean refresh);
/**
* Prepares the current {@link EventContext} instance with the current
* {@link Principal}. An exception is thrown if there is none.
*
* @param isReadOnly
*/
void loadEventContext(boolean isReadOnly);
/**
* Clears the content of the {@link EventContext}so that the
* {@link SecuritySystem} will no longer return true for {@link #isReady()}.
* The {@link Principal} set during {@link #login(Principal)} is retained.
*/
void invalidateEventContext();
// ~ Checks
// =========================================================================
/**
* checks if this {@link SecuritySystem} instance is in a valid state. This
* includes that a user is properly logged in and that a connection is
* available to all necessary resources, e.g. database handle and mapping
* session.
*
* Not all methods require that the instance is ready.
*
* @return true if all methods on this interface are ready to be called.
*/
boolean isReady();
/**
* checks if instances of the given type are "System-Types". Security logic
* for all system types is significantly different. In general, system types
* cannot be created, updated, or deleted by regular users, and are visible
* to all users.
*
* @param klass
* A class which extends from {@link IObject}
* @return true if instances of the class argument can be considered system
* types.
*/
boolean isSystemType(Class<? extends IObject> klass);
/**
* checks that the {@link IObject} argument has been granted a {@link Token}
* by the {@link SecuritySystem}.
*/
boolean hasPrivilegedToken(IObject obj);
/**
* Checks whether or not a {@link Policy} instance of matching
* name has been registered, considers itself active, <em>and</em>
* considers the passed context object to be restricted.
*
* @param name A non-null unique name for a class of policies.
* @param obj An instance which is to be checked against matching policies.
* @throws {@link SecurityViolation} if the given {@link Policy} is
* considered to be restricted.
*/
void checkRestriction(String name, IObject obj) throws SecurityViolation;
// ~ Subsystem disabling
// =========================================================================
/**
* disables components of the backend for the current Thread. Further checks
* to {@link #isDisabled(String)} will return false. It is the
* responsibility of various security system components to then throw
* exceptions.
*
* @param ids
* Non-null, non-empty array of String ids to disable.
*/
void disable(String... ids);
/**
* enables components of the backend for the current Thread. Further checks
* to {@link #isDisabled(String)} will return true.
*
* @param ids
* possibly null array of String ids. A null array specifies that
* all subsystems are to be enabled. Otherwise, only those
* subsystems specified by the ids.
*/
void enable(String... ids);
/**
* checks if the listed id is disabled for the current Thread.
*
* @param id
* non-null String representing a backend subsystem.
* @return true if the backend subsystem has been previously disabled by
* calls to {@link #disable(String[])}
*/
boolean isDisabled(String id);
// ~ Details checking (prime responsibility)
// =========================================================================
/**
* Determines if the current security context has the possibility of
* corrupting consistent graphs. Consistent graphs are enforced by the
* security context to make sure that all READ actions work smoothly. If an
* administrator or PI is logged into a private group, or otherwise may
* create an object linked to an object with lower READ rights, then
* corruption could occur.
*
* Starting with 4.4.2, a trusted details object should be passed in order
* to handle the situation where the current group id is -1. Possibles
* cases that can occur:
*
* <pre>
* The current group is non-negative, then use the previous logic;
* else the current group is negative,
* and the object is in a non-"user" group: USE THAT GROUP;
* else the object is in the "user" group: UNCLEAR
* (for the moment we're throwing an exception)
* </pre>
*
* If no {@link Details} instance is passed or a {@link Details} without
* a {@link ExperimenterGroup} value, then throw as well.
*
* @see <a
* href="https://trac.openmicroscopy.org/ome/ticket/1434>1434</a>
* @see <a
* href="https://trac.openmicroscopy.org/ome/ticket/1769>1769</a>
* @see <a
* href="https://trac.openmicroscopy.org/ome/ticket/9474>9474</a>
* @param details the details
* @return if the graph is critical
*/
boolean isGraphCritical(Details details);
/**
* creates a new secure {@link IObject#getDetails() details} for transient
* entities. Non-privileged users can only edit the
* {@link Details#getPermissions() Permissions} field. Privileged users can
* use the {@link Details} object as a single-step <code>chmod</code> and
* <code>chgrp</code>.
*
* {@link #newTransientDetails(IObject) newTransientDetails} always returns
* a non-null Details that is not equivalent (==) to the Details argument.
*
* This method can be used from anywhere in the codebase to obtain a valid
* {@link Details}, but passing in an {@link IObject} instance with a null
* {@link Details}. However, if the {@link Details} is non-null, there is
* the possibility that this method will throw an exception.
*
* @throws ApiUsageException
* if {@link SecuritySystem} is not {@link #isReady() ready}
* @throws SecurityViolation
* if {@link Details} instance contains illegal values.
*/
Details newTransientDetails(IObject iObject) throws ApiUsageException,
SecurityViolation;
/**
* checks that a non-privileged user has not attempted to edit the entity's
* {@link IObject#getDetails() security details}. Privileged users can set
* fields on {@link Details} as a single-step <code>chmod</code> and
* <code>chgrp</code>.
*
* {@link #checkManagedDetails(IObject, Details) managedDetails} may create
* a new Details instance and return that if needed. If the returned Details
* is not equivalent (==) to the argument Details, then values have been
* changed.
*
* @param iObject
* non-null {@link IObject} instance. {@link Details} for that
* instance can be null.
* @param trustedDetails
* possibly null {@link Details} instance. These {@link Details}
* are trusted in the sense that they have already once passed
* through the {@link SecuritySystem}.
* @throws ApiUsageException
* if {@link SecuritySystem} is not {@link #isReady() ready}
* @throws SecurityViolation
* if {@link Details} instance contains illegal values.
*/
Details checkManagedDetails(IObject iObject, Details trustedDetails)
throws ApiUsageException, SecurityViolation;
// ~ Actions
// =========================================================================
/**
* Allows actions to be performed with the
* {@link EventContext#isCurrentUserAdmin()} flag enabled but
* <em>without</em> changing the value of
* {@link EventContext#getCurrentUserId()}, so that ownerships are properly
* handled. The merging of detached entity graphs should be disabled for the
* extent of the execution.
*
* Due to the addition of the group permission system, we also permit
* setting the group on the call so that the administrator can work within
* all groups. A value of null will not change the current group.
*
* Note: the {@link ome.api.IUpdate} save methods should not be used, since
* they also accept detached entities, which could pose security risks.
* Instead load an entity from the database via {@link ome.api.IQuery},
* make changes, and save the changes with {@link ome.api.IUpdate}.
*
* @param group the group to run the action as
* @param action the action to run
*/
void runAsAdmin(ExperimenterGroup group, AdminAction action);
/**
* Calls {@link #runAsAdmin(ExperimenterGroup, AdminAction)} with a
* null group.
*
* @param action the action to run
*/
void runAsAdmin(AdminAction action);
<T extends IObject> T doAction(SecureAction action, T... objs);
// TODO do these need checks to isReady()?
// ~ Configured Elements
// =========================================================================
Roles getSecurityRoles();
}