source: fedd/federation/authorizer.py @ a31b94d

axis_examplecompt_changesinfo-opsversion-2.00version-3.01version-3.02
Last change on this file since a31b94d was a31b94d, checked in by Ted Faber <faber@…>, 15 years ago

tweaks to the abac authorizer

  • Property mode set to 100644
File size: 6.7 KB
Line 
1#/usr/local/bin/python
2
3from fedid import fedid
4from remote_service import service_caller
5from abac_remote_service import abac_service_caller
6from service_error import service_error
7
8import sys
9
10class authorizer_base:
11    """
12    Classes based on this one keep track of authorization attributes for the
13    various modules running.  This base class holds some utility functions that
14    they all potentially use.
15    """
16
17    # general error exception for badly formed names. 
18    class bad_name(RuntimeError): pass
19
20    @staticmethod
21    def auth_name(name):
22        """
23        Helper to convert a non-unicode local name to a unicode string.  Mixed
24        representations can needlessly confuse the authorizer.
25        """
26        if isinstance(name, basestring):
27            if not isinstance(name, unicode): return unicode(name)
28            else: return name
29        else: return name
30
31    @staticmethod
32    def valid_name(name):
33        """
34        Ensure that the given name is valid.  A valid name can either be a
35        triple of strings and fedids representing one of our generalized Emulab
36        names or a single fedid.  Compound names can include wildcards (None)
37        and must anchor to a fedid at their highest level (unless they're all
38        None)
39
40        This either returns True or throws an exception.  More an assertion
41        than a function.
42        """
43        if isinstance(name, tuple) and len(name) == 3:
44            for n in name:
45                if n: 
46                    if not (isinstance(n, basestring) or isinstance(n, fedid)):
47                        raise authorizer_base.bad_name(
48                                "names must be either a triple or a fedid")
49            for n in name: 
50                if n:
51                    if isinstance(n, fedid):
52                        return True
53                    else:
54                        raise authorizer_base.bad_name(
55                                "Compound names must be " + \
56                                "rooted in fedids: %s" % str(name))
57
58            return True
59        elif isinstance(name, fedid):
60            return True
61        else:
62            raise authorizer_base.bad_name(
63                    "Names must be a triple or a fedid (%s)" % name)
64
65
66class authorizer(authorizer_base):
67    """
68    This class keeps track of authorization attributes for the various modules
69    running.  When it gets smarter it will be the basis for a real
70    attribute-based authentication system.
71    """
72    def __init__(self, def_attr="testbed"):
73        self.attrs = { }
74        self.globals=set()
75
76    def set_attribute(self, name, attr):
77        """
78        Attach attr to name.  Multiple attrs can be attached.
79        """
80        self.valid_name(name)
81        if isinstance(name, tuple):
82            aname = tuple([ self.auth_name(n) for n in name])
83        else:
84            aname = self.auth_name(name)
85
86        if not self.attrs.has_key(aname):
87            self.attrs[aname] = set()
88        self.attrs[aname].add(attr)
89
90    def unset_attribute(self, name, attr):
91        """
92        Remove an attribute from name
93        """
94        self.valid_name(name)
95        if isinstance(name, tuple):
96            aname = tuple([ self.auth_name(n) for n in name])
97        else:
98            aname = self.auth_name(name)
99
100        attrs = self.attrs.get(aname, None)
101        if attrs: attrs.discard(attr)
102
103    def check_attribute(self, name, attr):
104        """
105        Return True if name has attr (or if attr is global).  Tuple names match
106        any tuple name that matches all names present and has None entries in
107        other fileds.  For tuple names True implies that there is a matching
108        tuple name with the attribute.
109        """
110        def tup(tup, i, p):
111            mask = 1 << i
112            if p & mask : return authorizer.auth_name(tup[i])
113            else: return None
114
115        self.valid_name(name)
116        if attr in self.globals:
117            return True
118
119        if isinstance(name, tuple):
120            for p in range(0,8):
121                lookup = ( tup(name, 0, p), tup(name,1, p), tup(name,2,p))
122                if self.attrs.has_key(lookup):
123                    if attr in self.attrs[lookup]:
124                        return True
125        else:
126            return  attr in self.attrs.get(self.auth_name(name), set())
127
128    def set_global_attribute(self, attr):
129        """
130        Set a global attribute.  All names, even those otherwise unknown to the
131        authorizer have this attribute.
132        """
133        self.globals.add(attr)
134
135    def unset_global_attribute(self, attr):
136        """
137        Remove a global attribute
138        """
139
140        self.globals.discard(attr)
141
142class abac_authorizer(authorizer_base):
143    """
144    Use the ABAC authorization system to make attribute decisions.
145    """
146
147    def __init__(self, url=None, cert_file=None, cert_pwd=None,
148            trusted_certs=None, me=None):
149        self.call_CreateContext = abac_service_caller('CreateContext')
150        self.call_CredentialUpdate = abac_service_caller('CredentialUpdate')
151        self.call_Access = abac_service_caller('Access')
152        self.globals = set()
153
154        self.url = url
155        self.cert_file = cert_file
156        self.cert_pwd = cert_pwd
157        self.trusted_certs = trusted_certs
158        self.me = me
159        self.contextID = me.get_hexstr()
160        req = { 'contextID' : self.contextID}
161        # May throw a service error
162        resp = self.call_CreateContext(self.url, req, self.cert_file,
163                self.cert_pwd, self.trusted_certs, tracefile=sys.stderr)
164
165        if resp.has_key('CreateContextResponseBody'):
166            resp =  resp['CreateContextResponseBody']
167            if resp.has_key('contextID'):
168                self.contextID = resp['contextID']
169            else:
170                raise service_error(service_error.internal, 
171                        "No context ID for the new authorization context")
172        else:
173            raise service_error(service_error.internal, 
174                    "Bad response to creating service context")
175
176    def set_attribute(self, name, attr):
177        self.valid_name(name)
178        if isinstance(name, tuple):
179            if not isinstance(name[0], fedid):
180                raise service_error(service_error.internal, 
181                        "Triple not anchored in testbed")
182            if not name[1] and not name[2]:
183                raise service_error(service_error.internal,
184                        "Anonymous access not yet permitted")
185
186            preconditions = "^".join(
187                    [ "%s.%s" % (name[0], self.auth_name(n)) \
188                            for n in name[1:3] if n ])
189
190            req = { 
191                    'credential': [ '%s.%s<--%s' % \
192                            ( self.me, attr, preconditions) ],
193                    'context': { 'contextID': self.contextID  }
194                }
195        else:
196            req = { 
197                    'credential': [ '%s.%s<--%s' % \
198                            ( self.me, attr, self.auth_name(name)) ],
199                    'context': { 'contextID': self.contextID }
200                }
201        print req
202        self.call_CredentialUpdate(self.url, req, self.cert_file, 
203                self.cert_pwd, self.trusted_certs, tracefile=sys.stderr)
204
205    def check_attribute(self, name, attr):
206        self.valid_name(name)
207        if attr in self.globals: return True
208
209        if isinstance(name, tuple):
210            raise service_error(service_error.internal, 
211                    "Only fedids permitted here")
212        req = {
213                'goal': '%s:%s<<---%s' % (self.me, attr, name),
214                'context': { 'contextID': self.contextID }
215            }
216        resp = self.call_Access(self.url, req, self.cert_file, self.cert_pwd, 
217                self.trusted_certs)
218        if resp.has_key('AccessResponseBody'):
219            resp = resp['AccessResponseBody']
220
221        result = resp.get('result', None)
222        return (result and result.lower() == 'true')
223
224    def set_global_attribute(self, attr):
225        """
226        Set a global attribute.  All names, even those otherwise unknown to the
227        authorizer have this attribute.
228        """
229        self.globals.add(attr)
230
231    def unset_global_attribute(self, attr):
232        """
233        Remove a global attribute
234        """
235
236        self.globals.discard(attr)
237
Note: See TracBrowser for help on using the repository browser.