====
[plcapi.git] / PLC / AuthorizeHelpers.py
index 1f3b8a9..ec8daaf 100644 (file)
 # Thierry Parmentelat - INRIA
 #
 from PLC.Faults import *
-from PLC.Persons import Person
-from PLC.Nodes import Node
+from PLC.Persons import Persons,Person
+from PLC.Sites import Sites,Site
+from PLC.Nodes import Nodes,Node
+from PLC.Interfaces import Interfaces,Interface
+from PLC.Slices import Slices,Slice
 
 class AuthorizeHelpers:
 
     @staticmethod
-    def interface_belongs_to_person (api,interface, person):
-        try:
-            node=api.GetNodes(interface['node_id'])[0]
-            return node_belong_to_person (api, node, person)
-        except:
-            return False
-
-    @staticmethod
-    def node_belongs_to_person (api, node, person):
-        try:
-            site=api.GetSites(node['site_id'])[0]
-            return person_belongs_to_site (api, person, site)
-        except:
-            return False
-
-    @staticmethod
-    def person_belongs_to_site (api, person, site):
-        return site['site_id'] in person['site_ids']
+    def person_tag_type_common_roles (api, person, tag_type):
+        return list (set(person['roles']).intersection(set(tag_type['roles'])))
 
     @staticmethod
     def caller_may_access_tag_type (api, caller, tag_type):
         if isinstance(caller,Person):
-            return len(set(caller['roles']).intersection(set(tag_type['roles'])))!=0
+            return len(AuthorizeHelpers.person_tag_type_common_roles(api,caller,tag_type))!=0
         elif isinstance(caller,Node):
             return 'node' in tag_type['roles']
         else:
             raise PLCInvalidArgument, "caller_may_access_tag_type - unexpected arg"
 
     @staticmethod
-    def person_access_person (api, caller_person, subject_person):
+    def person_may_access_person (api, caller_person, subject_person):
         # keep it simple for now - could be a bit more advanced for PIs maybe
         try:    return caller_person['person_id'] == subject_person['person_id']
         except: return False
 
+    @staticmethod
+    def person_in_site (api, person, site):
+        return site['site_id'] in person['site_ids']
+
     @staticmethod
     def person_in_slice (api, caller_person, slice):
         return caller_person['person_id'] in slice['person_ids']
 
+    @staticmethod
+    def slice_in_site (api, slice, site):
+        return slice['site_id']==site['site_id']
+
+    @staticmethod
+    def node_id_in_slice (api, node_id_or_hostname, slice):
+        if isinstance (node_id_or_hostname,int):
+            return node_id_or_hostname in slice['node_ids']
+        else:
+            try:   return Nodes(api,node_id_or_hostname)[0]['node_id'] in slice['node_ids']
+            except:return False
+
     @staticmethod
     def node_in_slice (api, caller_node, slice):
         return caller_node['node_id'] in slice['node_ids']
 
     @staticmethod
-    def node_id_or_hostname_in_slice (api, node_id_or_hostname, slice):
+    def node_id_in_site (api, node_id_or_hostname, site):
         if isinstance (node_id_or_hostname,int):
-            return node_id_or_hostname in slice['node_ids']
+            return node_id_or_hostname in site['node_ids']
         else:
-            try:   return GetNodes(node_id_or_hostname_in_slice)[0]['node_id'] in slice['node_ids']
+            try:   return Nodes(api,node_id_or_hostname)[0]['node_id'] in site['node_ids']
             except:return False
 
 
+    @staticmethod
+    def node_match_id (api, node, node_id_or_hostname):
+        if isinstance (node_id_or_hostname,int):
+            return node['node_id']==node_id_or_hostname
+        else:
+            return node['hostname']==node_id_or_hostname
+
+    @staticmethod
+    def interface_belongs_to_person (api,interface, person):
+        try:
+            node=Nodes(api,[interface['node_id']])[0]
+            return AuthorizeHelpers.node_belongs_to_person (api, node, person)
+        except:
+            return False
+
+    @staticmethod
+    def node_belongs_to_person (api, node, person):
+        try:
+            site=Sites(api,[node['site_id']])[0]
+            return AuthorizeHelpers.person_in_site (api, person, site)
+        except:
+            import traceback
+            return False
+
+    # does the slice belong to the site that the (pi) user is in ?
+    @staticmethod
+    def slice_belongs_to_pi (api, slice, pi):
+        return slice['site_id'] in pi['site_ids']
+
+    @staticmethod
+    def caller_is_node (api, caller, node):
+        return 'node_id' in caller and caller['node_id']==node['node_id']
+
+
+# authorization methods - check if a given caller can set tag on this object
+# called in {Add,Update,Delete}<Class>Tags methods, and in the accessors created in factory
+# attach these as <Class>.caller_may_write_tag so accessors can find it
+
+def caller_may_write_node_tag (node, api, caller, tag_type):
+    if 'roles' in caller and 'admin' in caller['roles']:
+        pass
+    elif not AuthorizeHelpers.caller_may_access_tag_type (api, caller, tag_type):
+        raise PLCPermissionDenied, "Role mismatch for writing tag %s"%(tag_type['tagname'])
+    elif AuthorizeHelpers.node_belongs_to_person (api, node, caller):
+        pass
+    elif AuthorizeHelpers.caller_is_node (api, caller, node):
+        pass
+    else:
+        raise PLCPermissionDenied, "Writing node tag: must belong in the same site as %s"%\
+            (node['hostname'])
+
+setattr(Node,'caller_may_write_tag',caller_may_write_node_tag)
+        
+
+def caller_may_write_interface_tag (interface, api, caller, tag_type):
+    if 'roles' in caller and 'admin' in caller['roles']:
+        pass
+    elif not AuthorizeHelpers.caller_may_access_tag_type (api, caller, tag_type):
+        raise PLCPermissionDenied, "Role mismatch for writing tag %s"%(tag_type['tagname'])
+    elif AuthorizeHelpers.interface_belongs_to_person (api, interface, caller):
+        pass
+    else:
+        raise PLCPermissionDenied, "Writing interface tag: must belong in the same site as %s"%\
+            (interface['ip'])
+        
+setattr(Interface,'caller_may_write_tag',caller_may_write_interface_tag)
+        
+
+def caller_may_write_site_tag (site, api, caller, tag_type):
+    if 'roles' in caller and 'admin' in caller['roles']:
+        pass
+    elif not AuthorizeHelpers.caller_may_access_tag_type (api, caller, tag_type):
+        raise PLCPermissionDenied, "Role mismatch for writing tag %s"%(tag_type['tagname'])
+    elif AuthorizeHelpers.person_in_site (api, caller, site):
+        pass
+    else:
+        raise PLCPermissionDenied, "Writing site tag: must be part of site"%site['login_base']
+
+setattr(Site,'caller_may_write_tag',caller_may_write_site_tag)
+
+
+def caller_may_write_person_tag (person, api, caller, tag_type):
+    if 'roles' in caller and 'admin' in caller['roles']:
+        pass
+    # user can change tags on self
+    elif AuthorizeHelpers.person_may_access_person (api, caller, person):
+        pass
+    else:
+        raise PLCPermissionDenied, "Writing person tag: you can only change your own tags"
+
+setattr(Person,'caller_may_write_tag',caller_may_write_person_tag)
+
+
+def caller_may_write_slice_tag (slice, api, caller, tag_type, node_id_or_hostname=None, nodegroup_id_or_name=None):
+    granted=False
+    reason=""
+    if 'roles' in caller and 'admin' in caller['roles']:
+        granted=True
+    # does caller have right role(s) ? this knows how to deal with caller being a node
+    elif not AuthorizeHelpers.caller_may_access_tag_type (api, caller, tag_type):
+        reason="caller may not access this tag type"
+        granted=False
+    # node callers: check the node is in the slice
+    elif isinstance(caller, Node): 
+        # nodes can only set their own sliver tags
+        if node_id_or_hostname is None: 
+            reason="wrong node caller"
+            granted=False
+        elif not AuthorizeHelpers.node_match_id (api, caller, node_id_or_hostname):
+            reason="node mismatch"
+            granted=False
+        elif not AuthorizeHelpers.node_in_slice (api, caller, slice):
+            reason="slice not in node"
+            granted=False
+        else:
+            granted=True
+    # caller is a non-admin person
+    else:
+        # only admins can handle slice tags on a nodegroup
+        if nodegroup_id_or_name:
+            raise PLCPermissionDenied, "Cannot set slice tag %s on nodegroup - restricted to admins"%\
+                (tag_type['tagname'])
+        # if a node is specified it is expected to be in the slice
+        if node_id_or_hostname:
+            if not AuthorizeHelpers.node_id_in_slice (api, node_id_or_hostname, slice):
+                raise PLCPermissionDenied, "%s, node must be in slice when setting sliver tag"
+        # try all roles to find a match - tech are ignored b/c not in AddSliceTag.roles anyways
+        for role in AuthorizeHelpers.person_tag_type_common_roles(api,caller,tag_type):
+            reason="user not in slice; or slice does not belong to pi's site"
+            # regular users need to be in the slice
+            if role=='user':
+                if AuthorizeHelpers.person_in_slice(api, caller, slice):
+                    granted=True ; break
+            # for convenience, pi's can tweak all the slices in their site
+            elif role=='pi':
+                if AuthorizeHelpers.slice_belongs_to_pi (api, slice, caller):
+                    granted=True ; break
+    if not granted:
+#        try: print "DEBUG: caller=%s"%caller
+#        except: pass
+        raise PLCPermissionDenied, "Cannot write slice tag %s - %s"%(tag_type['tagname'],reason)
+
+setattr(Slice,'caller_may_write_tag',caller_may_write_slice_tag)
+
+