Setting tag plcapi-5.0-29
[plcapi.git] / PLC / AuthorizeHelpers.py
1 #
2 # Thierry Parmentelat - INRIA
3 #
4 from PLC.Faults import *
5 from PLC.Persons import Persons,Person
6 from PLC.Sites import Sites,Site
7 from PLC.Nodes import Nodes,Node
8 from PLC.Interfaces import Interfaces,Interface
9 from PLC.Slices import Slices,Slice
10
11 class AuthorizeHelpers:
12
13     @staticmethod
14     def person_tag_type_common_roles (api, person, tag_type):
15         return list (set(person['roles']).intersection(set(tag_type['roles'])))
16
17     @staticmethod
18     def caller_may_access_tag_type (api, caller, tag_type):
19         if isinstance(caller,Person):
20             return len(AuthorizeHelpers.person_tag_type_common_roles(api,caller,tag_type))!=0
21         elif isinstance(caller,Node):
22             return 'node' in tag_type['roles']
23         else:
24             raise PLCInvalidArgument, "caller_may_access_tag_type - unexpected arg"
25
26     @staticmethod
27     def person_may_access_person (api, caller_person, subject_person):
28         # keep it simple for now - could be a bit more advanced for PIs maybe
29         try:    return caller_person['person_id'] == subject_person['person_id']
30         except: return False
31
32     @staticmethod
33     def person_in_site (api, person, site):
34         return site['site_id'] in person['site_ids']
35
36     @staticmethod
37     def person_in_slice (api, caller_person, slice):
38         return caller_person['person_id'] in slice['person_ids']
39
40     @staticmethod
41     def slice_in_site (api, slice, site):
42         return slice['site_id']==site['site_id']
43
44     @staticmethod
45     def node_id_in_slice (api, node_id_or_hostname, slice):
46         if isinstance (node_id_or_hostname,int):
47             return node_id_or_hostname in slice['node_ids']
48         else:
49             try:   return Nodes(api,node_id_or_hostname)[0]['node_id'] in slice['node_ids']
50             except:return False
51
52     @staticmethod
53     def node_in_slice (api, caller_node, slice):
54         return caller_node['node_id'] in slice['node_ids']
55
56     @staticmethod
57     def node_id_in_site (api, node_id_or_hostname, site):
58         if isinstance (node_id_or_hostname,int):
59             return node_id_or_hostname in site['node_ids']
60         else:
61             try:   return Nodes(api,node_id_or_hostname)[0]['node_id'] in site['node_ids']
62             except:return False
63
64
65     @staticmethod
66     def node_match_id (api, node, node_id_or_hostname):
67         if isinstance (node_id_or_hostname,int):
68             return node['node_id']==node_id_or_hostname
69         else:
70             return node['hostname']==node_id_or_hostname
71
72     @staticmethod
73     def interface_belongs_to_person (api,interface, person):
74         try:
75             node=Nodes(api,[interface['node_id']])[0]
76             return AuthorizeHelpers.node_belongs_to_person (api, node, person)
77         except:
78             return False
79
80     @staticmethod
81     def node_belongs_to_person (api, node, person):
82         try:
83             site=Sites(api,[node['site_id']])[0]
84             return AuthorizeHelpers.person_in_site (api, person, site)
85         except:
86             import traceback
87             return False
88
89     # does the slice belong to the site that the (pi) user is in ?
90     @staticmethod
91     def slice_belongs_to_pi (api, slice, pi):
92         return slice['site_id'] in pi['site_ids']
93
94     @staticmethod
95     def caller_is_node (api, caller, node):
96         return 'node_id' in caller and caller['node_id']==node['node_id']
97
98
99 # authorization methods - check if a given caller can set tag on this object
100 # called in {Add,Update,Delete}<Class>Tags methods, and in the accessors created in factory
101 # attach these as <Class>.caller_may_write_tag so accessors can find it
102
103 def caller_may_write_node_tag (node, api, caller, tag_type):
104     if 'roles' in caller and 'admin' in caller['roles']:
105         pass
106     elif not AuthorizeHelpers.caller_may_access_tag_type (api, caller, tag_type):
107         raise PLCPermissionDenied, "Role mismatch for writing tag %s"%(tag_type['tagname'])
108     elif AuthorizeHelpers.node_belongs_to_person (api, node, caller):
109         pass
110     elif AuthorizeHelpers.caller_is_node (api, caller, node):
111         pass
112     else:
113         raise PLCPermissionDenied, "Writing node tag: must belong in the same site as %s"%\
114             (node['hostname'])
115
116 setattr(Node,'caller_may_write_tag',caller_may_write_node_tag)
117         
118
119 def caller_may_write_interface_tag (interface, api, caller, tag_type):
120     if 'roles' in caller and 'admin' in caller['roles']:
121         pass
122     elif not AuthorizeHelpers.caller_may_access_tag_type (api, caller, tag_type):
123         raise PLCPermissionDenied, "Role mismatch for writing tag %s"%(tag_type['tagname'])
124     elif AuthorizeHelpers.interface_belongs_to_person (api, interface, caller):
125         pass
126     else:
127         raise PLCPermissionDenied, "Writing interface tag: must belong in the same site as %s"%\
128             (interface['ip'])
129         
130 setattr(Interface,'caller_may_write_tag',caller_may_write_interface_tag)
131         
132
133 def caller_may_write_site_tag (site, api, caller, tag_type):
134     if 'roles' in caller and 'admin' in caller['roles']:
135         pass
136     elif not AuthorizeHelpers.caller_may_access_tag_type (api, caller, tag_type):
137         raise PLCPermissionDenied, "Role mismatch for writing tag %s"%(tag_type['tagname'])
138     elif AuthorizeHelpers.person_in_site (api, caller, site):
139         pass
140     else:
141         raise PLCPermissionDenied, "Writing site tag: must be part of site"%site['login_base']
142
143 setattr(Site,'caller_may_write_tag',caller_may_write_site_tag)
144
145
146 def caller_may_write_person_tag (person, api, caller, tag_type):
147     if 'roles' in caller and 'admin' in caller['roles']:
148         pass
149     # user can change tags on self
150     elif AuthorizeHelpers.person_may_access_person (api, caller, person):
151         pass
152     else:
153         raise PLCPermissionDenied, "Writing person tag: you can only change your own tags"
154
155 setattr(Person,'caller_may_write_tag',caller_may_write_person_tag)
156
157
158 def caller_may_write_slice_tag (slice, api, caller, tag_type, node_id_or_hostname=None, nodegroup_id_or_name=None):
159     granted=False
160     reason=""
161     if 'roles' in caller and 'admin' in caller['roles']:
162         granted=True
163     # does caller have right role(s) ? this knows how to deal with caller being a node
164     elif not AuthorizeHelpers.caller_may_access_tag_type (api, caller, tag_type):
165         reason="caller may not access this tag type"
166         granted=False
167     # node callers: check the node is in the slice
168     elif isinstance(caller, Node): 
169         # nodes can only set their own sliver tags
170         if node_id_or_hostname is None: 
171             reason="wrong node caller"
172             granted=False
173         elif not AuthorizeHelpers.node_match_id (api, caller, node_id_or_hostname):
174             reason="node mismatch"
175             granted=False
176         elif not AuthorizeHelpers.node_in_slice (api, caller, slice):
177             reason="slice not in node"
178             granted=False
179         else:
180             granted=True
181     # caller is a non-admin person
182     else:
183         # only admins can handle slice tags on a nodegroup
184         if nodegroup_id_or_name:
185             raise PLCPermissionDenied, "Cannot set slice tag %s on nodegroup - restricted to admins"%\
186                 (tag_type['tagname'])
187         # if a node is specified it is expected to be in the slice
188         if node_id_or_hostname:
189             if not AuthorizeHelpers.node_id_in_slice (api, node_id_or_hostname, slice):
190                 raise PLCPermissionDenied, "%s, node must be in slice when setting sliver tag"
191         # try all roles to find a match - tech are ignored b/c not in AddSliceTag.roles anyways
192         for role in AuthorizeHelpers.person_tag_type_common_roles(api,caller,tag_type):
193             reason="user not in slice; or slice does not belong to pi's site"
194             # regular users need to be in the slice
195             if role=='user':
196                 if AuthorizeHelpers.person_in_slice(api, caller, slice):
197                     granted=True ; break
198             # for convenience, pi's can tweak all the slices in their site
199             elif role=='pi':
200                 if AuthorizeHelpers.slice_belongs_to_pi (api, slice, caller):
201                     granted=True ; break
202     if not granted:
203         raise PLCPermissionDenied, "Cannot write slice tag %s - %s"%(tag_type['tagname'],reason)
204
205 setattr(Slice,'caller_may_write_tag',caller_may_write_slice_tag)
206
207