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