DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
PrivilegeManager.cs
Go to the documentation of this file.
1 //
2 // Copyright 2010-2015 Deveel
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
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 
17 using System;
18 using System.Collections.Generic;
19 using System.Linq;
20 
21 using Deveel.Data.Services;
22 using Deveel.Data.Sql;
24 using Deveel.Data.Sql.Tables;
25 
26 namespace Deveel.Data.Security {
27  public class PrivilegeManager : IPrivilegeManager/*, IResolveCallback*/ {
28  private Dictionary<GrantCacheKey, Privileges> grantsCache;
29  private Dictionary<string, Privileges> groupsPrivilegesCache;
30 
31  public PrivilegeManager(IQuery queryContext) {
32  QueryContext = queryContext;
33  }
34 
36  Dispose(false);
37  }
38 
39  public IQuery QueryContext { get; private set; }
40 
41  public void Dispose() {
42  Dispose(true);
43  GC.SuppressFinalize(this);
44  }
45 
46  //void IResolveCallback.OnResolved(IResolveScope scope) {
47  // var context = scope as IQueryContext;
48  // if (context == null)
49  // throw new InvalidOperationException("Privilege manager resolved outside the scope of the query context.");
50 
51  // QueryContext = context;
52  //}
53 
54  protected virtual void Dispose(bool disposing) {
55  QueryContext = null;
56  }
57 
58  private static void UpdateGrants(IQuery queryContext, IMutableTable grantTable, DbObjectType objectType, ObjectName objectName,
59  string granter, string grantee, Privileges privileges, bool withOption) {
60  RevokeAllGrants(queryContext, grantTable, objectType, objectName, granter, grantee, withOption);
61 
62  if (privileges != Privileges.None) {
63  // Add the grant to the grants table.
64  var row = grantTable.NewRow();
65  row.SetValue(0, (int)privileges);
66  row.SetValue(1, (int)objectType);
67  row.SetValue(2, objectName.FullName);
68  row.SetValue(3, grantee);
69  row.SetValue(4, withOption);
70  row.SetValue(5, granter);
71  grantTable.AddRow(row);
72  }
73  }
74 
75  private static void RevokeAllGrants(IQuery queryContext, IMutableTable grantTable, DbObjectType objectType, ObjectName objectName, string revoker, string user, bool withOption = false) {
76  var objectCol = grantTable.GetResolvedColumnName(1);
77  var paramCol = grantTable.GetResolvedColumnName(2);
78  var granteeCol = grantTable.GetResolvedColumnName(3);
79  var grantOptionCol = grantTable.GetResolvedColumnName(4);
80  var granterCol = grantTable.GetResolvedColumnName(5);
81 
82  ITable t1 = grantTable;
83 
84  // All that match the given object parameter
85  // It's most likely this will reduce the search by the most so we do
86  // it first.
87  t1 = t1.SimpleSelect(queryContext, paramCol, SqlExpressionType.Equal,
89 
90  // The next is a single exhaustive select through the remaining records.
91  // It finds all grants that match either public or the grantee is the
92  // username, and that match the object type.
93 
94  // Expression: ("grantee_col" = username)
95  var userCheck = SqlExpression.Equal(SqlExpression.Reference(granteeCol),
97 
98  // Expression: ("object_col" = object AND
99  // "grantee_col" = username)
100  // All that match the given username or public and given object
101  var expr =
104  SqlExpression.Constant(DataObject.BigInt((int)objectType))), userCheck);
105 
106  // Are we only searching for grant options?
107  var grantOptionCheck = SqlExpression.Equal(SqlExpression.Reference(grantOptionCol),
109  expr = SqlExpression.And(expr, grantOptionCheck);
110 
111  // Make sure the granter matches up also
112  var granterCheck = SqlExpression.Equal(SqlExpression.Reference(granterCol),
114  expr = SqlExpression.And(expr, granterCheck);
115 
116  t1 = t1.ExhaustiveSelect(queryContext, expr);
117 
118  // Remove these rows from the table
119  grantTable.Delete(t1);
120  }
121 
122 
123  private void UpdateUserGrants(DbObjectType objectType, ObjectName objectName, string granter, string grantee, Privileges privileges, bool withOption) {
124  var grantTable = QueryContext.GetMutableTable(SystemSchema.UserGrantsTableName);
125 
126  try {
127  UpdateGrants(QueryContext, grantTable, objectType, objectName, granter, grantee, privileges, withOption);
128  } finally {
129  ClearUserGrantsCache(grantee, objectType, objectName, withOption, true);
130  }
131  }
132 
133  private void ClearUserGrantsCache(string userName, DbObjectType objectType, ObjectName objectName, bool withOption, bool withPublic) {
134  if (grantsCache == null)
135  return;
136 
137  var key = new GrantCacheKey(userName, objectType, objectName.FullName, withOption, withPublic);
138  grantsCache.Remove(key);
139  }
140 
141  private void ClearUserGrantsCache(string userName) {
142  if (grantsCache == null)
143  return;
144 
145  var keys = grantsCache.Keys.Where(x => x.userName.Equals(userName, StringComparison.OrdinalIgnoreCase));
146  foreach (var key in keys) {
147  grantsCache.Remove(key);
148  }
149  }
150 
151  public void GrantToUser(string userName, Grant grant) {
152  if (String.IsNullOrEmpty(userName))
153  throw new ArgumentNullException("userName");
154  if (grant == null)
155  throw new ArgumentNullException("grant");
156 
157  var objectType = grant.ObjectType;
158  var objectName = grant.ObjectName;
159  var privileges = grant.Privileges;
160 
161  Privileges oldPrivs = GetUserPrivileges(userName, objectType, objectName, grant.WithOption);
162  privileges |= oldPrivs;
163 
164  if (!oldPrivs.Equals(privileges))
165  UpdateUserGrants(objectType, objectName, grant.GranterName, userName, privileges, grant.WithOption);
166  }
167 
168  private bool TryGetPrivilegesFromCache(string userName, DbObjectType objectType, ObjectName objectName, bool withOption, bool withPublic,
169  out Privileges privileges) {
170  if (grantsCache == null) {
171  privileges = Privileges.None;
172  return false;
173  }
174 
175  var key = new GrantCacheKey(userName, objectType, objectName.FullName, withOption, withPublic);
176  return grantsCache.TryGetValue(key, out privileges);
177  }
178 
179  private void SetPrivilegesInCache(string userName, DbObjectType objectType, ObjectName objectName, bool withOption, bool withPublic,
180  Privileges privileges) {
181  var key = new GrantCacheKey(userName, objectType, objectName.FullName, withOption, withPublic);
182  if (grantsCache == null)
183  grantsCache = new Dictionary<GrantCacheKey, Privileges>();
184 
185  grantsCache[key] = privileges;
186  }
187 
188  private void RevokeAllGrantsFromUser(DbObjectType objectType, ObjectName objectName, string revoker, string user, bool withOption = false) {
189  var grantTable = QueryContext.GetMutableTable(SystemSchema.UserGrantsTableName);
190 
191  var objectCol = grantTable.GetResolvedColumnName(1);
192  var paramCol = grantTable.GetResolvedColumnName(2);
193  var granteeCol = grantTable.GetResolvedColumnName(3);
194  var grantOptionCol = grantTable.GetResolvedColumnName(4);
195  var granterCol = grantTable.GetResolvedColumnName(5);
196 
197  ITable t1 = grantTable;
198 
199  // All that match the given object parameter
200  // It's most likely this will reduce the search by the most so we do
201  // it first.
202  t1 = t1.SimpleSelect(QueryContext, paramCol, SqlExpressionType.Equal,
204 
205  // The next is a single exhaustive select through the remaining records.
206  // It finds all grants that match either public or the grantee is the
207  // username, and that match the object type.
208 
209  // Expression: ("grantee_col" = username)
210  var userCheck = SqlExpression.Equal(SqlExpression.Reference(granteeCol),
212 
213  // Expression: ("object_col" = object AND
214  // "grantee_col" = username)
215  // All that match the given username or public and given object
216  var expr =
219  SqlExpression.Constant(DataObject.BigInt((int)objectType))), userCheck);
220 
221  // Are we only searching for grant options?
222  var grantOptionCheck = SqlExpression.Equal(SqlExpression.Reference(grantOptionCol),
224  expr = SqlExpression.And(expr, grantOptionCheck);
225 
226  // Make sure the granter matches up also
227  var granterCheck = SqlExpression.Equal(SqlExpression.Reference(granterCol),
229  expr = SqlExpression.And(expr, granterCheck);
230 
231  t1 = t1.ExhaustiveSelect(QueryContext, expr);
232 
233  // Remove these rows from the table
234  grantTable.Delete(t1);
235  }
236 
237  private static Privileges QueryPrivileges(IQuery queryContext, ITable grantTable, string grantee,
238  DbObjectType objectType, ObjectName objectName, bool withOption, bool withPublic) {
239  var objectCol = grantTable.GetResolvedColumnName(1);
240  var paramCol = grantTable.GetResolvedColumnName(2);
241  var granteeCol = grantTable.GetResolvedColumnName(3);
242  var grantOptionCol = grantTable.GetResolvedColumnName(4);
243  var granterCol = grantTable.GetResolvedColumnName(5);
244 
245  ITable t1 = grantTable;
246 
247  // All that match the given object parameter
248  // It's most likely this will reduce the search by the most so we do
249  // it first.
250  t1 = t1.SimpleSelect(queryContext, paramCol, SqlExpressionType.Equal, SqlExpression.Constant(DataObject.String(objectName.FullName)));
251 
252  // The next is a single exhaustive select through the remaining records.
253  // It finds all grants that match either public or the grantee is the
254  // username, and that match the object type.
255 
256  // Expression: ("grantee_col" = username OR "grantee_col" = 'public')
257  var userCheck = SqlExpression.Equal(SqlExpression.Reference(granteeCol), SqlExpression.Constant(DataObject.String(grantee)));
258  if (withPublic) {
259  userCheck = SqlExpression.Or(userCheck, SqlExpression.Equal(SqlExpression.Reference(granteeCol),
261  }
262 
263  // Expression: ("object_col" = object AND
264  // ("grantee_col" = username OR "grantee_col" = 'public'))
265  // All that match the given username or public and given object
267  SqlExpression.Constant(DataObject.BigInt((int)objectType))), userCheck);
268 
269  // Are we only searching for grant options?
270  if (withOption) {
271  var grantOptionCheck = SqlExpression.Equal(SqlExpression.Reference(grantOptionCol),
273  expr = SqlExpression.And(expr, grantOptionCheck);
274  }
275 
276  t1 = t1.ExhaustiveSelect(queryContext, expr);
277 
278  // For each grant, merge with the resultant priv object
279  Privileges privs = Privileges.None;
280 
281  foreach (var row in t1) {
282  var priv = (int)row.GetValue(0).AsBigInt();
283  privs |= (Privileges)priv;
284  }
285 
286  return privs;
287  }
288 
289  private Privileges QueryUserPrivileges(string userName, DbObjectType objectType, ObjectName objectName,
290  bool withOption, bool withPublic) {
291  // The system grants table.
292  var grantTable = QueryContext.GetTable(SystemSchema.UserGrantsTableName);
293  return QueryPrivileges(QueryContext, grantTable, userName, objectType, objectName, withOption, withPublic);
294  }
295 
296  private Privileges QueryGroupPrivileges(string groupName, DbObjectType objectType, ObjectName objectName,
297  bool withOption, bool withPublic) {
298  var grantTable = QueryContext.GetTable(SystemSchema.GroupGrantsTable);
299  return QueryPrivileges(QueryContext, grantTable, groupName, objectType, objectName, withOption, withPublic);
300  }
301 
302  public Privileges GetUserPrivileges(string userName, DbObjectType objectType, ObjectName objectName, bool withOption) {
303  Privileges privs;
304  if (!TryGetPrivilegesFromCache(userName, objectType, objectName, withOption, true, out privs)) {
305  privs = QueryUserPrivileges(userName, objectType, objectName, withOption, true);
306  SetPrivilegesInCache(userName, objectType, objectName, withOption, true, privs);
307  }
308 
309  return privs;
310  }
311 
312  public void RevokeFromUser(string userName, Grant grant) {
313  if (String.IsNullOrEmpty(userName))
314  throw new ArgumentNullException("userName");
315 
316  try {
317  RevokeAllGrantsFromUser(grant.ObjectType, grant.ObjectName, grant.GranterName, userName, grant.WithOption);
318  } finally {
319  ClearUserGrantsCache(userName, grant.ObjectType, grant.ObjectName, grant.WithOption, false);
320  }
321  }
322 
323  public void GrantToGroup(string groupName, Grant grant) {
324  throw new NotImplementedException();
325  }
326 
327  public void RevokeFromGroup(string groupName, Grant grant) {
328  throw new NotImplementedException();
329  }
330 
331  public Privileges GetGroupPrivileges(string groupName, DbObjectType objectType, ObjectName objectName) {
332  Privileges privileges;
333  if (!TryGetPrivilegesFromCache(groupName, objectType, objectName, false, false, out privileges)) {
334  privileges = QueryGroupPrivileges(groupName, objectType, objectName, false, false);
335  SetPrivilegesInCache(groupName, objectType, objectName, false, false, privileges);
336  }
337 
338  return privileges;
339  }
340 
341  #region GrantCacheKey
342 
343  class GrantCacheKey : IEquatable<GrantCacheKey> {
344  public readonly string userName;
345  private readonly DbObjectType objectType;
346  private readonly string objectName;
347  private readonly int options;
348 
349  public GrantCacheKey(string userName, DbObjectType objectType, string objectName, bool withOption, bool withPublic) {
350  this.userName = userName;
351  this.objectType = objectType;
352  this.objectName = objectName;
353 
354  options = 0;
355  if (withOption)
356  options++;
357  if (withPublic)
358  options++;
359  }
360 
361  public override bool Equals(object obj) {
362  var other = obj as GrantCacheKey;
363  return Equals(other);
364  }
365 
366  public override int GetHashCode() {
367  return unchecked(((userName.GetHashCode() * objectName.GetHashCode()) ^ (int)objectType) + options);
368  }
369 
370  public bool Equals(GrantCacheKey other) {
371  if (other == null)
372  return false;
373 
374  if (!String.Equals(userName, other.userName, StringComparison.OrdinalIgnoreCase))
375  return false;
376 
377  if (objectType != other.objectType)
378  return false;
379 
380  if (!String.Equals(objectName, other.objectName, StringComparison.OrdinalIgnoreCase))
381  return false;
382 
383  if (options != other.options)
384  return false;
385 
386  return true;
387  }
388  }
389 
390  #endregion
391  }
392 }
Defines the contract to access the data contained into a table of a database.
Definition: ITable.cs:40
void GrantToUser(string userName, Grant grant)
GrantCacheKey(string userName, DbObjectType objectType, string objectName, bool withOption, bool withPublic)
Privileges QueryGroupPrivileges(string groupName, DbObjectType objectType, ObjectName objectName, bool withOption, bool withPublic)
static SqlBinaryExpression And(SqlExpression left, SqlExpression right)
Privileges Privileges
Gets the access privileges granted to the user.
Definition: Grant.cs:102
const string PublicName
The name of the PUBLIC special user.
Definition: User.cs:47
static readonly ObjectName GroupGrantsTable
Privileges QueryUserPrivileges(string userName, DbObjectType objectType, ObjectName objectName, bool withOption, bool withPublic)
Privileges GetGroupPrivileges(string groupName, DbObjectType objectType, ObjectName objectName)
static SqlBinaryExpression Equal(SqlExpression left, SqlExpression right)
Describes the name of an object within a database.
Definition: ObjectName.cs:44
static readonly DataObject BooleanTrue
The representation of a BOOLEAN true as DataObject
Definition: DataObject.cs:39
static void RevokeAllGrants(IQuery queryContext, IMutableTable grantTable, DbObjectType objectType, ObjectName objectName, string revoker, string user, bool withOption=false)
void GrantToGroup(string groupName, Grant grant)
static DataObject String(string s)
Definition: DataObject.cs:592
static DataObject Boolean(SqlBoolean value)
Definition: DataObject.cs:544
DataObject AsBigInt()
Definition: DataObject.cs:524
DataObject GetValue(long rowNumber, int columnOffset)
Gets a single cell within the table that is located at the given column offset and row...
SqlExpressionType
All the possible type of SqlExpression supported
bool TryGetPrivilegesFromCache(string userName, DbObjectType objectType, ObjectName objectName, bool withOption, bool withPublic, out Privileges privileges)
string GranterName
Gets the name of the user that provided this grant.
Definition: Grant.cs:68
void RevokeFromUser(string userName, Grant grant)
static SqlBinaryExpression Or(SqlExpression left, SqlExpression right)
ObjectName ObjectName
Gets the fully qualified name of the object on which this grant provides access privileges to the use...
Definition: Grant.cs:91
RowId AddRow(Row row)
Persists a new row to the table.
void UpdateUserGrants(DbObjectType objectType, ObjectName objectName, string granter, string grantee, Privileges privileges, bool withOption)
Represents a dynamic object that encapsulates a defined SqlType and a compatible constant ISqlObject ...
Definition: DataObject.cs:35
void ClearUserGrantsCache(string userName)
Provides utilities and properties for handling the SYSTEN schema of a database.
Definition: SystemSchema.cs:37
string FullName
Gets the full reference name formatted.
Definition: ObjectName.cs:114
virtual void Dispose(bool disposing)
void SetPrivilegesInCache(string userName, DbObjectType objectType, ObjectName objectName, bool withOption, bool withPublic, Privileges privileges)
void RevokeAllGrantsFromUser(DbObjectType objectType, ObjectName objectName, string revoker, string user, bool withOption=false)
Dictionary< GrantCacheKey, Privileges > grantsCache
static DataObject BigInt(long value)
Definition: DataObject.cs:580
static readonly ObjectName UserGrantsTableName
static SqlReferenceExpression Reference(ObjectName objectName)
void RevokeFromGroup(string groupName, Grant grant)
Dictionary< string, Privileges > groupsPrivilegesCache
Defines the base class for instances that represent SQL expression tree nodes.
static Privileges QueryPrivileges(IQuery queryContext, ITable grantTable, string grantee, DbObjectType objectType, ObjectName objectName, bool withOption, bool withPublic)
static SqlConstantExpression Constant(object value)
Privileges GetUserPrivileges(string userName, DbObjectType objectType, ObjectName objectName, bool withOption)
DbObjectType
The kind of objects that can be handled by a database system and its managers
Definition: DbObjectType.cs:27
Provides the information for a user in a database system
Definition: User.cs:27
bool WithOption
Gets a value indicating whether the grants include an option to grant to other users.
Definition: Grant.cs:77
static void UpdateGrants(IQuery queryContext, IMutableTable grantTable, DbObjectType objectType, ObjectName objectName, string granter, string grantee, Privileges privileges, bool withOption)
DbObjectType ObjectType
Gets the type of the object on which to provide access privileges to the user.
Definition: Grant.cs:97
An interface that defines contracts to alter the contents of a table.
The entity that holds the access control granted to an user or a group to a specific object in a data...
Definition: Grant.cs:26
void ClearUserGrantsCache(string userName, DbObjectType objectType, ObjectName objectName, bool withOption, bool withPublic)