source: fedd/federation/authorizer.py @ 7183b48

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

correct setup for ABAC

  • Property mode set to 100644
File size: 6.6 KB
RevLine 
[3f6bc5f]1#/usr/local/bin/python
2
3from fedid import fedid
[a31b94d]4from remote_service import service_caller
5from abac_remote_service import abac_service_caller
6from service_error import service_error
[3f6bc5f]7
[a31b94d]8import sys
9
10class authorizer_base:
[3f6bc5f]11    """
[a31b94d]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.
[3f6bc5f]15    """
[a31b94d]16
[3f6bc5f]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
[a31b94d]31    @staticmethod
32    def valid_name(name):
[3f6bc5f]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)):
[a31b94d]47                        raise authorizer_base.bad_name(
48                                "names must be either a triple or a fedid")
[3f6bc5f]49            for n in name: 
50                if n:
51                    if isinstance(n, fedid):
52                        return True
53                    else:
[a31b94d]54                        raise authorizer_base.bad_name(
55                                "Compound names must be " + \
[3f6bc5f]56                                "rooted in fedids: %s" % str(name))
57
58            return True
59        elif isinstance(name, fedid):
60            return True
61        else:
[a31b94d]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()
[3f6bc5f]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        """
[05191a6]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.
[3f6bc5f]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)
[05191a6]116        if attr in self.globals:
117            return True
[3f6bc5f]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
[05191a6]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)
[3f6bc5f]141
[a31b94d]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
[f760064]159        req = { 'context': { 'self': me.get_hexstr()} }
[a31b94d]160        # May throw a service error
161        resp = self.call_CreateContext(self.url, req, self.cert_file,
162                self.cert_pwd, self.trusted_certs, tracefile=sys.stderr)
163
164        if resp.has_key('CreateContextResponseBody'):
165            resp =  resp['CreateContextResponseBody']
166            if resp.has_key('contextID'):
167                self.contextID = resp['contextID']
168            else:
169                raise service_error(service_error.internal, 
170                        "No context ID for the new authorization context")
171        else:
172            raise service_error(service_error.internal, 
173                    "Bad response to creating service context")
174
175    def set_attribute(self, name, attr):
176        self.valid_name(name)
177        if isinstance(name, tuple):
178            if not isinstance(name[0], fedid):
179                raise service_error(service_error.internal, 
180                        "Triple not anchored in testbed")
181            if not name[1] and not name[2]:
182                raise service_error(service_error.internal,
183                        "Anonymous access not yet permitted")
184
185            preconditions = "^".join(
186                    [ "%s.%s" % (name[0], self.auth_name(n)) \
187                            for n in name[1:3] if n ])
188
189            req = { 
190                    'credential': [ '%s.%s<--%s' % \
191                            ( self.me, attr, preconditions) ],
192                    'context': { 'contextID': self.contextID  }
193                }
194        else:
195            req = { 
196                    'credential': [ '%s.%s<--%s' % \
197                            ( self.me, attr, self.auth_name(name)) ],
198                    'context': { 'contextID': self.contextID }
199                }
200        print req
201        self.call_CredentialUpdate(self.url, req, self.cert_file, 
202                self.cert_pwd, self.trusted_certs, tracefile=sys.stderr)
203
204    def check_attribute(self, name, attr):
205        self.valid_name(name)
206        if attr in self.globals: return True
207
208        if isinstance(name, tuple):
209            raise service_error(service_error.internal, 
210                    "Only fedids permitted here")
211        req = {
212                'goal': '%s:%s<<---%s' % (self.me, attr, name),
213                'context': { 'contextID': self.contextID }
214            }
215        resp = self.call_Access(self.url, req, self.cert_file, self.cert_pwd, 
216                self.trusted_certs)
217        if resp.has_key('AccessResponseBody'):
218            resp = resp['AccessResponseBody']
219
220        result = resp.get('result', None)
221        return (result and result.lower() == 'true')
222
223    def set_global_attribute(self, attr):
224        """
225        Set a global attribute.  All names, even those otherwise unknown to the
226        authorizer have this attribute.
227        """
228        self.globals.add(attr)
229
230    def unset_global_attribute(self, attr):
231        """
232        Remove a global attribute
233        """
234
235        self.globals.discard(attr)
236
Note: See TracBrowser for help on using the repository browser.