~corey.bryant/ubuntu/wily/python-pysaml2/3.0.0

« back to all changes in this revision

Viewing changes to src/saml2/assertion.py

  • Committer: Package Import Robot
  • Author(s): Thomas Goirand
  • Date: 2014-09-08 16:11:53 UTC
  • Revision ID: package-import@ubuntu.com-20140908161153-vms9r4gu0oz4v4ai
Tags: upstream-2.0.0
ImportĀ upstreamĀ versionĀ 2.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# -*- coding: utf-8 -*-
 
3
#
 
4
# Copyright (C) 2010-2011 UmeĆ„ University
 
5
#
 
6
# Licensed under the Apache License, Version 2.0 (the "License");
 
7
# you may not use this file except in compliance with the License.
 
8
# You may obtain a copy of the License at
 
9
#
 
10
#            http://www.apache.org/licenses/LICENSE-2.0
 
11
#
 
12
# Unless required by applicable law or agreed to in writing, software
 
13
# distributed under the License is distributed on an "AS IS" BASIS,
 
14
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
15
# See the License for the specific language governing permissions and
 
16
# limitations under the License.
 
17
import importlib
 
18
import logging
 
19
 
 
20
import re
 
21
from saml2.saml import NAME_FORMAT_URI
 
22
import xmlenc
 
23
 
 
24
from saml2 import saml
 
25
 
 
26
from saml2.time_util import instant, in_a_while
 
27
from saml2.attribute_converter import from_local
 
28
from saml2.s_utils import sid, MissingValue
 
29
from saml2.s_utils import factory
 
30
from saml2.s_utils import assertion_factory
 
31
 
 
32
 
 
33
logger = logging.getLogger(__name__)
 
34
 
 
35
 
 
36
def _filter_values(vals, vlist=None, must=False):
 
37
    """ Removes values from *vals* that does not appear in vlist
 
38
    
 
39
    :param vals: The values that are to be filtered
 
40
    :param vlist: required or optional value
 
41
    :param must: Whether the allowed values must appear
 
42
    :return: The set of values after filtering
 
43
    """
 
44
    
 
45
    if not vlist:  # No value specified equals any value
 
46
        return vals
 
47
    
 
48
    if isinstance(vlist, basestring):
 
49
        vlist = [vlist]
 
50
        
 
51
    res = []
 
52
    
 
53
    for val in vlist:
 
54
        if val in vals:
 
55
            res.append(val)
 
56
    
 
57
    if must:
 
58
        if res:
 
59
            return res
 
60
        else:
 
61
            raise MissingValue("Required attribute value missing")
 
62
    else:
 
63
        return res
 
64
 
 
65
 
 
66
def _match(attr, ava):
 
67
    if attr in ava:
 
68
        return attr
 
69
 
 
70
    _la = attr.lower()
 
71
    if _la in ava:
 
72
        return _la
 
73
 
 
74
    for _at in ava.keys():
 
75
        if _at.lower() == _la:
 
76
            return _at
 
77
 
 
78
    return None
 
79
 
 
80
 
 
81
def filter_on_attributes(ava, required=None, optional=None):
 
82
    """ Filter
 
83
    
 
84
    :param ava: An attribute value assertion as a dictionary
 
85
    :param required: list of RequestedAttribute instances defined to be 
 
86
        required
 
87
    :param optional: list of RequestedAttribute instances defined to be
 
88
        optional
 
89
    :return: The modified attribute value assertion
 
90
    """
 
91
    res = {}
 
92
    
 
93
    if required is None:
 
94
        required = []
 
95
 
 
96
    for attr in required:
 
97
        found = False
 
98
        nform = ""
 
99
        for nform in ["friendly_name", "name"]:
 
100
            try:
 
101
                _fn = _match(attr[nform], ava)
 
102
            except KeyError:
 
103
                pass
 
104
            else:
 
105
                if _fn:
 
106
                    try:
 
107
                        values = [av["text"] for av in attr["attribute_value"]]
 
108
                    except KeyError:
 
109
                        values = []
 
110
                    res[_fn] = _filter_values(ava[_fn], values, True)
 
111
                    found = True
 
112
                    break
 
113
 
 
114
        if not found:
 
115
            raise MissingValue("Required attribute missing: '%s'" % (
 
116
                attr[nform],))
 
117
 
 
118
    if optional is None:
 
119
        optional = []
 
120
 
 
121
    for attr in optional:
 
122
        for nform in ["friendly_name", "name"]:
 
123
            if nform in attr:
 
124
                _fn = _match(attr[nform], ava)
 
125
                if _fn:
 
126
                    try:
 
127
                        values = [av["text"] for av in attr["attribute_value"]]
 
128
                    except KeyError:
 
129
                        values = []
 
130
                    try:
 
131
                        res[_fn].extend(_filter_values(ava[_fn], values))
 
132
                    except KeyError:
 
133
                        res[_fn] = _filter_values(ava[_fn], values)
 
134
    
 
135
    return res
 
136
 
 
137
 
 
138
def filter_on_demands(ava, required=None, optional=None):
 
139
    """ Never return more than is needed. Filters out everything
 
140
    the server is prepared to return but the receiver doesn't ask for
 
141
    
 
142
    :param ava: Attribute value assertion as a dictionary
 
143
    :param required: Required attributes
 
144
    :param optional: Optional attributes
 
145
    :return: The possibly reduced assertion
 
146
    """
 
147
    
 
148
    # Is all what's required there:
 
149
    if required is None:
 
150
        required = {}
 
151
 
 
152
    lava = dict([(k.lower(), k) for k in ava.keys()])
 
153
 
 
154
    for attr, vals in required.items():
 
155
        attr = attr.lower()
 
156
        if attr in lava:
 
157
            if vals:
 
158
                for val in vals:
 
159
                    if val not in ava[lava[attr]]:
 
160
                        raise MissingValue(
 
161
                            "Required attribute value missing: %s,%s" % (attr,
 
162
                                                                         val))
 
163
        else:
 
164
            raise MissingValue("Required attribute missing: %s" % (attr,))
 
165
 
 
166
    if optional is None:
 
167
        optional = {}
 
168
 
 
169
    oka = [k.lower() for k in required.keys()]
 
170
    oka.extend([k.lower() for k in optional.keys()])
 
171
 
 
172
    # OK, so I can imaging releasing values that are not absolutely necessary
 
173
    # but not attributes that are not asked for.
 
174
    for attr in lava.keys():
 
175
        if attr not in oka:
 
176
            del ava[lava[attr]]
 
177
    
 
178
    return ava
 
179
 
 
180
 
 
181
def filter_on_wire_representation(ava, acs, required=None, optional=None):
 
182
    """
 
183
    :param ava: A dictionary with attributes and values
 
184
    :param acs: List of tuples (Attribute Converter name,
 
185
        Attribute Converter instance)
 
186
    :param required: A list of saml.Attributes
 
187
    :param optional: A list of saml.Attributes
 
188
    :return: Dictionary of expected/wanted attributes and values
 
189
    """
 
190
    acsdic = dict([(ac.name_format, ac) for ac in acs])
 
191
 
 
192
    if required is None:
 
193
        required = []
 
194
    if optional is None:
 
195
        optional = []
 
196
 
 
197
    res = {}
 
198
    for attr, val in ava.items():
 
199
        done = False
 
200
        for req in required:
 
201
            try:
 
202
                _name = acsdic[req.name_format]._to[attr]
 
203
                if _name == req.name:
 
204
                    res[attr] = val
 
205
                    done = True
 
206
            except KeyError:
 
207
                pass
 
208
        if done:
 
209
            continue
 
210
        for opt in optional:
 
211
            try:
 
212
                _name = acsdic[opt.name_format]._to[attr]
 
213
                if _name == opt.name:
 
214
                    res[attr] = val
 
215
                    break
 
216
            except KeyError:
 
217
                pass
 
218
 
 
219
    return res
 
220
 
 
221
 
 
222
def filter_attribute_value_assertions(ava, attribute_restrictions=None):
 
223
    """ Will weed out attribute values and values according to the
 
224
    rules defined in the attribute restrictions. If filtering results in
 
225
    an attribute without values, then the attribute is removed from the
 
226
    assertion.
 
227
    
 
228
    :param ava: The incoming attribute value assertion (dictionary)
 
229
    :param attribute_restrictions: The rules that govern which attributes
 
230
        and values that are allowed. (dictionary)
 
231
    :return: The modified attribute value assertion
 
232
    """
 
233
    if not attribute_restrictions:
 
234
        return ava
 
235
    
 
236
    for attr, vals in ava.items():
 
237
        _attr = attr.lower()
 
238
        try:
 
239
            _rests = attribute_restrictions[_attr]
 
240
        except KeyError:
 
241
            del ava[attr]
 
242
        else:
 
243
            if _rests is None:
 
244
                continue
 
245
            if isinstance(vals, basestring):
 
246
                vals = [vals]
 
247
            rvals = []
 
248
            for restr in _rests:
 
249
                for val in vals:
 
250
                    if restr.match(val):
 
251
                        rvals.append(val)
 
252
 
 
253
            if rvals:
 
254
                ava[attr] = list(set(rvals))
 
255
            else:
 
256
                del ava[attr]
 
257
    return ava
 
258
 
 
259
 
 
260
def restriction_from_attribute_spec(attributes):
 
261
    restr = {}
 
262
    for attribute in attributes:
 
263
        restr[attribute.name] = {}
 
264
        for val in attribute.attribute_value:
 
265
            if not val.text:
 
266
                restr[attribute.name] = None
 
267
                break
 
268
            else:
 
269
                restr[attribute.name] = re.compile(val.text)
 
270
    return restr
 
271
 
 
272
 
 
273
def post_entity_categories(maps, **kwargs):
 
274
    restrictions = {}
 
275
    if kwargs["mds"]:
 
276
        try:
 
277
            ecs = kwargs["mds"].entity_categories(kwargs["sp_entity_id"])
 
278
        except KeyError:
 
279
            for ec_map in maps:
 
280
                for attr in ec_map[""]:
 
281
                    restrictions[attr] = None
 
282
        else:
 
283
            for ec_map in maps:
 
284
                for key, val in ec_map.items():
 
285
                    if key == "":  # always released
 
286
                        attrs = val
 
287
                    elif isinstance(key, tuple):
 
288
                        attrs = val
 
289
                        for _key in key:
 
290
                            try:
 
291
                                assert _key in ecs
 
292
                            except AssertionError:
 
293
                                attrs = []
 
294
                                break
 
295
                    elif key in ecs:
 
296
                        attrs = val
 
297
                    else:
 
298
                        attrs = []
 
299
 
 
300
                    for attr in attrs:
 
301
                        restrictions[attr] = None
 
302
 
 
303
    return restrictions
 
304
 
 
305
 
 
306
class Policy(object):
 
307
    """ handles restrictions on assertions """
 
308
    
 
309
    def __init__(self, restrictions=None):
 
310
        if restrictions:
 
311
            self.compile(restrictions)
 
312
        else:
 
313
            self._restrictions = None
 
314
    
 
315
    def compile(self, restrictions):
 
316
        """ This is only for IdPs or AAs, and it's about limiting what
 
317
        is returned to the SP.
 
318
        In the configuration file, restrictions on which values that
 
319
        can be returned are specified with the help of regular expressions.
 
320
        This function goes through and pre-compiles the regular expressions.
 
321
        
 
322
        :param restrictions:
 
323
        :return: The assertion with the string specification replaced with
 
324
            a compiled regular expression.
 
325
        """
 
326
        
 
327
        self._restrictions = restrictions.copy()
 
328
        
 
329
        for who, spec in self._restrictions.items():
 
330
            if spec is None:
 
331
                continue
 
332
            try:
 
333
                items = spec["entity_categories"]
 
334
            except KeyError:
 
335
                pass
 
336
            else:
 
337
                ecs = []
 
338
                for cat in items:
 
339
                    _mod = importlib.import_module(
 
340
                        "saml2.entity_category.%s" % cat)
 
341
                    _ec = {}
 
342
                    for key, items in _mod.RELEASE.items():
 
343
                        _ec[key] = [k.lower() for k in items]
 
344
                    ecs.append(_ec)
 
345
                spec["entity_categories"] = ecs
 
346
            try:
 
347
                restr = spec["attribute_restrictions"]
 
348
            except KeyError:
 
349
                continue
 
350
 
 
351
            if restr is None:
 
352
                continue
 
353
 
 
354
            _are = {}
 
355
            for key, values in restr.items():
 
356
                if not values:
 
357
                    _are[key.lower()] = None
 
358
                    continue
 
359
 
 
360
                _are[key.lower()] = [re.compile(value) for value in values]
 
361
            spec["attribute_restrictions"] = _are
 
362
        logger.debug("policy restrictions: %s" % self._restrictions)
 
363
 
 
364
        return self._restrictions
 
365
 
 
366
    def get(self, attribute, sp_entity_id, default=None, post_func=None,
 
367
            **kwargs):
 
368
        """
 
369
 
 
370
        :param attribute:
 
371
        :param sp_entity_id:
 
372
        :param default:
 
373
        :param post_func:
 
374
        :return:
 
375
        """
 
376
        if not self._restrictions:
 
377
            return default
 
378
 
 
379
        try:
 
380
            try:
 
381
                val = self._restrictions[sp_entity_id][attribute]
 
382
            except KeyError:
 
383
                try:
 
384
                    val = self._restrictions["default"][attribute]
 
385
                except KeyError:
 
386
                    val = None
 
387
        except KeyError:
 
388
            val = None
 
389
 
 
390
        if val is None:
 
391
            return default
 
392
        elif post_func:
 
393
            return post_func(val, sp_entity_id=sp_entity_id, **kwargs)
 
394
        else:
 
395
            return val
 
396
 
 
397
    def get_nameid_format(self, sp_entity_id):
 
398
        """ Get the NameIDFormat to used for the entity id 
 
399
        :param: The SP entity ID
 
400
        :retur: The format
 
401
        """
 
402
        return self.get("nameid_format", sp_entity_id,
 
403
                        saml.NAMEID_FORMAT_TRANSIENT)
 
404
 
 
405
    def get_name_form(self, sp_entity_id):
 
406
        """ Get the NameFormat to used for the entity id 
 
407
        :param: The SP entity ID
 
408
        :retur: The format
 
409
        """
 
410
 
 
411
        return self.get("name_format", sp_entity_id, NAME_FORMAT_URI)
 
412
 
 
413
    def get_lifetime(self, sp_entity_id):
 
414
        """ The lifetime of the assertion 
 
415
        :param sp_entity_id: The SP entity ID
 
416
        :param: lifetime as a dictionary 
 
417
        """
 
418
        # default is a hour
 
419
        return self.get("lifetime", sp_entity_id, {"hours": 1})
 
420
 
 
421
    def get_attribute_restrictions(self, sp_entity_id):
 
422
        """ Return the attribute restriction for SP that want the information
 
423
        
 
424
        :param sp_entity_id: The SP entity ID
 
425
        :return: The restrictions
 
426
        """
 
427
 
 
428
        return self.get("attribute_restrictions", sp_entity_id)
 
429
 
 
430
    def entity_category_attributes(self, ec):
 
431
        if not self._restrictions:
 
432
            return None
 
433
 
 
434
        ec_maps = self._restrictions["default"]["entity_categories"]
 
435
        for ec_map in ec_maps:
 
436
            try:
 
437
                return ec_map[ec]
 
438
            except KeyError:
 
439
                pass
 
440
        return []
 
441
 
 
442
    def get_entity_categories(self, sp_entity_id, mds):
 
443
        """
 
444
 
 
445
        :param sp_entity_id:
 
446
        :param mds: MetadataStore instance
 
447
        :return: A dictionary with restrictions
 
448
        """
 
449
 
 
450
        kwargs = {"mds": mds}
 
451
 
 
452
        return self.get("entity_categories", sp_entity_id, default={},
 
453
                        post_func=post_entity_categories, **kwargs)
 
454
 
 
455
    def not_on_or_after(self, sp_entity_id):
 
456
        """ When the assertion stops being valid, should not be
 
457
        used after this time.
 
458
        
 
459
        :param sp_entity_id: The SP entity ID
 
460
        :return: String representation of the time
 
461
        """
 
462
        
 
463
        return in_a_while(**self.get_lifetime(sp_entity_id))
 
464
    
 
465
    def filter(self, ava, sp_entity_id, mdstore, required=None, optional=None):
 
466
        """ What attribute and attribute values returns depends on what
 
467
        the SP has said it wants in the request or in the metadata file and
 
468
        what the IdP/AA wants to release. An assumption is that what the SP
 
469
        asks for overrides whatever is in the metadata. But of course the
 
470
        IdP never releases anything it doesn't want to.
 
471
        
 
472
        :param ava: The information about the subject as a dictionary
 
473
        :param sp_entity_id: The entity ID of the SP
 
474
        :param mdstore: A Metadata store
 
475
        :param required: Attributes that the SP requires in the assertion
 
476
        :param optional: Attributes that the SP regards as optional
 
477
        :return: A possibly modified AVA
 
478
        """
 
479
 
 
480
        _rest = self.get_attribute_restrictions(sp_entity_id)
 
481
        if _rest is None:
 
482
            _rest = self.get_entity_categories(sp_entity_id, mdstore)
 
483
        logger.debug("filter based on: %s" % _rest)
 
484
        ava = filter_attribute_value_assertions(ava, _rest)
 
485
        
 
486
        if required or optional:
 
487
            ava = filter_on_attributes(ava, required, optional)
 
488
        
 
489
        return ava
 
490
    
 
491
    def restrict(self, ava, sp_entity_id, metadata=None):
 
492
        """ Identity attribute names are expected to be expressed in
 
493
        the local lingo (== friendlyName)
 
494
        
 
495
        :return: A filtered ava according to the IdPs/AAs rules and
 
496
            the list of required/optional attributes according to the SP.
 
497
            If the requirements can't be met an exception is raised.
 
498
        """
 
499
        if metadata:
 
500
            spec = metadata.attribute_requirement(sp_entity_id)
 
501
            if spec:
 
502
                ava = self.filter(ava, sp_entity_id, metadata,
 
503
                                  spec["required"], spec["optional"])
 
504
 
 
505
        return self.filter(ava, sp_entity_id, metadata, [], [])
 
506
 
 
507
    def conditions(self, sp_entity_id):
 
508
        """ Return a saml.Condition instance
 
509
        
 
510
        :param sp_entity_id: The SP entity ID
 
511
        :return: A saml.Condition instance
 
512
        """
 
513
        return factory(saml.Conditions,
 
514
                       not_before=instant(),
 
515
                       # How long might depend on who's getting it
 
516
                       not_on_or_after=self.not_on_or_after(sp_entity_id),
 
517
                       audience_restriction=[factory(
 
518
                           saml.AudienceRestriction,
 
519
                           audience=[factory(saml.Audience,
 
520
                                             text=sp_entity_id)])])
 
521
 
 
522
    def get_sign(self, sp_entity_id):
 
523
        """
 
524
        Possible choices
 
525
        "sign": ["response", "assertion", "on_demand"]
 
526
 
 
527
        :param sp_entity_id:
 
528
        :return:
 
529
        """
 
530
 
 
531
        return self.get("sign", sp_entity_id, [])
 
532
 
 
533
 
 
534
class EntityCategories(object):
 
535
    pass
 
536
 
 
537
 
 
538
class Assertion(dict):
 
539
    """ Handles assertions about subjects """
 
540
    
 
541
    def __init__(self, dic=None):
 
542
        dict.__init__(self, dic)
 
543
    
 
544
    def _authn_context_decl(self, decl, authn_auth=None):
 
545
        """
 
546
        Construct the authn context with a authn context declaration
 
547
        :param decl: The authn context declaration
 
548
        :param authn_auth: Authenticating Authority
 
549
        :return: An AuthnContext instance
 
550
        """
 
551
        return factory(saml.AuthnContext,
 
552
                       authn_context_decl=decl,
 
553
                       authenticating_authority=factory(
 
554
                           saml.AuthenticatingAuthority, text=authn_auth))
 
555
 
 
556
    def _authn_context_decl_ref(self, decl_ref, authn_auth=None):
 
557
        """
 
558
        Construct the authn context with a authn context declaration reference
 
559
        :param decl_ref: The authn context declaration reference
 
560
        :param authn_auth: Authenticating Authority
 
561
        :return: An AuthnContext instance
 
562
        """
 
563
        return factory(saml.AuthnContext,
 
564
                       authn_context_decl_ref=decl_ref,
 
565
                       authenticating_authority=factory(
 
566
                           saml.AuthenticatingAuthority, text=authn_auth))
 
567
 
 
568
    @staticmethod
 
569
    def _authn_context_class_ref(authn_class, authn_auth=None):
 
570
        """
 
571
        Construct the authn context with a authn context class reference
 
572
        :param authn_class: The authn context class reference
 
573
        :param authn_auth: Authenticating Authority
 
574
        :return: An AuthnContext instance
 
575
        """
 
576
        cntx_class = factory(saml.AuthnContextClassRef, text=authn_class)
 
577
        if authn_auth:
 
578
            return factory(saml.AuthnContext, 
 
579
                           authn_context_class_ref=cntx_class,
 
580
                           authenticating_authority=factory(
 
581
                               saml.AuthenticatingAuthority, text=authn_auth))
 
582
        else:
 
583
            return factory(saml.AuthnContext,
 
584
                           authn_context_class_ref=cntx_class)
 
585
        
 
586
    def _authn_statement(self, authn_class=None, authn_auth=None,
 
587
                         authn_decl=None, authn_decl_ref=None, authn_instant="",
 
588
                         subject_locality=""):
 
589
        """
 
590
        Construct the AuthnStatement
 
591
        :param authn_class: Authentication Context Class reference
 
592
        :param authn_auth: Authenticating Authority
 
593
        :param authn_decl: Authentication Context Declaration
 
594
        :param authn_decl_ref: Authentication Context Declaration reference
 
595
        :param authn_instant: When the Authentication was performed.
 
596
            Assumed to be seconds since the Epoch.
 
597
        :param subject_locality: Specifies the DNS domain name and IP address
 
598
            for the system from which the assertion subject was apparently
 
599
            authenticated.
 
600
        :return: An AuthnContext instance
 
601
        """
 
602
        if authn_instant:
 
603
            _instant = instant(time_stamp=authn_instant)
 
604
        else:
 
605
            _instant = instant()
 
606
 
 
607
        if authn_class:
 
608
            res = factory(
 
609
                saml.AuthnStatement,
 
610
                authn_instant=_instant,
 
611
                session_index=sid(),
 
612
                authn_context=self._authn_context_class_ref(
 
613
                    authn_class, authn_auth))
 
614
        elif authn_decl:
 
615
            res = factory(
 
616
                saml.AuthnStatement,
 
617
                authn_instant=_instant,
 
618
                session_index=sid(),
 
619
                authn_context=self._authn_context_decl(authn_decl, authn_auth))
 
620
        elif authn_decl_ref:
 
621
            res = factory(
 
622
                saml.AuthnStatement,
 
623
                authn_instant=_instant,
 
624
                session_index=sid(),
 
625
                authn_context=self._authn_context_decl_ref(authn_decl_ref,
 
626
                                                           authn_auth))
 
627
        else:
 
628
            res = factory(
 
629
                saml.AuthnStatement,
 
630
                authn_instant=_instant,
 
631
                session_index=sid())
 
632
 
 
633
        if subject_locality:
 
634
            res.subject_locality = saml.SubjectLocality(text=subject_locality)
 
635
 
 
636
        return res
 
637
 
 
638
    def construct(self, sp_entity_id, in_response_to, consumer_url,
 
639
                  name_id, attrconvs, policy, issuer, authn_class=None,
 
640
                  authn_auth=None, authn_decl=None, encrypt=None,
 
641
                  sec_context=None, authn_decl_ref=None, authn_instant="",
 
642
                  subject_locality=""):
 
643
        """ Construct the Assertion 
 
644
        
 
645
        :param sp_entity_id: The entityid of the SP
 
646
        :param in_response_to: An identifier of the message, this message is 
 
647
            a response to
 
648
        :param consumer_url: The intended consumer of the assertion
 
649
        :param name_id: An NameID instance
 
650
        :param attrconvs: AttributeConverters
 
651
        :param policy: The policy that should be adhered to when replying
 
652
        :param issuer: Who is issuing the statement
 
653
        :param authn_class: The authentication class
 
654
        :param authn_auth: The authentication instance
 
655
        :param authn_decl: An Authentication Context declaration
 
656
        :param encrypt: Whether to encrypt parts or all of the Assertion
 
657
        :param sec_context: The security context used when encrypting
 
658
        :param authn_decl_ref: An Authentication Context declaration reference
 
659
        :param authn_instant: When the Authentication was performed
 
660
        :param subject_locality: Specifies the DNS domain name and IP address
 
661
            for the system from which the assertion subject was apparently
 
662
            authenticated.
 
663
        :return: An Assertion instance
 
664
        """
 
665
 
 
666
        if policy:
 
667
            _name_format = policy.get_name_form(sp_entity_id)
 
668
        else:
 
669
            _name_format = NAME_FORMAT_URI
 
670
 
 
671
        attr_statement = saml.AttributeStatement(attribute=from_local(
 
672
            attrconvs, self, _name_format))
 
673
 
 
674
        if encrypt == "attributes":
 
675
            for attr in attr_statement.attribute:
 
676
                enc = sec_context.encrypt(text="%s" % attr)
 
677
 
 
678
                encd = xmlenc.encrypted_data_from_string(enc)
 
679
                encattr = saml.EncryptedAttribute(encrypted_data=encd)
 
680
                attr_statement.encrypted_attribute.append(encattr)
 
681
 
 
682
            attr_statement.attribute = []
 
683
 
 
684
        # start using now and for some time
 
685
        conds = policy.conditions(sp_entity_id)
 
686
 
 
687
        if authn_auth or authn_class or authn_decl or authn_decl_ref:
 
688
            _authn_statement = self._authn_statement(authn_class, authn_auth,
 
689
                                                     authn_decl, authn_decl_ref,
 
690
                                                     authn_instant,
 
691
                                                     subject_locality)
 
692
        else:
 
693
            _authn_statement = None
 
694
 
 
695
 
 
696
        _ass = assertion_factory(
 
697
            issuer=issuer,
 
698
            conditions=conds,
 
699
            subject=factory(
 
700
                saml.Subject,
 
701
                name_id=name_id,
 
702
                subject_confirmation=[factory(
 
703
                    saml.SubjectConfirmation,
 
704
                    method=saml.SCM_BEARER,
 
705
                    subject_confirmation_data=factory(
 
706
                        saml.SubjectConfirmationData,
 
707
                        in_response_to=in_response_to,
 
708
                        recipient=consumer_url,
 
709
                        not_on_or_after=policy.not_on_or_after(sp_entity_id)))]
 
710
            ),
 
711
        )
 
712
 
 
713
        if _authn_statement:
 
714
            _ass.authn_statement = [_authn_statement]
 
715
 
 
716
        if not attr_statement.empty():
 
717
            _ass.attribute_statement=[attr_statement]
 
718
 
 
719
        return _ass
 
720
    
 
721
    def apply_policy(self, sp_entity_id, policy, metadata=None):
 
722
        """ Apply policy to the assertion I'm representing 
 
723
        
 
724
        :param sp_entity_id: The SP entity ID
 
725
        :param policy: The policy
 
726
        :param metadata: Metadata to use
 
727
        :return: The resulting AVA after the policy is applied
 
728
        """
 
729
        ava = policy.restrict(self, sp_entity_id, metadata)
 
730
        self.update(ava)
 
731
        return ava
 
 
b'\\ No newline at end of file'