django-swingcms / swingcms / custodian / code.py

  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
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.core.mail.message import EmailMessage
from django.utils.translation import ugettext as _
from django.utils.hashcompat import sha_constructor
from django.dispatch import Signal

import settings
from loading import swingcache
from custodian.models import Permission, Group, User, AnonymousUser
from swingexceptions import PermissionDenied, ConfirmEmailError
from cms.code import get_hexdigest, make_random_key



__all__ = (
    'check_password',
    'authenticate',
    'get_user',
    'login',
    'logout',

    'get_all_global_perms',
    'get_all_model_perms',

    'get_global_perms',
    'get_model_perms',
    'get_specific_perms',

    'derive_perms',
    'calculate_group_global_perms',
    'calculate_group_model_perms',
    'calculate_group_specific_perms',
    'calculate_user_global_perms',
    'calculate_user_model_perms',
    'calculate_user_specific_perms',

    'has_perm',

    'grant_global_perm',
    'grant_model_perm',
    'grant_specific_perm',
    'remove_global_perm',
    'remove_model_perm',
    'remove_specific_perm',
    'remove_all_specific_perm',

    'inherit_perms',
    'templatize_perms',
    'initialize_perms',

    'check_password'
)

user_logged_in = Signal(providing_args=['request', 'user'])
user_logged_out = Signal(providing_args=['request', 'user'])

###############################################################
##                          VARIOUS                          ##
###############################################################


def check_password(provided_password, db_password):
    """
    Returns a boolean of whether the raw_password was correct. Handles
    encryption formats behind the scenes. Optionally bypass encrypting.
    """
    try:
        match = db_password == get_hexdigest("sha1", provided_password)
    except ValueError:
        match = False
    if not match and settings.TRY_NO_ENCRYPTED:
        match = provided_password == db_password
    return match


def authenticate(username=None, password=None):
    """
    Authenticate permitting encryption bypassing.
    """
    try:
        user = User.objects.get(username=username)
    except ObjectDoesNotExist:
        return None
    if not check_password(password, user.password):
        return None
    return user


def get_user(request):

    try:
        return User.objects.get(id=request.session[settings.SESSION_KEY])
    except KeyError, ObjectDoesNotExist:
        return AnonymousUser()


def login(request, user):
    """
    Persist a user id and a backend in the request. This way a user doesn't
    have to reauthenticate on every request.
    """
    if settings.SESSION_KEY in request.session:
        if request.session[settings.SESSION_KEY] != user.id:
            # To avoid reusing another user's session, create a new, empty
            # session if the existing session corresponds to a different
            # authenticated user.
            request.session.flush()
    else:
        request.session.cycle_key()

    request.session[settings.SESSION_KEY] = user.id
    if hasattr(request, 'user'):
        request.user = user

    user_logged_in.send(sender=user.__class__, request=request, user=user)


def logout(request):
    """
    Removes the authenticated user's ID from the request and flushes their
    session data.
    """
    # Dispatch the signal before the user is logged out so the receivers have a
    # chance to find out *who* logged out.
    from cms.models import Padlock

    user = get_user(request)

    if isinstance(user, AnonymousUser):
        return

    user_logged_out.send(sender=User, request=request, user=user)

    request.session.flush()
    Padlock.clear(owner=user, check_expired=False)

    if hasattr(request, 'user'):
        request.user = AnonymousUser()


###############################################################
##     GET, DERIVE, CALCULATE, GRANT, REMOVE PERMISSIONS     ##
###############################################################


DEFAULT_OWNER_ACTIONS = ('view', 'change', 'delete')


## get all permissions ##

def get_all_global_perms(object_name='ALL'):
    """
    Return all global permission, on object if provided.
    Note that also None object is computated.
    """
    if object_name == 'ALL':
        return settings.GLOBAL_PERMS
    else:
        return tuple((a, on) for a, on in settings.GLOBAL_PERMS if on == object_name)


def get_all_model_perms(model_name='ALL'):
    """
    Return all model permission, on model if model name provided.
    """
    if model_name == 'ALL':
        perms = tuple()
        for mn, actions in settings.MODELS_ACTIONS.items():
            perms += tuple((a, mn) for a in actions)
    else:
        perms = tuple((a, model_name) for a in settings.MODELS_ACTIONS.get(model_name, tuple()))
    return perms


## get GMS permissions for group and user ##

def get_global_perms(subject, object_name='ALL'):
    """
    Return global permissions of user or group, on object if provided.
    Hard-code superuser and anonymous capability for better performance.
    Grant super permission 'view public' to alls.
    """
    if object_name in ('public', 'ALL'):
        superperm = (('view', 'public'),)
    else:
        superperm = tuple()

    if hasattr(subject, 'is_anonymous') and subject.is_anonymous():
        return superperm + tuple(), tuple()

    if hasattr(subject, 'is_superuser') and subject.is_superuser:
        return superperm + get_all_global_perms(object_name=object_name), tuple()

    perms = subject.permissions.filter(type='GLOBAL')

    if not object_name == 'ALL':
        perms = perms.filter(object_name=object_name)

    can = tuple(perms.filter(mode='CAN').values_list('action', 'object_name'))
    cannot = tuple(perms.filter(mode='CANNOT').values_list('action', 'object_name'))

    return superperm + can, cannot


def get_model_perms(subject, model_name='ALL'):
    """
    Return model permissions of user or group, on model if model name
    if provided.
    Hard-code superuser and anonymous capability for better performance.
    """
    if hasattr(subject, 'is_anonymous') and subject.is_anonymous():
        return tuple(), tuple()

    if hasattr(subject, 'is_superuser') and subject.is_superuser:
        return get_all_model_perms(model_name=model_name), tuple()

    perms = subject.permissions.filter(type='MODEL')

    if not model_name == 'ALL':
        perms = perms.filter(object_name=model_name)

    can = tuple(perms.filter(mode='CAN').values_list('action', 'object_name'))
    cannot = tuple(perms.filter(mode='CANNOT').values_list('action', 'object_name'))

    return can, cannot


def get_specific_perms(subject, obj):
    """
    Return specific permissions of user or group on obj.
    Hard-code superuser and anonymous capability for better performance.
    """
    if hasattr(subject, 'is_anonymous') and subject.is_anonymous():
        return tuple(), tuple()

    if hasattr(subject, 'is_superuser') and subject.is_superuser:
        return get_all_model_perms(model_name=obj.__class__.__name__.lower()), tuple()

    perms = subject.permissions.filter(type='SPECIFIC')

    if not obj == 'ALL':
        perms = perms.filter(object_name=obj.__class__.__name__.lower(), object_id=obj.id)

    can = tuple(perms.filter(mode='CAN').values_list('action', 'object_name'))
    cannot = tuple(perms.filter(mode='CANNOT').values_list('action', 'object_name'))

    return can, cannot

## derivation of model and specific permissions from global permissions ##

def _default_derive_perms(Gp, obj_or_model_name):
    """
    Derive model or specific permissions from global permissions
    when global permission object is null or when obj provided and
    global permission match its status. Obviusly permissions that haven't
    action consistent with provided (obj's) model name are filtered.
    """
    # evaluate on obj
    if not isinstance(obj_or_model_name, basestring):
        return tuple((action, obj_or_model_name.__class__.__name__.lower()) for action, object_name in Gp \
                    if object_name in (None, getattr(obj_or_model_name, 'status', None)) \
                    and action in settings.MODELS_ACTIONS.get(obj_or_model_name.__class__.__name__.lower(), tuple()))

    # evaluate on model
    elif not obj_or_model_name == 'ALL':
        return tuple((action, obj_or_model_name) for action, object_ in Gp \
                    if object_ == None \
                    and action in settings.MODELS_ACTIONS.get(obj_or_model_name, tuple()))

    # evaluate on all models
    else:
        perms = tuple()
        for action, object_name in Gp:
            if object_name == None:
                for model_name in swingcache.MODELS_NAMES:
                    if action in settings.MODELS_ACTIONS.get(model_name, tuple()):
                        perms += ((action, model_name),)
        return perms

# import function for derivation from global permissions, default in this module
derive_perms = getattr(settings, 'DERIVE_PERMS_PATH', None) \
               and __import__(settings.DERIVE_PERMS_PATH, 'derive_perms') \
               or _default_derive_perms


## calculate permissions ##

def calculate_group_global_perms(group=None, object_name='ALL', **perms):
    """
    Calculate global permisions simply by difference.
    """
    gGp_can, gGp_cannot = perms.get('gGp') or get_global_perms(group, object_name=object_name)
    return set(gGp_can).difference(gGp_cannot)


def calculate_group_model_perms(group=None, model_name='ALL', **perms):
    """
    Calculate model permission taking account off
    global permissions in right priority order.
    """
    gGp_can, gGp_cannot = perms.get('gGp') or get_global_perms(group)
    gMp_can, gMp_cannot = perms.get('gMp') or get_model_perms(group, model_name=model_name)

    # derive from globals
    perms = derive_perms(set(gGp_can).difference(gGp_cannot), model_name)

    # algebra on global and model permissions
    perms = set(gMp_can).union(perms).difference(gMp_cannot)

    return perms


def calculate_group_specific_perms(obj, group=None, **perms):
    """
    Calculate specific permission taking account off global
    and model permissions in right priority order.
    """
    gGp_can, gGp_cannot = perms.get('gGp') or get_global_perms(group)
    gMp_can, gMp_cannot = perms.get('gMp') or get_model_perms(group, model_name=obj.__class__.__name__.lower())
    gSp_can, gSp_cannot = perms.get('gSp') or get_specific_perms(group, obj)

    # derive from globals
    perms = derive_perms(set(gGp_can).difference(gGp_cannot), obj)

    # algebra on global and model permissions
    perms = set(gMp_can).union(perms).difference(gMp_cannot)

    # algebra on model and specific permissions
    perms = set(gSp_can).union(perms).difference(gSp_cannot)

    return perms


def calculate_user_global_perms(user=None, object_name='ALL', **perms):
    """
    Calculate global permisions taking account off groups.
    """
    uGp_can, uGp_cannot = perms.get('uGp') or get_global_perms(user, object_name=object_name)
    gGp_tuple = perms.get('gGp_tuple') or ((get_global_perms(g, object_name=object_name)) for g in user.groups.all())

    groups_perms = set()
    for gGp in gGp_tuple:
        groups_perms.update(calculate_group_global_perms(object_name=object_name, gGp=gGp))

    perms = set(uGp_can).union(groups_perms).difference(uGp_cannot)

    return perms


def calculate_user_model_perms(user=None, model_name='ALL', **perms):
    """
    Calculate model permission taking account off groups
    and global permissions in right priority order.
    """
    uGp_can, uGp_cannot = perms.get('uGp') or get_global_perms(user)
    uMp_can, uMp_cannot = perms.get('uMp') or get_model_perms(user, model_name=model_name)
    gGp_tuple = perms.get('gGp') or ((get_global_perms(g)) for g in user.groups.all())
    gMp_tuple = perms.get('gMp') or ((get_model_perms(g, model_name=model_name)) for g in user.groups.all())

    # merge all permission from users's groups, the result is "extensive"
    # because each group are merged after mode (can/cannot) evaluation
    groups_perms = set()
    for gGp, gMp in zip(gGp_tuple,gMp_tuple):
        groups_perms.update(calculate_group_model_perms(model_name=model_name, gGp=gGp, gMp=gMp))

    # derive from globals
    derived_can = derive_perms(uGp_can, model_name)
    derived_cannot = derive_perms(uGp_cannot, model_name)
    perms = set(derived_can).union(groups_perms).difference(derived_cannot)

    # algebra on global and model permissions
    perms = set(uMp_can).union(perms).difference(uMp_cannot)

    return perms


def calculate_user_specific_perms(obj, user=None, **perms):
    """
    Calculate specific permission taking account off groups,
    global, model and owner permissions in right priority order.
    """
    uGp_can, uGp_cannot = perms.get('uGp') or get_global_perms(user)
    uMp_can, uMp_cannot = perms.get('uMp') or get_model_perms(user, model_name=obj.__class__.__name__.lower())
    uSp_can, uSp_cannot = perms.get('uSp') or get_specific_perms(user, obj)
    gGp_tuple = perms.get('gGp') or ((get_global_perms(g)) for g in user.groups.all())
    gMp_tuple = perms.get('gMp') or ((get_model_perms(g, model_name=obj.__class__.__name__.lower())) for g in user.groups.all())
    gSp_tuple = perms.get('gSp') or ((get_specific_perms(g, obj)) for g in user.groups.all())

    # merge all permission from users's groups, the result is "extensive"
    # because each group are merged after mode (can/cannot) evaluation
    groups_perms = set()
    for gGp, gMp,gSp in zip(gGp_tuple,gMp_tuple,gSp_tuple):
        groups_perms.update(calculate_group_specific_perms(obj, gGp=gGp, gMp=gMp, gSp=gSp))

    # derive from globals
    derived_can = derive_perms(uGp_can, obj)
    derived_cannot = derive_perms(uGp_cannot, obj)
    perms = set(derived_can).union(groups_perms).difference(derived_cannot)

    # algebra on global and model permissions
    perms = set(uMp_can).union(perms).difference(uMp_cannot)

    # owner permissions
    if user and user == obj.owner:
        perms.update(((action, obj.__class__.__name__.lower()) for action in DEFAULT_OWNER_ACTIONS))

    # algebra on model and specific permissions and hard-code public visibility
    perms = set(uSp_can).union(perms).difference(uSp_cannot)

    return perms


## check permissions ##

def has_perm(user, action, obj=None, object_or_model_name=None, **perms):
    '''
    Check permission on obj, hard-coding super permission 'view public'
    and anonymous-superuser capability for better performance.
    '''
    if user.is_superuser:
        return True

    # evaluate a permission on obj
    if obj:
        if action == 'view' and getattr(obj, 'status', None) == 'public':
            return True
        elif user.is_anonymous():
            return False
        print calculate_user_specific_perms(obj,
                                                                                         user=user,
                                                                                         **perms)
        return (action, obj.__class__.__name__.lower()) in calculate_user_specific_perms(obj,
                                                                                         user=user,
                                                                                         **perms)
    # evaluate a permission on model
    elif object_or_model_name in swingcache.MODELS_NAMES:
        if user.is_anonymous():
            return False
        return (action, object_or_model_name) in calculate_user_model_perms(user=user,
                                                                            model_name=object_or_model_name,
                                                                            **perms)
    # evaluate a global permission
    else:
        if (action, object_or_model_name) == ('view', 'public'):
            return True
        elif user.is_anonymous():
            return False
        return (action, object_or_model_name) in calculate_user_global_perms(user=user,
                                                                             object_=object_or_model_name,
                                                                             **perms)


## grant and remove permissions ##

def grant_global_perm(subject, mode, action, object_name):

    perm, created = Permission.objects.get_or_create(type='GLOBAL',
                                                     mode=mode,
                                                     action=action,
                                                     object_name=object_name)
    subject.permissions.add(perm)
    return perm, created


def grant_model_perm(subject, mode, action, model_name):

    perm, created = Permission.objects.get_or_create(type='MODEL',
                                                     mode=mode,
                                                     action=action,
                                                     object_name=model_name)
    subject.permissions.add(perm)
    return perm, created


def grant_specific_perm(subject, mode, action, obj):

    perm, created = Permission.objects.get_or_create(type='SPECIFIC',
                                                     mode=mode,
                                                     action=action,
                                                     object_name=obj.__class__.__name__.lower(),
                                                     object_id=obj.id)
    subject.permissions.add(perm)
    return perm, created


def remove_global_perm(subject, mode, action, object_name):

    perm = Permission.objects.get(type='GLOBAL',
                                  mode=mode,
                                  action=action,
                                  object_name=object_name)
    subject.permissions.remove(perm)


def remove_model_perm(subject, mode, action, model_name):

    perm = Permission.objects.get(type='MODEL',
                                  mode=mode,
                                  action=action,
                                  object_name=model_name)
    subject.permissions.remove(perm)


def remove_specific_perm(subject, mode, action, obj):

    perm = Permission.objects.get(type='SPECIFIC',
                                  mode=mode,
                                  action=action,
                                  object_name=obj.__class__.__name__.lower(),
                                  object_id=obj.id)
    subject.permissions.remove(perm)


def remove_all_specific_perm(obj):

    Permission.objects.filter(object_name=obj.__class__.__name__.lower(), object_id=obj.id).delete()


## other method on permissions ##

def _default_inherit_perms(obj, parent=None):
    """
    Obj acquire specific compatible permissions from parent.
    For better performance make this at the moment of adding.
    """
    parent = parent or obj.get_parent()

    for p in Permission.objects.filter(object_id=parent.id, type='SPECIFIC'):
        if p.action in settings.MODELS_ACTIONS[obj.__class__.__name__.lower()]:
            grant_specific_perm(p.user, p.mode, action, obj)
            grant_specific_perm(p.group, p.mode, action, obj)

# import function for inherition from parent container, default in this module
inherit_perms = getattr(settings, 'INHERIT_PERMS_PATH', None) \
                and __import__(settings.INHERIT_PERMS_PATH, 'inherit_perms') \
                or _default_inherit_perms


def templatize_perms(perms_in, obj):
    """
    Prepare for template: extract addable objs from 'add obj to'
    permissions, replaces model name with 'obj' wildcard string.
    """
    addable = set()
    perms_out = set()

    for action, object_or_model_name in perms_in:

        # extract addable from "add obj to" permissions
        if action[:3] == 'add' and action[-2:] == 'to' and object_or_model_name == obj.__class__.__name__.lower():
            addable.update((action[4:-3],))

        # reduce to string's tuple for template lookup
        elif object_or_model_name == obj.__class__.__name__.lower():
            object_or_model_name = 'obj'

        perms_out.update(((action, object_or_model_name),))

    return perms_out, addable
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.