2013-03-24 02:43:11 +04:00
# (c) 2013, AnsibleWorks, Michael DeHaan <michael@ansibleworks.com>
#
# This file is part of Ansible Commander
#
# Ansible Commander is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible Commander is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible Commander. If not, see <http://www.gnu.org/licenses/>.
2013-03-22 23:22:30 +04:00
from django . http import HttpResponse
from django . views . decorators . csrf import csrf_exempt
from lib . main . models import *
from django . contrib . auth . models import User
from lib . main . serializers import *
from lib . main . rbac import *
from django . core . exceptions import PermissionDenied
from rest_framework import mixins
from rest_framework import generics
from rest_framework import permissions
from rest_framework . response import Response
from rest_framework import status
import exceptions
import datetime
2013-03-27 02:18:05 +04:00
import json as python_json
2013-03-22 23:22:30 +04:00
# FIXME: machinery for auto-adding audit trail logs to all CREATE/EDITS
class BaseList ( generics . ListCreateAPIView ) :
2013-03-24 02:28:38 +04:00
2013-03-22 23:22:30 +04:00
def list_permissions_check ( self , request , obj = None ) :
''' determines some early yes/no access decisions, pre-filtering '''
if request . method == ' GET ' :
return True
if request . method == ' POST ' :
2013-03-24 20:36:42 +04:00
if self . __class__ . model in [ User ] :
2013-03-27 02:18:05 +04:00
ok = request . user . is_superuser or ( request . user . admin_of_organizations . count ( ) > 0 )
2013-03-26 22:44:12 +04:00
if not ok :
raise PermissionDenied ( )
return True
2013-03-24 20:36:42 +04:00
else :
2013-03-26 22:44:12 +04:00
if not self . __class__ . model . can_user_add ( request . user , self . request . DATA ) :
raise PermissionDenied ( )
return True
2013-03-22 23:22:30 +04:00
raise exceptions . NotImplementedError
def get_queryset ( self ) :
2013-03-24 00:03:17 +04:00
base = self . _get_queryset ( )
model = self . __class__ . model
if model == User :
return base . filter ( is_active = True )
elif model in [ Tag , AuditTrail ] :
return base
2013-03-23 23:34:16 +04:00
else :
return self . _get_queryset ( ) . filter ( active = True )
2013-03-22 23:22:30 +04:00
2013-03-24 20:36:42 +04:00
2013-03-23 00:52:44 +04:00
class BaseSubList ( BaseList ) :
''' used for subcollections with an overriden post '''
2013-03-23 22:31:36 +04:00
def list_permissions_check ( self , request , obj = None ) :
''' determines some early yes/no access decisions, pre-filtering '''
if request . method == ' GET ' :
return True
if request . method == ' POST ' :
# the can_user_attach methods will be called below
return True
raise exceptions . NotImplementedError
2013-03-23 00:52:44 +04:00
def post ( self , request , * args , * * kwargs ) :
2013-03-24 00:50:25 +04:00
postable = getattr ( self . __class__ , ' postable ' , False )
if not postable :
return Response ( status = status . HTTP_405_METHOD_NOT_ALLOWED )
2013-03-23 00:52:44 +04:00
parent_id = kwargs [ ' pk ' ]
2013-03-27 02:18:05 +04:00
sub_id = request . DATA . get ( ' id ' , None )
2013-03-23 00:52:44 +04:00
main = self . __class__ . parent_model . objects . get ( pk = parent_id )
2013-03-27 02:18:05 +04:00
subs = None
if sub_id :
subs = self . __class__ . model . objects . filter ( pk = sub_id )
else :
if ' disassociate ' in request . DATA :
raise PermissionDenied ( ) # ID is required to disassociate
else :
# this is a little tricky and a little manual
# the object ID was not specified, so it probably doesn't exist in the DB yet.
# we want to see if we can create it. The URL may choose to inject it's primary key into the object
# because we are posting to a subcollection. Use all the normal access control mechanisms.
inject_primary_key = getattr ( self . __class__ , ' inject_primary_key_on_post_as ' , None )
if inject_primary_key is not None :
# add the key to the post data using the pk from the URL
request . DATA [ inject_primary_key ] = kwargs [ ' pk ' ]
# attempt to deserialize the object
ser = self . __class__ . serializer_class ( data = request . DATA )
if not ser . is_valid ( ) :
return Response ( status = status . HTTP_400_BAD_REQUEST , data = python_json . dumps ( dict ( msg = ' invalid post data ' ) ) )
# ask the usual access control settings
if not self . __class__ . model . can_user_add ( request . user , ser . init_data ) :
raise PermissionDenied ( )
# save the object through the serializer, reload and returned the saved object deserialized
obj = ser . save ( )
ser = self . __class__ . serializer_class ( obj )
# now make sure we could have already attached the two together. If we could not have, raise an exception
# such that the transaction does not commit.
if not self . __class__ . parent_model . can_user_attach ( request . user , main , obj , self . __class__ . relationship ) :
raise PermissionDenied ( )
return Response ( status = status . HTTP_201_CREATED , data = python_json . dumps ( ser . data ) )
else :
# view didn't specify a way to get the pk from the URL, so not even trying
return Response ( status = status . HTTP_400_BAD_REQUEST , data = python_json . dumps ( dict ( msg = ' object cannot be created ' ) ) )
# we didn't have to create the object, so this is just associating the two objects together now...
# (or disassociating them)
2013-03-23 00:52:44 +04:00
if len ( subs ) != 1 :
return Response ( status = status . HTTP_400_BAD_REQUEST )
sub = subs [ 0 ]
relationship = getattr ( main , self . __class__ . relationship )
if not ' disassociate ' in request . DATA :
2013-03-23 23:08:02 +04:00
if not request . user . is_superuser and not self . __class__ . parent_model . can_user_attach ( request . user , main , sub , self . __class__ . relationship ) :
2013-03-23 00:52:44 +04:00
raise PermissionDenied ( )
if sub in relationship . all ( ) :
return Response ( status = status . HTTP_409_CONFLICT )
relationship . add ( sub )
else :
if not request . user . is_superuser and not self . __class__ . parent_model . can_user_unattach ( request . user , main , sub , self . __class__ . relationship ) :
raise PermissionDenied ( )
relationship . remove ( sub )
return Response ( status = status . HTTP_204_NO_CONTENT )
2013-03-22 23:22:30 +04:00
class BaseDetail ( generics . RetrieveUpdateDestroyAPIView ) :
def pre_save ( self , obj ) :
2013-03-24 20:36:42 +04:00
if type ( obj ) not in [ User , Tag , AuditTrail ] :
obj . created_by = self . request . user
2013-03-22 23:22:30 +04:00
def destroy ( self , request , * args , * * kwargs ) :
# somewhat lame that delete has to call it's own permissions check
obj = self . model . objects . get ( pk = kwargs [ ' pk ' ] )
if not request . user . is_superuser and not self . delete_permissions_check ( request , obj ) :
raise PermissionDenied ( )
2013-03-26 22:44:12 +04:00
if isinstance ( obj , PrimordialModel ) :
2013-03-24 22:14:59 +04:00
obj . name = " _deleted_ %s _ %s " % ( str ( datetime . time ( ) ) , obj . name )
obj . active = False
obj . save ( )
elif type ( obj ) == User :
obj . username = " _deleted_ %s _ %s " % ( str ( datetime . time ( ) ) , obj . username )
obj . is_active = False
obj . save ( )
else :
raise Exception ( " InternalError: destroy() not implemented yet for %s " % obj )
2013-03-22 23:22:30 +04:00
return HttpResponse ( status = 204 )
2013-03-22 23:36:59 +04:00
def delete_permissions_check ( self , request , obj ) :
2013-03-26 22:44:12 +04:00
if isinstance ( obj , PrimordialModel ) :
2013-03-24 22:14:59 +04:00
return self . __class__ . model . can_user_delete ( request . user , obj )
elif isinstance ( obj , User ) :
return UserHelper . can_user_delete ( request . user , obj )
raise PermissionDenied ( )
2013-03-22 23:36:59 +04:00
2013-03-22 23:44:32 +04:00
def item_permissions_check ( self , request , obj ) :
if request . method == ' GET ' :
2013-03-24 20:36:42 +04:00
if type ( obj ) == User :
return UserHelper . can_user_read ( request . user , obj )
else :
return self . __class__ . model . can_user_read ( request . user , obj )
2013-03-22 23:44:32 +04:00
elif request . method in [ ' PUT ' ] :
2013-03-24 20:36:42 +04:00
if type ( obj ) == User :
return UserHelper . can_user_administrate ( request . user , obj )
else :
return self . __class__ . model . can_user_administrate ( request . user , obj )
2013-03-22 23:44:32 +04:00
return False
2013-03-24 20:36:42 +04:00
def put ( self , request , * args , * * kwargs ) :
self . put_filter ( request , * args , * * kwargs )
return super ( BaseDetail , self ) . put ( request , * args , * * kwargs )
def put_filter ( self , request , * args , * * kwargs ) :
''' scrub any fields the user cannot/should not put, based on user context. This runs after read-only serialization filtering '''
pass
2013-03-22 23:36:59 +04:00