-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathSecurityManager.cs
373 lines (341 loc) · 22.8 KB
/
SecurityManager.cs
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
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
using System;
using System.Collections.Generic;
using System.ComponentModel;
using GalleryServerPro.Business.Interfaces;
using System.Globalization;
namespace GalleryServerPro.Business
{
/// <summary>
/// Contains security-related functionality.
/// </summary>
public static class SecurityManager
{
#region Public Static Methods
/// <summary>
/// Throws a <see cref="GalleryServerPro.Events.CustomExceptions.GallerySecurityException" /> if the user belonging to the
/// specified <paramref name="roles" /> does not have at least one of the requested permissions for the specified album.
/// </summary>
/// <param name="securityRequest">Represents the permission or permissions being requested. Multiple actions can be specified by using
/// a bitwise OR between them (example: <see cref="SecurityActions.AdministerSite" /> | <see cref="SecurityActions.AdministerGallery" />).
/// If multiple actions are specified, the method is successful when the user has permission for at least one of the actions.</param>
/// <param name="roles">A collection of Gallery Server roles to which the currently logged-on user belongs. This parameter is ignored
/// for anonymous users (i.e. <paramref name="isAuthenticated" />=false). The parameter may be null.</param>
/// <param name="albumId">The album for which the requested permission applies.</param>
/// <param name="galleryId">The ID for the gallery the user is requesting permission in. The <paramref name="albumId" /> must exist in
/// this gallery. This parameter is not required <paramref name="securityRequest" /> is SecurityActions.AdministerSite (you can specify
/// <see cref="int.MinValue" />).</param>
/// <param name="isAuthenticated">A value indicating whether the current user is logged in. If true, the
/// <paramref name="roles" /> parameter must be given the names of the roles for the current user. If
/// <paramref name="isAuthenticated" />=true and the <paramref name="roles" /> parameter
/// is either null or an empty collection, this method thows a <see cref="GalleryServerPro.Events.CustomExceptions.GallerySecurityException" /> exception.</param>
/// <param name="isPrivateAlbum">A value indicating whether the album is hidden from anonymous users. This parameter is ignored for
/// logged-on users.</param>
/// <param name="isVirtualAlbum">if set to <c>true</c> the album is a virtual album.</param>
/// <remarks>
/// This method handles both anonymous and logged on users. Note that when <paramref name="isAuthenticated" />=true, the <paramref name="isPrivateAlbum" /> parameter is
/// ignored. When it is false, the <paramref name="roles" /> parameter is ignored.
/// </remarks>
/// <exception cref="Events.CustomExceptions.GallerySecurityException">Thrown when user is not authorized.</exception>
public static void ThrowIfUserNotAuthorized(SecurityActions securityRequest, IGalleryServerRoleCollection roles, int albumId, int galleryId, bool isAuthenticated, bool isPrivateAlbum, bool isVirtualAlbum)
{
if (!(IsUserAuthorized(securityRequest, roles, albumId, galleryId, isAuthenticated, isPrivateAlbum, SecurityActionsOption.RequireOne, isVirtualAlbum)))
{
throw new Events.CustomExceptions.GallerySecurityException(String.Format(CultureInfo.CurrentCulture, "You do not have permission '{0}' for album ID {1}.", securityRequest.ToString(), albumId));
}
}
/// <overloads>
/// Determine if a user has permission to perform the requested action.
/// </overloads>
/// <summary>
/// Determine whether the user belonging to the specified <paramref name="roles" /> has permission to perform at least one of the specified security
/// actions against the specified <paramref name="albumId" />. The user may be anonymous or logged on.
/// When the the user is logged on (i.e. <paramref name="isAuthenticated"/> = true), this method determines whether the user is authorized by
/// validating that at least one role has the requested permission to the specified album. When the user is anonymous,
/// the <paramref name="roles"/> parameter is ignored and instead the <paramref name="isPrivateAlbum"/> parameter is used.
/// Anonymous users do not have any access to private albums. When the the user is logged on (i.e. <paramref name="isAuthenticated"/> = true),
/// the <paramref name="roles"/> parameter must contain the roles belonging to the user.
/// </summary>
/// <param name="securityRequests">Represents the permission or permissions being requested. Multiple actions can be specified by using
/// a bitwise OR between them (example: <see cref="SecurityActions.AdministerSite" /> | <see cref="SecurityActions.AdministerGallery" />).
/// If multiple actions are specified, the method is successful if the user has permission for at least one of the actions. If you require
/// that all actions be satisfied to be successful, call one of the overloads that accept a <see cref="SecurityActionsOption" /> and
/// specify <see cref="SecurityActionsOption.RequireAll" />.</param>
/// <param name="roles">A collection of Gallery Server roles to which the currently logged-on user belongs. This parameter is ignored
/// for anonymous users (i.e. <paramref name="isAuthenticated"/>=false). The parameter may be null.</param>
/// <param name="albumId">The album for which the requested permission applies. This parameter does not apply when the requested permission
/// is <see cref="SecurityActions.AdministerSite" />.</param>
/// <param name="galleryId">The ID for the gallery the user is requesting permission in. The <paramref name="albumId" /> must exist in this
/// gallery. This parameter is not required <paramref name="securityRequests" /> is SecurityActions.AdministerSite (you can specify
/// <see cref="int.MinValue" />).</param>
/// <param name="isAuthenticated">A value indicating whether the current user is logged on. If true, the
/// <paramref name="roles"/> parameter should contain the names of the roles for the current user. If <paramref name="isAuthenticated"/>=true
/// and the <paramref name="roles"/> parameter is either null or an empty collection, this method returns false.</param>
/// <param name="isPrivateAlbum">A value indicating whether the album is hidden from anonymous users. This parameter is ignored for
/// logged-on users.</param>
/// <param name="isVirtualAlbum">if set to <c>true</c> the album is a virtual album.</param> ///
/// <returns>
/// Returns true if the user has the requested permission; returns false if not.
/// </returns>
/// <remarks>This method handles both anonymous and logged on users. Note that when <paramref name="isAuthenticated"/>=true, the
/// <paramref name="isPrivateAlbum"/> parameter is ignored. When it is false, the <paramref name="roles" /> parameter is ignored.</remarks>
public static bool IsUserAuthorized(SecurityActions securityRequests, IGalleryServerRoleCollection roles, int albumId, int galleryId, bool isAuthenticated, bool isPrivateAlbum, bool isVirtualAlbum)
{
return IsUserAuthorized(securityRequests, roles, albumId, galleryId, isAuthenticated, isPrivateAlbum, SecurityActionsOption.RequireOne, isVirtualAlbum);
}
/// <summary>
/// Determine whether the user belonging to the specified <paramref name="roles" /> is a site administrator. The user is considered a site
/// administrator if at least one role has Allow Administer Site permission.
/// </summary>
/// <param name="roles">A collection of Gallery Server roles to which the currently logged-on user belongs. The parameter may be null.</param>
/// <returns>
/// <c>true</c> if the user is a site administrator; otherwise, <c>false</c>.
/// </returns>
public static bool IsUserSiteAdministrator(IGalleryServerRoleCollection roles)
{
return IsUserAuthorized(SecurityActions.AdministerSite, roles, int.MinValue, int.MinValue, true, false, false);
}
/// <summary>
/// Determine whether the user belonging to the specified <paramref name="roles" /> is a gallery administrator for the specified
/// <paramref name="galleryId" />. The user is considered a gallery administrator if at least one role has Allow Administer Gallery permission.
/// </summary>
/// <param name="roles">A collection of Gallery Server roles to which the currently logged-on user belongs. The parameter may be null.</param>
/// <param name="galleryId">The gallery ID.</param>
/// <returns>
/// <c>true</c> if the user is a gallery administrator; otherwise, <c>false</c>.
/// </returns>
public static bool IsUserGalleryAdministrator(IGalleryServerRoleCollection roles, int galleryId)
{
return IsUserAuthorized(SecurityActions.AdministerGallery, roles, int.MinValue, galleryId, true, false, false);
}
/// <summary>
/// Determine whether the user belonging to the specified <paramref name="roles" /> has permission to perform all of the specified security
/// actions against the specified <paramref name="albumId" />. The user may be anonymous or logged on.
/// When the the user is logged on (i.e. <paramref name="isAuthenticated" /> = true), this method determines whether the user is authorized by
/// validating that at least one role has the requested permission to the specified album. When the user is anonymous,
/// the <paramref name="roles" /> parameter is ignored and instead the <paramref name="isPrivateAlbum" /> parameter is used.
/// Anonymous users do not have any access to private albums. When the the user is logged on (i.e. <paramref name="isAuthenticated" /> = true),
/// the <paramref name="roles" /> parameter must contain the roles belonging to the user.
/// </summary>
/// <param name="securityRequests">Represents the permission or permissions being requested. Multiple actions can be specified by using
/// a bitwise OR between them (example: <see cref="SecurityActions.AdministerSite" /> | <see cref="SecurityActions.AdministerGallery" />).
/// If multiple actions are specified, use <paramref name="secActionsOption" /> to specify whether all of the actions must be satisfied
/// to be successful or only one item must be satisfied.</param>
/// <param name="roles">A collection of Gallery Server roles to which the currently logged-on user belongs. This parameter is ignored
/// for anonymous users (i.e. <paramref name="isAuthenticated" />=false). The parameter may be null.</param>
/// <param name="albumId">The album for which the requested permission applies. This parameter does not apply when the requested permission
/// is <see cref="SecurityActions.AdministerSite" /> or <see cref="SecurityActions.AdministerGallery" />.</param>
/// <param name="galleryId">The ID for the gallery the user is requesting permission in. The <paramref name="albumId" /> must exist in this
/// gallery. This parameter is not required <paramref name="securityRequests" /> is SecurityActions.AdministerSite (you can specify
/// <see cref="int.MinValue" />).</param>
/// <param name="isAuthenticated">A value indicating whether the current user is logged on. If true, the
/// <paramref name="roles" /> parameter should contain the names of the roles for the current user. If <paramref name="isAuthenticated" />=true
/// and the <paramref name="roles" /> parameter is either null or an empty collection, this method returns false.</param>
/// <param name="isPrivateAlbum">A value indicating whether the album is hidden from anonymous users. This parameter is ignored for
/// logged-on users.</param>
/// <param name="secActionsOption">Specifies whether the user must have permission for all items in <paramref name="securityRequests" />
/// to be successful or just one. This parameter defaults to SecurityActionsOption.RequireAll when not specified, and is applicable only
/// when <paramref name="securityRequests" /> contains more than one item.</param>
/// <param name="isVirtualAlbum">if set to <c>true</c> the album is a virtual album.</param>
/// <returns>
/// Returns true if the user has the requested permission; returns false if not.
/// </returns>
/// <exception cref="System.ArgumentOutOfRangeException"></exception>
/// <exception cref="System.ComponentModel.InvalidEnumArgumentException"></exception>
/// <remarks>
/// This method handles both anonymous and logged on users. Note that when <paramref name="isAuthenticated" />=true, the
/// <paramref name="isPrivateAlbum" /> parameter is ignored. When it is false, the <paramref name="roles" /> parameter is ignored.
/// </remarks>
public static bool IsUserAuthorized(SecurityActions securityRequests, IGalleryServerRoleCollection roles, int albumId, int galleryId, bool isAuthenticated, bool isPrivateAlbum, SecurityActionsOption secActionsOption, bool isVirtualAlbum)
{
#region Validation
if (isAuthenticated && !isVirtualAlbum && ((roles == null) || (roles.Count == 0)))
return false;
var userIsRequestingSysAdminPermission = (securityRequests & SecurityActions.AdministerSite) == SecurityActions.AdministerSite;
var userIsRequestingGalleryAdminPermission = (securityRequests & SecurityActions.AdministerGallery) == SecurityActions.AdministerGallery;
if (galleryId == int.MinValue)
{
var isMoreThanOnePermissionRequest = !SecurityActionEnumHelper.IsSingleSecurityAction(securityRequests);
if (isMoreThanOnePermissionRequest || !userIsRequestingSysAdminPermission)
{
throw new ArgumentOutOfRangeException("galleryId", String.Format(CultureInfo.CurrentCulture, "A valid gallery ID must be specified. Instead, the value was {0}.", galleryId));
}
}
#endregion
if (isVirtualAlbum && (!userIsRequestingSysAdminPermission && !userIsRequestingGalleryAdminPermission))
{
return true; // Virtual albums are always allowed, but only for non-admin requests. This feels hacky and non-intuitive; should try to improve someday
}
// Handle anonymous users.
if (!isAuthenticated)
{
return IsAnonymousUserAuthorized(securityRequests, isPrivateAlbum, galleryId, secActionsOption);
}
// If we get here we are dealing with an authenticated (logged on) user. Authorization for authenticated users is
// given if the user is a member of at least one role that provides permission.
if (SecurityActionEnumHelper.IsSingleSecurityAction(securityRequests))
{
// Iterate through each GalleryServerRole. If at least one allows the action, return true. Note that the
// AdministerSite security action, if granted, applies to all albums and allows all actions (except HideWatermark).
foreach (IGalleryServerRole role in roles)
{
if (IsAuthenticatedUserAuthorized(securityRequests, role, albumId, galleryId))
return true;
}
return false;
}
else
{
// There are multiple security actions in securityRequest enum. Iterate through each one and determine if the user
// has permission for it.
List<bool> authResults = new List<bool>();
foreach (SecurityActions securityAction in SecurityActionEnumHelper.ParseSecurityAction(securityRequests))
{
// Iterate through each role. If at least one role allows the action, permission is granted.
foreach (IGalleryServerRole role in roles)
{
bool authResult = IsAuthenticatedUserAuthorized(securityAction, role, albumId, galleryId);
authResults.Add(authResult);
if (authResult)
break; // We found a role that provides permission, so no need to check the other roles. Just move on to the next security request.
}
}
// Determine the return value based on what the calling method wanted.
if (secActionsOption == SecurityActionsOption.RequireAll)
{
return (authResults.Count > 0 ? authResults.TrueForAll(delegate(bool value) { return value; }) : false);
}
else if (secActionsOption == SecurityActionsOption.RequireOne)
{
return authResults.Contains(true);
}
else
{
throw new InvalidEnumArgumentException("secActionsOption", (int)secActionsOption, typeof(SecurityActionsOption));
}
}
}
/// <summary>
/// Gets an object describing whether a user having the specified <paramref name="roles" /> has permission to add albums
/// and media objects to at least one album in the gallery having ID <paramref name="galleryId" />. Item1 in the return
/// value indicates permission to add an album. Item2 represents permission to add a media object. This method works
/// by iterating through the roles and looking for <see cref="IGalleryServerRole.AllowAddMediaObject" /> and
/// <see cref="IGalleryServerRole.AllowAddChildAlbum" /> permission, so it is quite efficient.
/// </summary>
/// <param name="roles">The roles a user belongs to.</param>
/// <param name="galleryId">The gallery ID.</param>
/// <returns>Tuple{System.BooleanSystem.Boolean}.</returns>
public static Tuple<bool, bool> GetUserAddObjectPermissions(IEnumerable<IGalleryServerRole> roles, int galleryId)
{
var userCanAddAlbumToAtLeastOneAlbum = false;
var userCanAddMediaObjectToAtLeastOneAlbum = false;
var gallery = Factory.LoadGallery(galleryId);
foreach (var role in roles)
{
if (role.Galleries.Contains(gallery))
{
if (role.AllowAdministerSite)
{
userCanAddMediaObjectToAtLeastOneAlbum = true;
userCanAddAlbumToAtLeastOneAlbum = true;
break;
}
if (role.AllowAddMediaObject)
userCanAddMediaObjectToAtLeastOneAlbum = true;
if (role.AllowAddChildAlbum)
userCanAddAlbumToAtLeastOneAlbum = true;
}
}
return new Tuple<bool, bool>(userCanAddAlbumToAtLeastOneAlbum, userCanAddMediaObjectToAtLeastOneAlbum);
}
#endregion
#region Private Static Methods
private static bool IsAnonymousUserAuthorized(SecurityActions securityRequests, bool isPrivateAlbum, int galleryId, SecurityActionsOption secActionsOption)
{
// Anonymous user. Return true for viewing-related permission requests on PUBLIC albums; return false for all others.
IGallerySettings gallerySettings = Factory.LoadGallerySetting(galleryId);
if (SecurityActionEnumHelper.IsSingleSecurityAction(securityRequests))
{
return IsAnonymousUserAuthorizedForSingleSecurityAction(securityRequests, isPrivateAlbum, gallerySettings);
}
else
{
return IsAnonymousUserAuthorizedForMultipleSecurityActions(securityRequests, isPrivateAlbum, gallerySettings, secActionsOption);
}
}
private static bool IsAnonymousUserAuthorizedForSingleSecurityAction(SecurityActions securityRequests, bool isPrivateAlbum, IGallerySettings gallerySettings)
{
return (securityRequests == SecurityActions.ViewAlbumOrMediaObject) && !isPrivateAlbum && gallerySettings.AllowAnonymousBrowsing ||
(securityRequests == SecurityActions.ViewOriginalMediaObject) && !isPrivateAlbum && gallerySettings.AllowAnonymousBrowsing && gallerySettings.EnableAnonymousOriginalMediaObjectDownload;
}
private static bool IsAnonymousUserAuthorizedForMultipleSecurityActions(SecurityActions securityRequests, bool isPrivateAlbum, IGallerySettings gallerySettings, SecurityActionsOption secActionsOption)
{
// There are multiple security actions in securityAction enum. Iterate through each one and determine if the user
// has permission for it.
List<bool> authResults = new List<bool>();
foreach (SecurityActions securityAction in SecurityActionEnumHelper.ParseSecurityAction(securityRequests))
{
authResults.Add(IsAnonymousUserAuthorizedForSingleSecurityAction(securityAction, isPrivateAlbum, gallerySettings));
}
if (secActionsOption == SecurityActionsOption.RequireAll)
{
return (authResults.Count > 0 ? authResults.TrueForAll(delegate(bool value) { return value; }) : false);
}
else if (secActionsOption == SecurityActionsOption.RequireOne)
{
return authResults.Contains(true);
}
else
{
throw new InvalidEnumArgumentException("secActionsOption", (int)secActionsOption, typeof(SecurityActionsOption));
}
}
private static bool IsAuthenticatedUserAuthorized(SecurityActions securityRequest, IGalleryServerRole role, int albumId, int galleryId)
{
if (role.AllowAdministerSite && (securityRequest != SecurityActions.HideWatermark))
{
// Administer permissions imply permissions to carry out all other actions, except for hide watermark, which is more of
// a preference assigned to the user.
return true;
}
switch (securityRequest)
{
case SecurityActions.AdministerSite: if (role.AllowAdministerSite) return true; break;
case SecurityActions.AdministerGallery: if (role.AllowAdministerGallery && (role.Galleries.FindById(galleryId) != null)) return true; break;
case SecurityActions.ViewAlbumOrMediaObject: if (role.AllowViewAlbumOrMediaObject && role.AllAlbumIds.Contains(albumId)) return true; break;
case SecurityActions.ViewOriginalMediaObject: if (role.AllowViewOriginalImage && role.AllAlbumIds.Contains(albumId)) return true; break;
case SecurityActions.AddChildAlbum: if (role.AllowAddChildAlbum && role.AllAlbumIds.Contains(albumId)) return true; break;
case SecurityActions.AddMediaObject: if (role.AllowAddMediaObject && role.AllAlbumIds.Contains(albumId)) return true; break;
case SecurityActions.DeleteAlbum:
{
// It is OK to delete the album if the AllowDeleteChildAlbum permission is true and one of the following is true:
// 1. The album is the root album and its ID is in the list of targeted albums (Note that we never actually delete the root album.
// Instead, we delete all objects within the album. But the idea of deleting the top level album to clear out all objects in the
// gallery is useful to the user.)
// 2. The album is not the root album and its parent album's ID is in the list of targeted albums.
if (role.AllowDeleteChildAlbum)
{
IAlbum album = Factory.LoadAlbumInstance(albumId, false);
if (album.IsRootAlbum)
{
if (role.AllAlbumIds.Contains(album.Id)) return true; break;
}
else
{
if (role.AllAlbumIds.Contains(album.Parent.Id)) return true; break;
}
}
break;
}
case SecurityActions.DeleteChildAlbum: if (role.AllowDeleteChildAlbum && role.AllAlbumIds.Contains(albumId)) return true; break;
case SecurityActions.DeleteMediaObject: if (role.AllowDeleteMediaObject && role.AllAlbumIds.Contains(albumId)) return true; break;
case SecurityActions.EditAlbum: if (role.AllowEditAlbum && role.AllAlbumIds.Contains(albumId)) return true; break;
case SecurityActions.EditMediaObject: if (role.AllowEditMediaObject && role.AllAlbumIds.Contains(albumId)) return true; break;
case SecurityActions.HideWatermark: if (role.HideWatermark && role.AllAlbumIds.Contains(albumId)) return true; break;
case SecurityActions.Synchronize: if (role.AllowSynchronize && role.AllAlbumIds.Contains(albumId)) return true; break;
default: throw new GalleryServerPro.Events.CustomExceptions.BusinessException(String.Format(CultureInfo.CurrentCulture, "The IsUserAuthorized function is not designed to handle the {0} SecurityActions. It must be updated by a developer.", securityRequest.ToString()));
}
return false;
}
#endregion
}
}