28e11b3531a34e7ad90141ee2424d3ac245a2c7c
[sfa.git] / sfa / trust / rights.py
1 #----------------------------------------------------------------------
2 # Copyright (c) 2008 Board of Trustees, Princeton University
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and/or hardware specification (the "Work") to
6 # deal in the Work without restriction, including without limitation the
7 # rights to use, copy, modify, merge, publish, distribute, sublicense,
8 # and/or sell copies of the Work, and to permit persons to whom the Work
9 # is furnished to do so, subject to the following conditions:
10 #
11 # The above copyright notice and this permission notice shall be
12 # included in all copies or substantial portions of the Work.
13 #
14 # THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
18 # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 # OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS
21 # IN THE WORK.
22 #----------------------------------------------------------------------
23 ##
24 # This Module implements rights and lists of rights for the SFA. Rights
25 # are implemented by two classes:
26 #
27 # Right - represents a single right
28 #
29 # Rights - represents a list of rights
30 #
31 # A right may allow several different operations. For example, the "info" right
32 # allows "listslices", "listcomponentresources", etc.
33 ##
34
35
36
37 ##
38 # privilege_table is a list of priviliges and what operations are allowed
39 # per privilege.
40 # Note that "*" is a privilege granted by ProtoGENI slice authorities, and we
41 # give it access to the GENI AM calls
42
43 privilege_table = {"authority": ["register", "remove", "update", "resolve", "list", "getcredential", "*"],
44                    "refresh": ["remove", "update"],
45                    "resolve": ["resolve", "list", "getcredential"],
46                    "sa": ["getticket", "redeemslice", "redeemticket", "createslice", "createsliver", "deleteslice", "deletesliver", "updateslice",
47                           "getsliceresources", "getticket", "loanresources", "stopslice", "startslice", "renewsliver",
48                           "deleteslice", "deletesliver", "resetslice", "listslices", "listnodes", "getpolicy", "sliverstatus"],
49                    "embed": ["getticket", "redeemslice", "redeemticket", "createslice", "createsliver", "renewsliver", "deleteslice", 
50                              "deletesliver", "updateslice", "sliverstatus", "getsliceresources", "shutdown"],
51                    "bind": ["getticket", "loanresources", "redeemticket"],
52                    "control": ["updateslice", "createslice", "createsliver", "renewsliver", "sliverstatus", "stopslice", "startslice", 
53                                "deleteslice", "deletesliver", "resetslice", "getsliceresources", "getgids"],
54                    "info": ["listslices", "listnodes", "getpolicy"],
55                    "ma": ["setbootstate", "getbootstate", "reboot", "getgids", "gettrustedcerts"],
56                    "operator": ["gettrustedcerts", "getgids"],                   
57                    "*": ["createsliver", "deletesliver", "sliverstatus", "renewsliver", "shutdown"]} 
58
59
60
61 ##
62 # Determine the rights that an object should have. The rights are entirely
63 # dependent on the type of the object. For example, users automatically
64 # get "refresh", "resolve", and "info".
65 #
66 # @param type the type of the object (user | sa | ma | slice | node)
67 # @param name human readable name of the object (not used at this time)
68 #
69 # @return Rights object containing rights
70
71 def determine_rights(type, name):
72     rl = Rights()
73
74     # rights seem to be somewhat redundant with the type of the credential.
75     # For example, a "sa" credential implies the authority right, because
76     # a sa credential cannot be issued to a user who is not an owner of
77     # the authority
78     if type == "user":
79         rl.add("refresh")
80         rl.add("resolve")
81         rl.add("info")
82     elif type in ["sa", "authority+sa"]:
83         rl.add("authority")
84         rl.add("sa")
85     elif type in ["ma", "authority+ma", "cm", "authority+cm", "sm", "authority+sm"]:
86         rl.add("authority")
87         rl.add("ma")
88     elif type == "authority":
89         rl.add("authority")
90         rl.add("sa")
91         rl.add("ma")
92     elif type == "slice":
93         rl.add("refresh")
94         rl.add("embed")
95         rl.add("bind")
96         rl.add("control")
97         rl.add("info")
98 # wouldn't that be authority+cm instead ?
99     elif type == "component":
100         rl.add("operator")
101     return rl
102
103
104 ##
105 # The Right class represents a single privilege.
106
107
108
109 class Right:
110     ##
111     # Create a new right.
112     #
113     # @param kind is a string naming the right. For example "control"
114
115     def __init__(self, kind, delegate=False):
116         self.kind = kind
117         self.delegate = delegate
118
119     def __repr__ (self): return "<Rgt:%s>"%self.kind
120
121     ##
122     # Test to see if this right object is allowed to perform an operation.
123     # Returns True if the operation is allowed, False otherwise.
124     #
125     # @param op_name is a string naming the operation. For example "listslices".
126
127     def can_perform(self, op_name):
128         allowed_ops = privilege_table.get(self.kind.lower(), None)
129         if not allowed_ops:
130             return False
131
132         # if "*" is specified, then all ops are permitted
133         if "*" in allowed_ops:
134             return True
135
136         return (op_name.lower() in allowed_ops)
137
138     ##
139     # Test to see if this right is a superset of a child right. A right is a
140     # superset if every operating that is allowed by the child is also allowed
141     # by this object.
142     #
143     # @param child is a Right object describing the child right
144
145     def is_superset(self, child):
146         my_allowed_ops = privilege_table.get(self.kind.lower(), None)
147         child_allowed_ops = privilege_table.get(child.kind.lower(), None)
148
149         if not self.delegate:
150             return False
151
152         if "*" in my_allowed_ops:
153             return True
154
155         for right in child_allowed_ops:
156             if not right in my_allowed_ops:
157                 return False
158
159         return True
160
161 ##
162 # A Rights object represents a list of privileges.
163
164 class Rights:
165     ##
166     # Create a new rightlist object, containing no rights.
167     #
168     # @param string if string!=None, load the rightlist from the string
169
170     def __init__(self, string=None):
171         self.rights = []
172         if string:
173             self.load_from_string(string)
174
175     def __repr__ (self): return "[" + " ".join( ["%s"%r for r in self.rights]) + "]"
176
177     def is_empty(self):
178         return self.rights == []
179
180     ##
181     # Add a right to this list
182     #
183     # @param right is either a Right object or a string describing the right
184
185     def add(self, right, delegate=False):
186         if isinstance(right, str):
187             right = Right(right, delegate)
188         self.rights.append(right)
189
190     ##
191     # Load the rightlist object from a string
192
193     def load_from_string(self, string):
194         self.rights = []
195
196         # none == no rights, so leave the list empty
197         if not string:
198             return
199
200         parts = string.split(",")
201         for part in parts:
202             if ':' in part:
203                 spl = part.split(':')
204                 kind = spl[0].strip()
205                 delegate = bool(int(spl[1]))
206             else:
207                 kind = part.strip()
208                 delegate = 0
209             self.rights.append(Right(kind, bool(delegate)))
210
211     ##
212     # Save the rightlist object to a string. It is saved in the format of a
213     # comma-separated list.
214
215     def save_to_string(self):
216         right_names = []
217         for right in self.rights:
218             right_names.append('%s:%d' % (right.kind.strip(), right.delegate))
219
220         return ",".join(right_names)
221
222     ##
223     # Check to see if some right in this list allows an operation. This is
224     # done by evaluating the can_perform function of each operation in the
225     # list.
226     #
227     # @param op_name is an operation to check, for example "listslices"
228
229     def can_perform(self, op_name):
230         
231         for right in self.rights:
232             if right.can_perform(op_name):
233                 return True
234         return False
235
236     ##
237     # Check to see if all of the rights in this rightlist are a superset
238     # of all the rights in a child rightlist. A rightlist is a superset
239     # if there is no operation in the child rightlist that cannot be
240     # performed in the parent rightlist.
241     #
242     # @param child is a rightlist object describing the child
243
244     def is_superset(self, child):
245         for child_right in child.rights:
246             allowed = False
247             for my_right in self.rights:
248                 if my_right.is_superset(child_right):
249                     allowed = True
250                     break
251             if not allowed:
252                 return False
253         return True
254
255
256     ##
257     # set the delegate bit to 'delegate' on
258     # all privileges
259     #
260     # @param delegate boolean (True or False)
261
262     def delegate_all_privileges(self, delegate):
263         for right in self.rights:
264             right.delegate = delegate
265
266     ##
267     # true if all privileges have delegate bit set true
268     # false otherwise
269
270     def get_all_delegate(self):
271         for right in self.rights:
272             if not right.delegate:
273                 return False
274         return True
275
276     def pretty_rights(self):
277         return "<Rights{}>".format(";".join(["{}".format(r) for r in self.rights]))