Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Andreas Richter
2014-04-16 10:39:02 -04:00
73 changed files with 2001 additions and 1397 deletions

4
TODO
View File

@@ -1,3 +1,3 @@
1. Write documentations.
2. Write unittests for signature related utility methods.
1. Write documentation.
2. Write unit tests for signature related utility methods.
3. Complete saml2 message class.

View File

@@ -28,7 +28,7 @@ instance, the friendly name is used as the key.
Setup
-----
I you look in the example/sp directory of the distribution you will see
If you look in the example/sp directory of the distribution you will see
the necessary files:
application.py
@@ -64,7 +64,7 @@ it line by line::
"service": ["sp"],
Tells the software what type of services the software are suppost to
Tells the software what type of services the software is supposed to
supply. It is used to check for the
completeness of the configuration and also when constructing metadata from
the configuration. More about that later. Allowed values are: "sp"
@@ -119,13 +119,13 @@ building metadata. ::
#telephone_number
}]
Another piece of information that only is matters if you build and distribute
Another piece of information that only matters if you build and distribute
metadata.
So, now to that part. In order to allow the IdP to talk to you you may have
to provide the one running the IdP with a metadata file.
If you have a SP configuration file similar to the one I've walked you
through here, but with your information. You can make the metadata file
through here, but with your information, you can make the metadata file
by running the make_metadata script you can find in the tools directory.
Change directory to where you have the configuration file and do ::
@@ -138,7 +138,7 @@ Repoze configuration
--------------------
I'm not going through the INI file format here. You should read
`Middleware Responsibilities <http://static.repoze.org/whodocs/narr.html>`_
`Middleware Responsibilities <http://docs.repoze.org/who/2.0/middleware.html>`_
to get a good introduction to the concept.
The configuration of the pysaml2 part in the applications middleware are
@@ -178,16 +178,16 @@ Which means that the plugin is used in all phases.
The application
---------------
Is as said before extremly simple. The only thing that is connected to
the PySaml2 configuration are at the bottom, namely where the server are.
The app is, as said before, extremely simple. The only thing that is connected to
the PySaml2 configuration is at the bottom, namely where the server is.
You have to ascertain that this coincides with what is specified in the
PySaml2 configuration. Apart from that there really are no thing in
PySaml2 configuration. Apart from that there really is nothing in
application.py that demands that you use PySaml2 as middleware. If you
switched to using the LDAP or CAS plugins nothing would change in the
application. In the application configuration yes! But not in the application.
And that is really how it should be done.
There is one assumption and that is that the middleware plugin that gathers
information about the user places the extra information in as value on the
There is one assumption, and that is that the middleware plugin that gathers
information about the user places the extra information in as a value on the
"user" property in the dictionary found under the key "repoze.who.identity"
in the environment.

View File

@@ -3,15 +3,15 @@
Configuration of pySAML2 entities
=================================
Whether you plan to run a pySAML2 Service Provider, Identity provider or an
Whether you plan to run a pySAML2 Service Provider, Identity Provider or an
attribute authority you have to configure it. The format of the configuration
file is the same disregarding which type of service you plan to run.
What differs is some of the directives.
Below you will find a list of all the used directives in alphabetic order.
file is the same regardless of which type of service you plan to run.
What differs are some of the directives.
Below you will find a list of all the used directives in alphabetical order.
The configuration is written as a python module which contains a named
dictionary ("CONFIG") that contains the configuration directives.
The basic structure of the configuration file is therefor like this::
The basic structure of the configuration file is therefore like this::
from saml2 import BINDING_HTTP_REDIRECT
@@ -90,9 +90,9 @@ The attribute map module contains a MAP dictionary with three items. The
The *to* and *fro* sub-dictionaries then contain the mapping between the names.
As you see the format is again a python dictionary where the key is the
name to convert from and the value is the name to convert to.
name to convert from, and the value is the name to convert to.
Since *to* in most cases are the inverse of the *fro* file, the
Since *to* in most cases is the inverse of the *fro* file, the
software allowes you to only specify one of them and it will
automatically create the other.
@@ -111,7 +111,7 @@ contact_person
This is only used by *make_metadata.py* when it constructs the metadata for
the service described by the configuration file.
This is where you described who can be contacted if questions arises
This is where you describe who can be contacted if questions arise
about the service or if support is needed. The possible types are according to
the standard **technical**, **support**, **administrative**, **billing**
and **other**.::
@@ -148,7 +148,7 @@ Format::
The globally unique identifier of the entity.
.. note:: There is a recommendation that the entityid should point to a real
.. note:: It is recommended that the entityid should point to a real
webpage where the metadata for the entity can be found.
key_file
@@ -160,13 +160,13 @@ Format::
*key_file* is the name of a PEM formatted file that contains the private key
of the service. This is presently used both to encrypt/sign assertions and as
client key in a HTTPS session.
the client key in an HTTPS session.
metadata
^^^^^^^^
Contains a list of places where metadata can be found. This can be either
a file accessible on the server the service runs on or somewhere on the net.::
a file accessible on the server the service runs on, or somewhere on the net.::
"metadata" : {
"local": [
@@ -180,8 +180,8 @@ a file accessible on the server the service runs on or somewhere on the net.::
},
The above configuration means that the service should read two local
metadata files and on top of that load one from the net. To verify the
authenticity of the file downloaded from the net the local copy of the
metadata files, and on top of that load one from the net. To verify the
authenticity of the file downloaded from the net, the local copy of the
public key should be used.
This public key must be acquired by some out-of-band method.
@@ -205,7 +205,7 @@ Where you describe the organization responsible for the service.::
service
^^^^^^^
Which services the server will provide, those are combinations of "idp","sp"
Which services the server will provide; those are combinations of "idp", "sp"
and "aa".
So if a server is a Service Provider (SP) then the configuration
could look something like this::
@@ -228,13 +228,13 @@ could look something like this::
There are two options common to all services: 'name' and 'endpoints'.
The remaining options are specific to one or the other of the service types.
Which one is specified along side the name of the option
Which one is specified along side the name of the option.
timeslack
^^^^^^^^^
If your computer and another computer that you are communicating with are not
in synch regarding the computer clock. Then you here can state how big a
in synch regarding the computer clock, then here you can state how big a
difference you are prepared to accept.
.. note:: This will indiscriminately effect all time comparisons.
@@ -275,7 +275,7 @@ policy
If the server is an IdP and/or an AA then there might be reasons to do things
differently depending on who is asking; this is where that is specified.
The keys are 'default' and SP entity identifiers, default is used whenever
The keys are 'default' and SP entity identifiers. Default is used whenever
there is no entry for a specific SP. The reasoning is also that if there is
no default and only SP entity identifiers as keys, then the server will only
except connections from the specified SPs.
@@ -301,12 +301,12 @@ An example might be::
}
*lifetime*
is the maximum amount of time before the information should be
This is the maximum amount of time before the information should be
regarded as stale. In an Assertion this is represented in the NotOnOrAfter
attribute.
*attribute_restrictions*
By default there is no restrictions as to which attributes should be
return. Instead all the attributes and values that is gathered by the
return. Instead all the attributes and values that are gathered by the
database backends will be returned if nothing else is stated.
In the example above the SP with the entity identifier
"urn:mace:umu.se:saml:roland:sp"
@@ -332,7 +332,7 @@ regular expressions.::
}
}
Here only mail addresses that ends with ".umu.se" will be returned.
Here only mail addresses that end with ".umu.se" will be returned.
sp
^^
@@ -345,7 +345,7 @@ authn_requests_signed
Indicates if the Authentication Requests sent by this SP should be signed
by default. This can be overriden by application code for a specific call.
This set the AuthnRequestsSigned attribute of the SPSSODescriptor node.
This sets the AuthnRequestsSigned attribute of the SPSSODescriptor node
of the metadata so the IdP will know this SP preference.
Valid values are "true" or "false". Default value is "false".
@@ -362,9 +362,9 @@ Example::
idp
"""
Defines the set of IdPs that this SP is allowed to use. If not all the IdPs in
the metadata is allowed, then the value is expected to be a list with entity
identifiers for the allowed IdPs.
Defines the set of IdPs that this SP is allowed to use; if unset, all listed
IdPs may be used. If set, then the value is expected to be a list with entity
identifiers for the allowed IdPs.
A typical configuration, when the allowed set of IdPs are limited, would look
something like this::
@@ -376,8 +376,6 @@ something like this::
In this case the SP has only one IdP it can use.
If all IdPs present in the metadata loaded this directive must be left out.
optional_attributes
"""""""""""""""""""
@@ -415,7 +413,7 @@ want_assertions_signed
""""""""""""""""""""""
Indicates if this SP wants the IdP to send the assertions signed. This
set the WantAssertionsSigned attribute of the SPSSODescriptor node.
sets the WantAssertionsSigned attribute of the SPSSODescriptor node
of the metadata so the IdP will know this SP preference.
Valid values are "true" or "false". Default value is "true".
@@ -440,7 +438,7 @@ endpoints
"""""""""
Where the endpoints for the services provided are.
This directive has as value a dictionary with one of the following keys:
This directive has as value a dictionary with one or more of the following keys:
* artifact_resolution_service (aa, idp and sp)
* assertion_consumer_service (sp)
@@ -474,7 +472,7 @@ Indicates if this entity will sign the Logout Requests originated from it.
This can be overriden by application code for a specific call.
Valid values are "true" or "false". Default value is "false"
Valid values are "true" or "false". Default value is "false".
Example::
@@ -491,7 +489,7 @@ The name of a database where the map between a local identifier and
a distributed identifier is kept. By default this is a shelve database.
So if you just specify name, then a shelve database with that name
is created. On the other hand if you specify a tuple then the first
element in the tuple specifise which type of database you want to use
element in the tuple specifies which type of database you want to use
and the second element is the address of the database.
Example::
@@ -519,7 +517,7 @@ Gives information about common identifiers for virtual_organizations::
},
Keys in this dictionary are the identifiers for the virtual organizations.
The arguments per organization is 'nameid_format' and 'common_identifier'.
The arguments per organization are 'nameid_format' and 'common_identifier'.
Useful if all the IdPs and AAs that are involved in a virtual organization
have common attribute values for users that are part of the VO.
@@ -562,8 +560,8 @@ We start with a simple but fairly complete Service provider configuration::
}
This is the typical setup for a SP.
A metadata file to load is *always* needed, but it can of course be
containing anything from 1 up to many entity descriptions.
A metadata file to load is *always* needed, but it can of course
contain anything from 1 up to many entity descriptions.
------

View File

@@ -12,10 +12,10 @@ If you have not done it yet, read the :ref:`install`
Well, now you have it installed and you want to do something.
And I'm sorry to tell you this; but there isn't really a lot you can do with
this code on it's own.
this code on its own.
Sure you can send a AuthenticationRequest to an IdentityProvider or a
AttributeQuery to an AttributeAuthority but in order to get what they
AttributeQuery to an AttributeAuthority, but in order to get what they
return you have to sit behind a Web server. Well that is not really true since
the AttributeQuery would be over SOAP and you would get the result over the
connection you have to the AttributeAuthority.
@@ -29,7 +29,7 @@ But it can be used in a non-WSGI environment too.
So you will find descriptions of both cases here.
The configuration is the same disregarding whether you are using PySAML2 in a
The configuration is the same regardless of whether you are using PySAML2 in a
WSGI or non-WSGI environment.
.. toctree::

View File

@@ -13,7 +13,7 @@ pysaml2
PySAML2 is a pure python implementation of SAML2. It contains all necessary pieces for building a SAML2 service
provider or an identity provider. The distribution contains examples of both. Originally written to work in a WSGI
environment there are extensions that allow you to use it with other frameworks.
environment, there are extensions that allow you to use it with other frameworks.
Contents:
@@ -39,4 +39,4 @@ Indices and tables
<a href="https://github.com/rohe/pysaml2" class="github" target="_blank">
<img style="position: absolute; top: 0; right: 0; border: 0;" src="_static/ViewmeonGitHub.png" alt="View me on GitHub" class="github"/>
</a>
</a>

View File

@@ -53,5 +53,5 @@ The tests are based on the pypy test environment, so::
py.test
is what you should use. If you don't have py.test, get it it's part of pypy!
It's really good !
It's really good!

View File

@@ -2,4 +2,4 @@
rm -f ./code/*
sphinx-apidoc -F -o ../doc/code ../src
make clean
make html
make html

View File

@@ -1,30 +1,40 @@
This is a very simple setup just to check that all your gear are in order.
The setup consists of one IdP and one SP.
The IdP authenticates users by using a htpasswd plugin and gets the identity information
from the ini-plugin.
The setup consists of one IdP and one SP, in idp2/ and sp-wsgi/ respectively.
All this is in the idp/who.ini configuration file, the file used for authentication
is idp/passwd and the ini file is idp/idp_user.ini.
To run the setup do:
The passwords in passwd in clear text:
./all.sh start
roland:friend
ozzie:two
derek:three
ryan:four
ischiro:five
and then use your favourite webbrowser to look at "http://localhost:8087/"
To shut it down do:
./all.sh stop
The IdP authenticates users using a dictionary built in to idp2/idp.py;
look for the dictionary called PASSWD inside that file.
Other metadata about the accounts (names, email addresses, etc) are
stored in idp2/idp_user.py. (Note, not all accounts have all such data
defined.)
The username:password pairs in PASSWD:
haho0032:qwerty
roland:dianakra
babs:howes
upper:crust
The SP doesn't do anything but show you the information that the IdP sent.
Note, the listeners are all configured to bind to localhost (127.0.0.1) only.
If you want to be able to connect to them externally, grep "HOST = '127.0.0.1'"
example/*/*.py and replace 127.0.0.1 with 0.0.0.0 or a specific IP.
To make it easy, for me :-), both the IdP and the SP uses the same keys.
To generate new keys, run create_key.sh and follow its instructions.
To run the setup do
There are alternate IdP and SP configs in idp2_repoze/ and sp-repoze/ that
are still in flux; do not use them unless you know what you are doing.
./all.sh start
and then use your favourit webbrowser to look at "http://localhost:8087/whoami"
./all stop
will of course stop your IdP and SP.

View File

@@ -2,19 +2,19 @@
startme() {
cd sp-wsgi
if [ ! -f conf.py ] ; then
cp conf.py.example conf.py
if [ ! -f sp_conf.py ] ; then
cp sp_conf.py.example sp_conf.py
fi
../../tools/make_metadata.py conf > sp.xml
../../tools/make_metadata.py sp_conf > sp.xml
cd ../idp2
if [ ! -f idp_conf.py ] ; then
cp idp_conf.py.example conf.py
cp idp_conf.py.example idp_conf.py
fi
../../tools/make_metadata.py idp_conf > idp.xml
cd ../sp-wsgi
./sp.py conf &
./sp.py sp_conf &
cd ../idp2
./idp.py idp_conf &
@@ -34,4 +34,4 @@ case "$1" in
*) echo "usage: $0 start|stop|restart" >&2
exit 1
;;
esac
esac

View File

@@ -1,5 +1,25 @@
openssl genrsa -des3 -out server.key 1024
#!/bin/bash
cat <<EOF
Generating a new test key and certificate. To change the defaults offered
by openssl, edit your openssl.cnf, such as /etc/ssl/openssl.cnf
EOF
openssl genrsa -out server.key 1024
chmod 600 server.key
openssl req -new -key server.key -out server.csr
cp server.key server.key.org
openssl rsa -in server.key.org -out server.key
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
cat <<EOH
Now to enable these new keys, do:
cp server.key idp2/pki/mykey.pem
cp server.crt idp2/pki/mycert.pem
cp server.key sp-wsgi/pki/mykey.pem
cp server.crt sp-wsgi/pki/mycert.pem
EOH

View File

@@ -871,7 +871,7 @@ def application(environ, start_response):
captures in the WSGI environment as `myapp.url_args` so that
the functions from above can access the url placeholders.
If nothing matches call the `not_found` function.
If nothing matches, call the `not_found` function.
:param environ: The HTTP application environment
:param start_response: The application to run when the handling of the
@@ -976,10 +976,11 @@ if __name__ == '__main__':
module_directory=_rot + 'modules',
input_encoding='utf-8', output_encoding='utf-8')
HOST = '127.0.0.1'
PORT = 8088
SRV = make_server('', PORT, application)
print "IdP listening on port: %s" % PORT
SRV = make_server(HOST, PORT, application)
print "IdP listening on %s:%s" % (HOST, PORT)
SRV.serve_forever()
else:
_rot = args.mako_root

View File

@@ -116,7 +116,7 @@ CONFIG = {
"email_address": "support@example.com"
},
],
# This database holds the map between a subjects local identifier and
# This database holds the map between a subject's local identifier and
# the identifier returned to a SP
"xmlsec_binary": xmlsec_path,
#"attribute_map_dir": "../attributemaps",
@@ -141,4 +141,4 @@ PWD_VERIFY = "%s/verify_pwd" % BASE
AUTHORIZATION = {
"CAS" : {"ACR": "CAS", "WEIGHT": 1, "URL": CAS_VERIFY},
"UserPassword" : {"ACR": "PASSWORD", "WEIGHT": 2, "URL": PWD_VERIFY}
}
}

View File

@@ -869,10 +869,10 @@ def application(environ, start_response):
"""
The main WSGI application. Dispatch the current request to
the functions from above and store the regular expression
captures in the WSGI environment as `myapp.url_args` so that
captures in the WSGI environment as `myapp.url_args` so that
the functions from above can access the url placeholders.
If nothing matches call the `not_found` function.
If nothing matches, call the `not_found` function.
:param environ: The HTTP application environment
:param start_response: The application to run when the handling of the
@@ -977,10 +977,11 @@ if __name__ == '__main__':
module_directory=_rot + 'modules',
input_encoding='utf-8', output_encoding='utf-8')
HOST = '127.0.0.1'
PORT = 8088
SRV = make_server('', PORT, application)
print "IdP listening on port: %s" % PORT
SRV = make_server(HOST, PORT, application)
print "IdP listening on %s:%s" % (HOST, PORT)
SRV.serve_forever()
else:
_rot = args.mako_root

View File

@@ -116,7 +116,7 @@ CONFIG = {
"email_address": "support@example.com"
},
],
# This database holds the map between a subjects local identifier and
# This database holds the map between a subject's local identifier and
# the identifier returned to a SP
"xmlsec_binary": xmlsec_path,
#"attribute_map_dir": "../attributemaps",
@@ -141,4 +141,4 @@ PWD_VERIFY = "%s/verify_pwd" % BASE
AUTHORIZATION = {
"CAS" : {"ACR": "CAS", "WEIGHT": 1, "URL": CAS_VERIFY},
"UserPassword" : {"ACR": "PASSWORD", "WEIGHT": 2, "URL": PWD_VERIFY}
}
}

View File

@@ -96,7 +96,7 @@ def whoami(environ, start_response, user):
if not nameid:
return not_authn(environ, start_response)
if ava:
response = ["<h2>Your identity are supposed to be</h2>"]
response = ["<h2>Your identity is supposed to be</h2>"]
response.extend(dict_to_table(ava))
else:
response = [
@@ -222,10 +222,10 @@ def application(environ, start_response):
"""
The main WSGI application. Dispatch the current request to
the functions from above and store the regular expression
captures in the WSGI environment as `myapp.url_args` so that
captures in the WSGI environment as `myapp.url_args` so that
the functions from above can access the url placeholders.
If nothing matches call the `not_found` function.
If nothing matches, call the `not_found` function.
:param environ: The HTTP application environment
:param start_response: The application to run when the handling of the
@@ -268,6 +268,7 @@ app_with_auth = make_middleware_with_config(application, {"here": "."},
log_file="repoze_who.log")
# ----------------------------------------------------------------------------
HOST = '127.0.0.1'
PORT = 8087
# allow uwsgi or gunicorn mount
@@ -291,6 +292,6 @@ if __name__ == '__main__':
args = parser.parse_args()
from wsgiref.simple_server import make_server
srv = make_server('', PORT, app_with_auth)
print "SP listening on port: %s" % PORT
srv.serve_forever()
srv = make_server(HOST, PORT, app_with_auth)
print "SP listening on %s:%s" % (HOST, PORT)
srv.serve_forever()

View File

@@ -1,5 +1,6 @@
from saml2.assertion import Policy
HOST = '127.0.0.1'
PORT = 8087
HTTPS = False
@@ -11,6 +12,6 @@ POLICY = Policy(
)
# HTTPS cert information
SERVER_CERT = "pki/ssl.crt"
SERVER_KEY = "pki/ssl.pem"
CERT_CHAIN = ""
SERVER_CERT = "pki/mycert.pem"
SERVER_KEY = "pki/mykey.pem"
CERT_CHAIN = ""

View File

@@ -93,7 +93,7 @@ def dict_to_table(ava, lev=0, width=1):
def handle_static(environ, start_response, path):
"""
Creates a response for a static file. There might be a longer path
then just /static/... if so strip the path leading up to static.
then just /static/... - if so strip the path leading up to static.
:param environ: wsgi enviroment
:param start_response: wsgi start response
@@ -534,7 +534,7 @@ class SSO(object):
self.cache.relay_state[_rstate] = came_from
ht_args = _cli.apply_binding(_binding, "%s" % req, destination,
relay_state=_rstate)
_sid = req.id
_sid = req_id
logger.debug("ht_args: %s" % ht_args)
except Exception, exc:
logger.exception(exc)
@@ -645,7 +645,7 @@ def application(environ, start_response):
The main WSGI application. Dispatch the current request to
the functions from above.
If nothing matches call the `not_found` function.
If nothing matches, call the `not_found` function.
:param environ: The HTTP application environment
:param start_response: The application to run when the handling of the
@@ -683,13 +683,14 @@ def application(environ, start_response):
# ----------------------------------------------------------------------------
HOST = service_conf.HOST
PORT = service_conf.PORT
# ------- HTTPS -------
# These should point to relevant files
SERVER_CERT = service_conf.SERVER_CERT
SERVER_KEY = service_conf.SERVER_KEY
# This is of course the certificate chain for the CA that signed
# you cert and all the way up to the top
# your cert and all the way up to the top
CERT_CHAIN = service_conf.CERT_CHAIN
if __name__ == '__main__':
@@ -727,13 +728,13 @@ if __name__ == '__main__':
add_urls()
SRV = wsgiserver.CherryPyWSGIServer(('0.0.0.0', PORT), application)
SRV = wsgiserver.CherryPyWSGIServer((HOST, PORT), application)
if service_conf.HTTPS:
SRV.ssl_adapter = ssl_pyopenssl.pyOpenSSLAdapter(SERVER_CERT,
SERVER_KEY, CERT_CHAIN)
logger.info("Server starting")
print "SP listening on port: %s" % PORT
print "SP listening on %s:%s" % (HOST, PORT)
try:
SRV.start()
except KeyboardInterrupt:

View File

@@ -46,7 +46,8 @@ install_requires = [
'pycrypto', # 'Crypto'
'pytz',
'pyOpenSSL',
'python-dateutil'
'python-dateutil',
'argparse'
]
tests_require = [

View File

@@ -78,7 +78,8 @@ def _match(attr, ava):
return None
def filter_on_attributes(ava, required=None, optional=None, acs=None):
def filter_on_attributes(ava, required=None, optional=None, acs=None,
fail_on_unfulfilled_requirements=True):
""" Filter
:param ava: An attribute value assertion as a dictionary
@@ -86,6 +87,8 @@ def filter_on_attributes(ava, required=None, optional=None, acs=None):
required
:param optional: list of RequestedAttribute instances defined to be
optional
:param fail_on_unfulfilled_requirements: If required attributes
are missing fail or fail not depending on this parameter.
:return: The modified attribute value assertion
"""
res = {}
@@ -116,7 +119,7 @@ def filter_on_attributes(ava, required=None, optional=None, acs=None):
values = []
res[_fn] = _filter_values(ava[_fn], values, True)
continue
else:
elif fail_on_unfulfilled_requirements:
desc = "Required attribute missing: '%s' (%s)" % (attr["name"],
_name)
raise MissingValue(desc)
@@ -434,6 +437,16 @@ class Policy(object):
return self.get("attribute_restrictions", sp_entity_id)
def get_fail_on_missing_requested(self, sp_entity_id):
""" Return the whether the IdP should should fail if the SPs
requested attributes could not be found.
:param sp_entity_id: The SP entity ID
:return: The restrictions
"""
return self.get("fail_on_missing_requested", sp_entity_id, True)
def entity_category_attributes(self, ec):
if not self._restrictions:
return None
@@ -492,7 +505,9 @@ class Policy(object):
if required or optional:
logger.debug("required: %s, optional: %s" % (required, optional))
ava = filter_on_attributes(ava, required, optional, self.acs)
ava = filter_on_attributes(
ava, required, optional, self.acs,
self.get_fail_on_missing_requested(sp_entity_id))
return ava
@@ -543,6 +558,103 @@ class EntityCategories(object):
pass
def _authn_context_class_ref(authn_class, authn_auth=None):
"""
Construct the authn context with a authn context class reference
:param authn_class: The authn context class reference
:param authn_auth: Authenticating Authority
:return: An AuthnContext instance
"""
cntx_class = factory(saml.AuthnContextClassRef, text=authn_class)
if authn_auth:
return factory(saml.AuthnContext,
authn_context_class_ref=cntx_class,
authenticating_authority=factory(
saml.AuthenticatingAuthority, text=authn_auth))
else:
return factory(saml.AuthnContext,
authn_context_class_ref=cntx_class)
def _authn_context_decl(decl, authn_auth=None):
"""
Construct the authn context with a authn context declaration
:param decl: The authn context declaration
:param authn_auth: Authenticating Authority
:return: An AuthnContext instance
"""
return factory(saml.AuthnContext,
authn_context_decl=decl,
authenticating_authority=factory(
saml.AuthenticatingAuthority, text=authn_auth))
def _authn_context_decl_ref(decl_ref, authn_auth=None):
"""
Construct the authn context with a authn context declaration reference
:param decl_ref: The authn context declaration reference
:param authn_auth: Authenticating Authority
:return: An AuthnContext instance
"""
return factory(saml.AuthnContext,
authn_context_decl_ref=decl_ref,
authenticating_authority=factory(
saml.AuthenticatingAuthority, text=authn_auth))
def authn_statement(authn_class=None, authn_auth=None,
authn_decl=None, authn_decl_ref=None, authn_instant="",
subject_locality=""):
"""
Construct the AuthnStatement
:param authn_class: Authentication Context Class reference
:param authn_auth: Authenticating Authority
:param authn_decl: Authentication Context Declaration
:param authn_decl_ref: Authentication Context Declaration reference
:param authn_instant: When the Authentication was performed.
Assumed to be seconds since the Epoch.
:param subject_locality: Specifies the DNS domain name and IP address
for the system from which the assertion subject was apparently
authenticated.
:return: An AuthnContext instance
"""
if authn_instant:
_instant = instant(time_stamp=authn_instant)
else:
_instant = instant()
if authn_class:
res = factory(
saml.AuthnStatement,
authn_instant=_instant,
session_index=sid(),
authn_context=_authn_context_class_ref(
authn_class, authn_auth))
elif authn_decl:
res = factory(
saml.AuthnStatement,
authn_instant=_instant,
session_index=sid(),
authn_context=_authn_context_decl(authn_decl, authn_auth))
elif authn_decl_ref:
res = factory(
saml.AuthnStatement,
authn_instant=_instant,
session_index=sid(),
authn_context=_authn_context_decl_ref(authn_decl_ref,
authn_auth))
else:
res = factory(
saml.AuthnStatement,
authn_instant=_instant,
session_index=sid())
if subject_locality:
res.subject_locality = saml.SubjectLocality(text=subject_locality)
return res
class Assertion(dict):
""" Handles assertions about subjects """
@@ -550,101 +662,6 @@ class Assertion(dict):
dict.__init__(self, dic)
self.acs = []
@staticmethod
def _authn_context_decl(decl, authn_auth=None):
"""
Construct the authn context with a authn context declaration
:param decl: The authn context declaration
:param authn_auth: Authenticating Authority
:return: An AuthnContext instance
"""
return factory(saml.AuthnContext,
authn_context_decl=decl,
authenticating_authority=factory(
saml.AuthenticatingAuthority, text=authn_auth))
def _authn_context_decl_ref(self, decl_ref, authn_auth=None):
"""
Construct the authn context with a authn context declaration reference
:param decl_ref: The authn context declaration reference
:param authn_auth: Authenticating Authority
:return: An AuthnContext instance
"""
return factory(saml.AuthnContext,
authn_context_decl_ref=decl_ref,
authenticating_authority=factory(
saml.AuthenticatingAuthority, text=authn_auth))
@staticmethod
def _authn_context_class_ref(authn_class, authn_auth=None):
"""
Construct the authn context with a authn context class reference
:param authn_class: The authn context class reference
:param authn_auth: Authenticating Authority
:return: An AuthnContext instance
"""
cntx_class = factory(saml.AuthnContextClassRef, text=authn_class)
if authn_auth:
return factory(saml.AuthnContext,
authn_context_class_ref=cntx_class,
authenticating_authority=factory(
saml.AuthenticatingAuthority, text=authn_auth))
else:
return factory(saml.AuthnContext,
authn_context_class_ref=cntx_class)
def _authn_statement(self, authn_class=None, authn_auth=None,
authn_decl=None, authn_decl_ref=None, authn_instant="",
subject_locality=""):
"""
Construct the AuthnStatement
:param authn_class: Authentication Context Class reference
:param authn_auth: Authenticating Authority
:param authn_decl: Authentication Context Declaration
:param authn_decl_ref: Authentication Context Declaration reference
:param authn_instant: When the Authentication was performed.
Assumed to be seconds since the Epoch.
:param subject_locality: Specifies the DNS domain name and IP address
for the system from which the assertion subject was apparently
authenticated.
:return: An AuthnContext instance
"""
if authn_instant:
_instant = instant(time_stamp=authn_instant)
else:
_instant = instant()
if authn_class:
res = factory(
saml.AuthnStatement,
authn_instant=_instant,
session_index=sid(),
authn_context=self._authn_context_class_ref(
authn_class, authn_auth))
elif authn_decl:
res = factory(
saml.AuthnStatement,
authn_instant=_instant,
session_index=sid(),
authn_context=self._authn_context_decl(authn_decl, authn_auth))
elif authn_decl_ref:
res = factory(
saml.AuthnStatement,
authn_instant=_instant,
session_index=sid(),
authn_context=self._authn_context_decl_ref(authn_decl_ref,
authn_auth))
else:
res = factory(
saml.AuthnStatement,
authn_instant=_instant,
session_index=sid())
if subject_locality:
res.subject_locality = saml.SubjectLocality(text=subject_locality)
return res
def construct(self, sp_entity_id, in_response_to, consumer_url,
name_id, attrconvs, policy, issuer, authn_class=None,
authn_auth=None, authn_decl=None, encrypt=None,
@@ -695,10 +712,10 @@ class Assertion(dict):
conds = policy.conditions(sp_entity_id)
if authn_auth or authn_class or authn_decl or authn_decl_ref:
_authn_statement = self._authn_statement(authn_class, authn_auth,
authn_decl, authn_decl_ref,
authn_instant,
subject_locality)
_authn_statement = authn_statement(authn_class, authn_auth,
authn_decl, authn_decl_ref,
authn_instant,
subject_locality)
else:
_authn_statement = None

View File

@@ -1,20 +1,18 @@
# See http://technet.microsoft.com/en-us/library/cc733065(v=ws.10).aspx
# and http://technet.microsoft.com/en-us/library/ee913589(v=ws.10).aspx
# for information regarding the default claim types supported by
# Microsoft ADFS v1.x.
CLAIMS = 'http://schemas.xmlsoap.org/claims/'
MAP = {
"identifier": "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified",
"fro": {
'http://schemas.xmlsoap.org/claims/commonname': 'commonName',
'http://schemas.xmlsoap.org/claims/emailaddress': 'emailAddress',
'http://schemas.xmlsoap.org/claims/group': 'group',
'http://schemas.xmlsoap.org/claims/upn': 'upn',
},
"to": {
'commonName': 'http://schemas.xmlsoap.org/claims/commonname',
'emailAddress': 'http://schemas.xmlsoap.org/claims/emailaddress',
'group': 'http://schemas.xmlsoap.org/claims/group',
'upn': 'http://schemas.xmlsoap.org/claims/upn',
'fro': {
CLAIMS+'commonname': 'commonName',
CLAIMS+'emailaddress': 'emailAddress',
CLAIMS+'group': 'group',
CLAIMS+'upn': 'upn',
},
'to': {
'commonName': CLAIMS+'commonname',
'emailAddress': CLAIMS+'emailaddress',
'group': CLAIMS+'group',
'upn': CLAIMS+'upn',
}
}

View File

@@ -1,47 +1,49 @@
# See http://technet.microsoft.com/en-us/library/ee913589(v=ws.10).aspx
# for information regarding the default claim types supported by
# Microsoft ADFS v2.0.
CLAIMS = 'http://schemas.xmlsoap.org/claims/'
COM_WS_CLAIMS = 'http://schemas.xmlsoap.com/ws/2005/05/identity/claims/'
MS_CLAIMS = 'http://schemas.microsoft.com/ws/2008/06/identity/claims/'
ORG_WS_CLAIMS = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/'
MAP = {
"identifier": "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified",
"fro": {
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress': 'emailAddress',
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname': 'givenName',
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name': 'name',
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn': 'upn',
'http://schemas.xmlsoap.org/claims/commonname': 'commonName',
'http://schemas.xmlsoap.org/claims/group': 'group',
'http://schemas.microsoft.com/ws/2008/06/identity/claims/role': 'role',
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname': 'surname',
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier': 'privatePersonalId',
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier': 'nameId',
'http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod': 'authenticationMethod',
'http://schemas.xmlsoap.com/ws/2005/05/identity/claims/denyonlysid': 'denyOnlySid',
'http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarysid': 'denyOnlyPrimarySid',
'http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarygroupsid': 'denyOnlyPrimaryGroupSid',
'http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid': 'groupSid',
'http://schemas.microsoft.com/ws/2008/06/identity/claims/primarygroupsid': 'primaryGroupSid',
'http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid': 'primarySid',
'http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname': 'windowsAccountName',
},
"to": {
'emailAddress': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress',
'givenName': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname',
'name': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name',
'upn': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn',
'commonName': 'http://schemas.xmlsoap.org/claims/commonname',
'group': 'http://schemas.xmlsoap.org/claims/group',
'role': 'http://schemas.microsoft.com/ws/2008/06/identity/claims/role',
'surname': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname',
'privatePersonalId': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier',
'nameId': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier',
'authenticationMethod': 'http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod',
'denyOnlySid': 'http://schemas.xmlsoap.com/ws/2005/05/identity/claims/denyonlysid',
'denyOnlyPrimarySid': 'http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarysid',
'denyOnlyPrimaryGroupSid': 'http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarygroupsid',
'groupSid': 'http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid',
'primaryGroupSid': 'http://schemas.microsoft.com/ws/2008/06/identity/claims/primarygroupsid',
'primarySid': 'http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid',
'windowsAccountName': 'http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname',
'fro': {
CLAIMS+'commonname': 'commonName',
CLAIMS+'group': 'group',
COM_WS_CLAIMS+'denyonlysid': 'denyOnlySid',
MS_CLAIMS+'authenticationmethod': 'authenticationMethod',
MS_CLAIMS+'denyonlyprimarygroupsid': 'denyOnlyPrimaryGroupSid',
MS_CLAIMS+'denyonlyprimarysid': 'denyOnlyPrimarySid',
MS_CLAIMS+'groupsid': 'groupSid',
MS_CLAIMS+'primarygroupsid': 'primaryGroupSid',
MS_CLAIMS+'primarysid': 'primarySid',
MS_CLAIMS+'role': 'role',
MS_CLAIMS+'windowsaccountname': 'windowsAccountName',
ORG_WS_CLAIMS+'emailaddress': 'emailAddress',
ORG_WS_CLAIMS+'givenname': 'givenName',
ORG_WS_CLAIMS+'name': 'name',
ORG_WS_CLAIMS+'nameidentifier': 'nameId',
ORG_WS_CLAIMS+'privatepersonalidentifier': 'privatePersonalId',
ORG_WS_CLAIMS+'surname': 'surname',
ORG_WS_CLAIMS+'upn': 'upn',
},
'to': {
'authenticationMethod': MS_CLAIMS+'authenticationmethod',
'commonName': CLAIMS+'commonname',
'denyOnlyPrimaryGroupSid': MS_CLAIMS+'denyonlyprimarygroupsid',
'denyOnlyPrimarySid': MS_CLAIMS+'denyonlyprimarysid',
'denyOnlySid': COM_WS_CLAIMS+'denyonlysid',
'emailAddress': ORG_WS_CLAIMS+'emailaddress',
'givenName': ORG_WS_CLAIMS+'givenname',
'group': CLAIMS+'group',
'groupSid': MS_CLAIMS+'groupsid',
'name': ORG_WS_CLAIMS+'name',
'nameId': ORG_WS_CLAIMS+'nameidentifier',
'primaryGroupSid': MS_CLAIMS+'primarygroupsid',
'primarySid': MS_CLAIMS+'primarysid',
'privatePersonalId': ORG_WS_CLAIMS+'privatepersonalidentifier',
'role': MS_CLAIMS+'role',
'surname': ORG_WS_CLAIMS+'surname',
'upn': ORG_WS_CLAIMS+'upn',
'windowsAccountName': MS_CLAIMS+'windowsaccountname',
}
}

View File

@@ -1,326 +1,328 @@
DEF = 'urn:mace:dir:attribute-def:'
MAP = {
"identifier": "urn:oasis:names:tc:SAML:2.0:attrname-format:basic",
"fro": {
'urn:mace:dir:attribute-def:aRecord': 'aRecord',
'urn:mace:dir:attribute-def:aliasedEntryName': 'aliasedEntryName',
'urn:mace:dir:attribute-def:aliasedObjectName': 'aliasedObjectName',
'urn:mace:dir:attribute-def:associatedDomain': 'associatedDomain',
'urn:mace:dir:attribute-def:associatedName': 'associatedName',
'urn:mace:dir:attribute-def:audio': 'audio',
'urn:mace:dir:attribute-def:authorityRevocationList': 'authorityRevocationList',
'urn:mace:dir:attribute-def:buildingName': 'buildingName',
'urn:mace:dir:attribute-def:businessCategory': 'businessCategory',
'urn:mace:dir:attribute-def:c': 'c',
'urn:mace:dir:attribute-def:cACertificate': 'cACertificate',
'urn:mace:dir:attribute-def:cNAMERecord': 'cNAMERecord',
'urn:mace:dir:attribute-def:carLicense': 'carLicense',
'urn:mace:dir:attribute-def:certificateRevocationList': 'certificateRevocationList',
'urn:mace:dir:attribute-def:cn': 'cn',
'urn:mace:dir:attribute-def:co': 'co',
'urn:mace:dir:attribute-def:commonName': 'commonName',
'urn:mace:dir:attribute-def:countryName': 'countryName',
'urn:mace:dir:attribute-def:crossCertificatePair': 'crossCertificatePair',
'urn:mace:dir:attribute-def:dITRedirect': 'dITRedirect',
'urn:mace:dir:attribute-def:dSAQuality': 'dSAQuality',
'urn:mace:dir:attribute-def:dc': 'dc',
'urn:mace:dir:attribute-def:deltaRevocationList': 'deltaRevocationList',
'urn:mace:dir:attribute-def:departmentNumber': 'departmentNumber',
'urn:mace:dir:attribute-def:description': 'description',
'urn:mace:dir:attribute-def:destinationIndicator': 'destinationIndicator',
'urn:mace:dir:attribute-def:displayName': 'displayName',
'urn:mace:dir:attribute-def:distinguishedName': 'distinguishedName',
'urn:mace:dir:attribute-def:dmdName': 'dmdName',
'urn:mace:dir:attribute-def:dnQualifier': 'dnQualifier',
'urn:mace:dir:attribute-def:documentAuthor': 'documentAuthor',
'urn:mace:dir:attribute-def:documentIdentifier': 'documentIdentifier',
'urn:mace:dir:attribute-def:documentLocation': 'documentLocation',
'urn:mace:dir:attribute-def:documentPublisher': 'documentPublisher',
'urn:mace:dir:attribute-def:documentTitle': 'documentTitle',
'urn:mace:dir:attribute-def:documentVersion': 'documentVersion',
'urn:mace:dir:attribute-def:domainComponent': 'domainComponent',
'urn:mace:dir:attribute-def:drink': 'drink',
'urn:mace:dir:attribute-def:eduOrgHomePageURI': 'eduOrgHomePageURI',
'urn:mace:dir:attribute-def:eduOrgIdentityAuthNPolicyURI': 'eduOrgIdentityAuthNPolicyURI',
'urn:mace:dir:attribute-def:eduOrgLegalName': 'eduOrgLegalName',
'urn:mace:dir:attribute-def:eduOrgSuperiorURI': 'eduOrgSuperiorURI',
'urn:mace:dir:attribute-def:eduOrgWhitePagesURI': 'eduOrgWhitePagesURI',
'urn:mace:dir:attribute-def:eduPersonAffiliation': 'eduPersonAffiliation',
'urn:mace:dir:attribute-def:eduPersonEntitlement': 'eduPersonEntitlement',
'urn:mace:dir:attribute-def:eduPersonNickname': 'eduPersonNickname',
'urn:mace:dir:attribute-def:eduPersonOrgDN': 'eduPersonOrgDN',
'urn:mace:dir:attribute-def:eduPersonOrgUnitDN': 'eduPersonOrgUnitDN',
'urn:mace:dir:attribute-def:eduPersonPrimaryAffiliation': 'eduPersonPrimaryAffiliation',
'urn:mace:dir:attribute-def:eduPersonPrimaryOrgUnitDN': 'eduPersonPrimaryOrgUnitDN',
'urn:mace:dir:attribute-def:eduPersonPrincipalName': 'eduPersonPrincipalName',
'urn:mace:dir:attribute-def:eduPersonScopedAffiliation': 'eduPersonScopedAffiliation',
'urn:mace:dir:attribute-def:eduPersonTargetedID': 'eduPersonTargetedID',
'urn:mace:dir:attribute-def:email': 'email',
'urn:mace:dir:attribute-def:emailAddress': 'emailAddress',
'urn:mace:dir:attribute-def:employeeNumber': 'employeeNumber',
'urn:mace:dir:attribute-def:employeeType': 'employeeType',
'urn:mace:dir:attribute-def:enhancedSearchGuide': 'enhancedSearchGuide',
'urn:mace:dir:attribute-def:facsimileTelephoneNumber': 'facsimileTelephoneNumber',
'urn:mace:dir:attribute-def:favouriteDrink': 'favouriteDrink',
'urn:mace:dir:attribute-def:fax': 'fax',
'urn:mace:dir:attribute-def:federationFeideSchemaVersion': 'federationFeideSchemaVersion',
'urn:mace:dir:attribute-def:friendlyCountryName': 'friendlyCountryName',
'urn:mace:dir:attribute-def:generationQualifier': 'generationQualifier',
'urn:mace:dir:attribute-def:givenName': 'givenName',
'urn:mace:dir:attribute-def:gn': 'gn',
'urn:mace:dir:attribute-def:homePhone': 'homePhone',
'urn:mace:dir:attribute-def:homePostalAddress': 'homePostalAddress',
'urn:mace:dir:attribute-def:homeTelephoneNumber': 'homeTelephoneNumber',
'urn:mace:dir:attribute-def:host': 'host',
'urn:mace:dir:attribute-def:houseIdentifier': 'houseIdentifier',
'urn:mace:dir:attribute-def:info': 'info',
'urn:mace:dir:attribute-def:initials': 'initials',
'urn:mace:dir:attribute-def:internationaliSDNNumber': 'internationaliSDNNumber',
'urn:mace:dir:attribute-def:janetMailbox': 'janetMailbox',
'urn:mace:dir:attribute-def:jpegPhoto': 'jpegPhoto',
'urn:mace:dir:attribute-def:knowledgeInformation': 'knowledgeInformation',
'urn:mace:dir:attribute-def:l': 'l',
'urn:mace:dir:attribute-def:labeledURI': 'labeledURI',
'urn:mace:dir:attribute-def:localityName': 'localityName',
'urn:mace:dir:attribute-def:mDRecord': 'mDRecord',
'urn:mace:dir:attribute-def:mXRecord': 'mXRecord',
'urn:mace:dir:attribute-def:mail': 'mail',
'urn:mace:dir:attribute-def:mailPreferenceOption': 'mailPreferenceOption',
'urn:mace:dir:attribute-def:manager': 'manager',
'urn:mace:dir:attribute-def:member': 'member',
'urn:mace:dir:attribute-def:mobile': 'mobile',
'urn:mace:dir:attribute-def:mobileTelephoneNumber': 'mobileTelephoneNumber',
'urn:mace:dir:attribute-def:nSRecord': 'nSRecord',
'urn:mace:dir:attribute-def:name': 'name',
'urn:mace:dir:attribute-def:norEduOrgAcronym': 'norEduOrgAcronym',
'urn:mace:dir:attribute-def:norEduOrgNIN': 'norEduOrgNIN',
'urn:mace:dir:attribute-def:norEduOrgSchemaVersion': 'norEduOrgSchemaVersion',
'urn:mace:dir:attribute-def:norEduOrgUniqueIdentifier': 'norEduOrgUniqueIdentifier',
'urn:mace:dir:attribute-def:norEduOrgUniqueNumber': 'norEduOrgUniqueNumber',
'urn:mace:dir:attribute-def:norEduOrgUnitUniqueIdentifier': 'norEduOrgUnitUniqueIdentifier',
'urn:mace:dir:attribute-def:norEduOrgUnitUniqueNumber': 'norEduOrgUnitUniqueNumber',
'urn:mace:dir:attribute-def:norEduPersonBirthDate': 'norEduPersonBirthDate',
'urn:mace:dir:attribute-def:norEduPersonLIN': 'norEduPersonLIN',
'urn:mace:dir:attribute-def:norEduPersonNIN': 'norEduPersonNIN',
'urn:mace:dir:attribute-def:o': 'o',
'urn:mace:dir:attribute-def:objectClass': 'objectClass',
'urn:mace:dir:attribute-def:organizationName': 'organizationName',
'urn:mace:dir:attribute-def:organizationalStatus': 'organizationalStatus',
'urn:mace:dir:attribute-def:organizationalUnitName': 'organizationalUnitName',
'urn:mace:dir:attribute-def:otherMailbox': 'otherMailbox',
'urn:mace:dir:attribute-def:ou': 'ou',
'urn:mace:dir:attribute-def:owner': 'owner',
'urn:mace:dir:attribute-def:pager': 'pager',
'urn:mace:dir:attribute-def:pagerTelephoneNumber': 'pagerTelephoneNumber',
'urn:mace:dir:attribute-def:personalSignature': 'personalSignature',
'urn:mace:dir:attribute-def:personalTitle': 'personalTitle',
'urn:mace:dir:attribute-def:photo': 'photo',
'urn:mace:dir:attribute-def:physicalDeliveryOfficeName': 'physicalDeliveryOfficeName',
'urn:mace:dir:attribute-def:pkcs9email': 'pkcs9email',
'urn:mace:dir:attribute-def:postOfficeBox': 'postOfficeBox',
'urn:mace:dir:attribute-def:postalAddress': 'postalAddress',
'urn:mace:dir:attribute-def:postalCode': 'postalCode',
'urn:mace:dir:attribute-def:preferredDeliveryMethod': 'preferredDeliveryMethod',
'urn:mace:dir:attribute-def:preferredLanguage': 'preferredLanguage',
'urn:mace:dir:attribute-def:presentationAddress': 'presentationAddress',
'urn:mace:dir:attribute-def:protocolInformation': 'protocolInformation',
'urn:mace:dir:attribute-def:pseudonym': 'pseudonym',
'urn:mace:dir:attribute-def:registeredAddress': 'registeredAddress',
'urn:mace:dir:attribute-def:rfc822Mailbox': 'rfc822Mailbox',
'urn:mace:dir:attribute-def:roleOccupant': 'roleOccupant',
'urn:mace:dir:attribute-def:roomNumber': 'roomNumber',
'urn:mace:dir:attribute-def:sOARecord': 'sOARecord',
'urn:mace:dir:attribute-def:searchGuide': 'searchGuide',
'urn:mace:dir:attribute-def:secretary': 'secretary',
'urn:mace:dir:attribute-def:seeAlso': 'seeAlso',
'urn:mace:dir:attribute-def:serialNumber': 'serialNumber',
'urn:mace:dir:attribute-def:singleLevelQuality': 'singleLevelQuality',
'urn:mace:dir:attribute-def:sn': 'sn',
'urn:mace:dir:attribute-def:st': 'st',
'urn:mace:dir:attribute-def:stateOrProvinceName': 'stateOrProvinceName',
'urn:mace:dir:attribute-def:street': 'street',
'urn:mace:dir:attribute-def:streetAddress': 'streetAddress',
'urn:mace:dir:attribute-def:subtreeMaximumQuality': 'subtreeMaximumQuality',
'urn:mace:dir:attribute-def:subtreeMinimumQuality': 'subtreeMinimumQuality',
'urn:mace:dir:attribute-def:supportedAlgorithms': 'supportedAlgorithms',
'urn:mace:dir:attribute-def:supportedApplicationContext': 'supportedApplicationContext',
'urn:mace:dir:attribute-def:surname': 'surname',
'urn:mace:dir:attribute-def:telephoneNumber': 'telephoneNumber',
'urn:mace:dir:attribute-def:teletexTerminalIdentifier': 'teletexTerminalIdentifier',
'urn:mace:dir:attribute-def:telexNumber': 'telexNumber',
'urn:mace:dir:attribute-def:textEncodedORAddress': 'textEncodedORAddress',
'urn:mace:dir:attribute-def:title': 'title',
'urn:mace:dir:attribute-def:uid': 'uid',
'urn:mace:dir:attribute-def:uniqueIdentifier': 'uniqueIdentifier',
'urn:mace:dir:attribute-def:uniqueMember': 'uniqueMember',
'urn:mace:dir:attribute-def:userCertificate': 'userCertificate',
'urn:mace:dir:attribute-def:userClass': 'userClass',
'urn:mace:dir:attribute-def:userPKCS12': 'userPKCS12',
'urn:mace:dir:attribute-def:userPassword': 'userPassword',
'urn:mace:dir:attribute-def:userSMIMECertificate': 'userSMIMECertificate',
'urn:mace:dir:attribute-def:userid': 'userid',
'urn:mace:dir:attribute-def:x121Address': 'x121Address',
'urn:mace:dir:attribute-def:x500UniqueIdentifier': 'x500UniqueIdentifier',
},
"to": {
'aRecord': 'urn:mace:dir:attribute-def:aRecord',
'aliasedEntryName': 'urn:mace:dir:attribute-def:aliasedEntryName',
'aliasedObjectName': 'urn:mace:dir:attribute-def:aliasedObjectName',
'associatedDomain': 'urn:mace:dir:attribute-def:associatedDomain',
'associatedName': 'urn:mace:dir:attribute-def:associatedName',
'audio': 'urn:mace:dir:attribute-def:audio',
'authorityRevocationList': 'urn:mace:dir:attribute-def:authorityRevocationList',
'buildingName': 'urn:mace:dir:attribute-def:buildingName',
'businessCategory': 'urn:mace:dir:attribute-def:businessCategory',
'c': 'urn:mace:dir:attribute-def:c',
'cACertificate': 'urn:mace:dir:attribute-def:cACertificate',
'cNAMERecord': 'urn:mace:dir:attribute-def:cNAMERecord',
'carLicense': 'urn:mace:dir:attribute-def:carLicense',
'certificateRevocationList': 'urn:mace:dir:attribute-def:certificateRevocationList',
'cn': 'urn:mace:dir:attribute-def:cn',
'co': 'urn:mace:dir:attribute-def:co',
'commonName': 'urn:mace:dir:attribute-def:commonName',
'countryName': 'urn:mace:dir:attribute-def:countryName',
'crossCertificatePair': 'urn:mace:dir:attribute-def:crossCertificatePair',
'dITRedirect': 'urn:mace:dir:attribute-def:dITRedirect',
'dSAQuality': 'urn:mace:dir:attribute-def:dSAQuality',
'dc': 'urn:mace:dir:attribute-def:dc',
'deltaRevocationList': 'urn:mace:dir:attribute-def:deltaRevocationList',
'departmentNumber': 'urn:mace:dir:attribute-def:departmentNumber',
'description': 'urn:mace:dir:attribute-def:description',
'destinationIndicator': 'urn:mace:dir:attribute-def:destinationIndicator',
'displayName': 'urn:mace:dir:attribute-def:displayName',
'distinguishedName': 'urn:mace:dir:attribute-def:distinguishedName',
'dmdName': 'urn:mace:dir:attribute-def:dmdName',
'dnQualifier': 'urn:mace:dir:attribute-def:dnQualifier',
'documentAuthor': 'urn:mace:dir:attribute-def:documentAuthor',
'documentIdentifier': 'urn:mace:dir:attribute-def:documentIdentifier',
'documentLocation': 'urn:mace:dir:attribute-def:documentLocation',
'documentPublisher': 'urn:mace:dir:attribute-def:documentPublisher',
'documentTitle': 'urn:mace:dir:attribute-def:documentTitle',
'documentVersion': 'urn:mace:dir:attribute-def:documentVersion',
'domainComponent': 'urn:mace:dir:attribute-def:domainComponent',
'drink': 'urn:mace:dir:attribute-def:drink',
'eduOrgHomePageURI': 'urn:mace:dir:attribute-def:eduOrgHomePageURI',
'eduOrgIdentityAuthNPolicyURI': 'urn:mace:dir:attribute-def:eduOrgIdentityAuthNPolicyURI',
'eduOrgLegalName': 'urn:mace:dir:attribute-def:eduOrgLegalName',
'eduOrgSuperiorURI': 'urn:mace:dir:attribute-def:eduOrgSuperiorURI',
'eduOrgWhitePagesURI': 'urn:mace:dir:attribute-def:eduOrgWhitePagesURI',
'eduPersonAffiliation': 'urn:mace:dir:attribute-def:eduPersonAffiliation',
'eduPersonEntitlement': 'urn:mace:dir:attribute-def:eduPersonEntitlement',
'eduPersonNickname': 'urn:mace:dir:attribute-def:eduPersonNickname',
'eduPersonOrgDN': 'urn:mace:dir:attribute-def:eduPersonOrgDN',
'eduPersonOrgUnitDN': 'urn:mace:dir:attribute-def:eduPersonOrgUnitDN',
'eduPersonPrimaryAffiliation': 'urn:mace:dir:attribute-def:eduPersonPrimaryAffiliation',
'eduPersonPrimaryOrgUnitDN': 'urn:mace:dir:attribute-def:eduPersonPrimaryOrgUnitDN',
'eduPersonPrincipalName': 'urn:mace:dir:attribute-def:eduPersonPrincipalName',
'eduPersonScopedAffiliation': 'urn:mace:dir:attribute-def:eduPersonScopedAffiliation',
'eduPersonTargetedID': 'urn:mace:dir:attribute-def:eduPersonTargetedID',
'email': 'urn:mace:dir:attribute-def:email',
'emailAddress': 'urn:mace:dir:attribute-def:emailAddress',
'employeeNumber': 'urn:mace:dir:attribute-def:employeeNumber',
'employeeType': 'urn:mace:dir:attribute-def:employeeType',
'enhancedSearchGuide': 'urn:mace:dir:attribute-def:enhancedSearchGuide',
'facsimileTelephoneNumber': 'urn:mace:dir:attribute-def:facsimileTelephoneNumber',
'favouriteDrink': 'urn:mace:dir:attribute-def:favouriteDrink',
'fax': 'urn:mace:dir:attribute-def:fax',
'federationFeideSchemaVersion': 'urn:mace:dir:attribute-def:federationFeideSchemaVersion',
'friendlyCountryName': 'urn:mace:dir:attribute-def:friendlyCountryName',
'generationQualifier': 'urn:mace:dir:attribute-def:generationQualifier',
'givenName': 'urn:mace:dir:attribute-def:givenName',
'gn': 'urn:mace:dir:attribute-def:gn',
'homePhone': 'urn:mace:dir:attribute-def:homePhone',
'homePostalAddress': 'urn:mace:dir:attribute-def:homePostalAddress',
'homeTelephoneNumber': 'urn:mace:dir:attribute-def:homeTelephoneNumber',
'host': 'urn:mace:dir:attribute-def:host',
'houseIdentifier': 'urn:mace:dir:attribute-def:houseIdentifier',
'info': 'urn:mace:dir:attribute-def:info',
'initials': 'urn:mace:dir:attribute-def:initials',
'internationaliSDNNumber': 'urn:mace:dir:attribute-def:internationaliSDNNumber',
'janetMailbox': 'urn:mace:dir:attribute-def:janetMailbox',
'jpegPhoto': 'urn:mace:dir:attribute-def:jpegPhoto',
'knowledgeInformation': 'urn:mace:dir:attribute-def:knowledgeInformation',
'l': 'urn:mace:dir:attribute-def:l',
'labeledURI': 'urn:mace:dir:attribute-def:labeledURI',
'localityName': 'urn:mace:dir:attribute-def:localityName',
'mDRecord': 'urn:mace:dir:attribute-def:mDRecord',
'mXRecord': 'urn:mace:dir:attribute-def:mXRecord',
'mail': 'urn:mace:dir:attribute-def:mail',
'mailPreferenceOption': 'urn:mace:dir:attribute-def:mailPreferenceOption',
'manager': 'urn:mace:dir:attribute-def:manager',
'member': 'urn:mace:dir:attribute-def:member',
'mobile': 'urn:mace:dir:attribute-def:mobile',
'mobileTelephoneNumber': 'urn:mace:dir:attribute-def:mobileTelephoneNumber',
'nSRecord': 'urn:mace:dir:attribute-def:nSRecord',
'name': 'urn:mace:dir:attribute-def:name',
'norEduOrgAcronym': 'urn:mace:dir:attribute-def:norEduOrgAcronym',
'norEduOrgNIN': 'urn:mace:dir:attribute-def:norEduOrgNIN',
'norEduOrgSchemaVersion': 'urn:mace:dir:attribute-def:norEduOrgSchemaVersion',
'norEduOrgUniqueIdentifier': 'urn:mace:dir:attribute-def:norEduOrgUniqueIdentifier',
'norEduOrgUniqueNumber': 'urn:mace:dir:attribute-def:norEduOrgUniqueNumber',
'norEduOrgUnitUniqueIdentifier': 'urn:mace:dir:attribute-def:norEduOrgUnitUniqueIdentifier',
'norEduOrgUnitUniqueNumber': 'urn:mace:dir:attribute-def:norEduOrgUnitUniqueNumber',
'norEduPersonBirthDate': 'urn:mace:dir:attribute-def:norEduPersonBirthDate',
'norEduPersonLIN': 'urn:mace:dir:attribute-def:norEduPersonLIN',
'norEduPersonNIN': 'urn:mace:dir:attribute-def:norEduPersonNIN',
'o': 'urn:mace:dir:attribute-def:o',
'objectClass': 'urn:mace:dir:attribute-def:objectClass',
'organizationName': 'urn:mace:dir:attribute-def:organizationName',
'organizationalStatus': 'urn:mace:dir:attribute-def:organizationalStatus',
'organizationalUnitName': 'urn:mace:dir:attribute-def:organizationalUnitName',
'otherMailbox': 'urn:mace:dir:attribute-def:otherMailbox',
'ou': 'urn:mace:dir:attribute-def:ou',
'owner': 'urn:mace:dir:attribute-def:owner',
'pager': 'urn:mace:dir:attribute-def:pager',
'pagerTelephoneNumber': 'urn:mace:dir:attribute-def:pagerTelephoneNumber',
'personalSignature': 'urn:mace:dir:attribute-def:personalSignature',
'personalTitle': 'urn:mace:dir:attribute-def:personalTitle',
'photo': 'urn:mace:dir:attribute-def:photo',
'physicalDeliveryOfficeName': 'urn:mace:dir:attribute-def:physicalDeliveryOfficeName',
'pkcs9email': 'urn:mace:dir:attribute-def:pkcs9email',
'postOfficeBox': 'urn:mace:dir:attribute-def:postOfficeBox',
'postalAddress': 'urn:mace:dir:attribute-def:postalAddress',
'postalCode': 'urn:mace:dir:attribute-def:postalCode',
'preferredDeliveryMethod': 'urn:mace:dir:attribute-def:preferredDeliveryMethod',
'preferredLanguage': 'urn:mace:dir:attribute-def:preferredLanguage',
'presentationAddress': 'urn:mace:dir:attribute-def:presentationAddress',
'protocolInformation': 'urn:mace:dir:attribute-def:protocolInformation',
'pseudonym': 'urn:mace:dir:attribute-def:pseudonym',
'registeredAddress': 'urn:mace:dir:attribute-def:registeredAddress',
'rfc822Mailbox': 'urn:mace:dir:attribute-def:rfc822Mailbox',
'roleOccupant': 'urn:mace:dir:attribute-def:roleOccupant',
'roomNumber': 'urn:mace:dir:attribute-def:roomNumber',
'sOARecord': 'urn:mace:dir:attribute-def:sOARecord',
'searchGuide': 'urn:mace:dir:attribute-def:searchGuide',
'secretary': 'urn:mace:dir:attribute-def:secretary',
'seeAlso': 'urn:mace:dir:attribute-def:seeAlso',
'serialNumber': 'urn:mace:dir:attribute-def:serialNumber',
'singleLevelQuality': 'urn:mace:dir:attribute-def:singleLevelQuality',
'sn': 'urn:mace:dir:attribute-def:sn',
'st': 'urn:mace:dir:attribute-def:st',
'stateOrProvinceName': 'urn:mace:dir:attribute-def:stateOrProvinceName',
'street': 'urn:mace:dir:attribute-def:street',
'streetAddress': 'urn:mace:dir:attribute-def:streetAddress',
'subtreeMaximumQuality': 'urn:mace:dir:attribute-def:subtreeMaximumQuality',
'subtreeMinimumQuality': 'urn:mace:dir:attribute-def:subtreeMinimumQuality',
'supportedAlgorithms': 'urn:mace:dir:attribute-def:supportedAlgorithms',
'supportedApplicationContext': 'urn:mace:dir:attribute-def:supportedApplicationContext',
'surname': 'urn:mace:dir:attribute-def:surname',
'telephoneNumber': 'urn:mace:dir:attribute-def:telephoneNumber',
'teletexTerminalIdentifier': 'urn:mace:dir:attribute-def:teletexTerminalIdentifier',
'telexNumber': 'urn:mace:dir:attribute-def:telexNumber',
'textEncodedORAddress': 'urn:mace:dir:attribute-def:textEncodedORAddress',
'title': 'urn:mace:dir:attribute-def:title',
'uid': 'urn:mace:dir:attribute-def:uid',
'uniqueIdentifier': 'urn:mace:dir:attribute-def:uniqueIdentifier',
'uniqueMember': 'urn:mace:dir:attribute-def:uniqueMember',
'userCertificate': 'urn:mace:dir:attribute-def:userCertificate',
'userClass': 'urn:mace:dir:attribute-def:userClass',
'userPKCS12': 'urn:mace:dir:attribute-def:userPKCS12',
'userPassword': 'urn:mace:dir:attribute-def:userPassword',
'userSMIMECertificate': 'urn:mace:dir:attribute-def:userSMIMECertificate',
'userid': 'urn:mace:dir:attribute-def:userid',
'x121Address': 'urn:mace:dir:attribute-def:x121Address',
'x500UniqueIdentifier': 'urn:mace:dir:attribute-def:x500UniqueIdentifier',
'fro': {
DEF+'aRecord': 'aRecord',
DEF+'aliasedEntryName': 'aliasedEntryName',
DEF+'aliasedObjectName': 'aliasedObjectName',
DEF+'associatedDomain': 'associatedDomain',
DEF+'associatedName': 'associatedName',
DEF+'audio': 'audio',
DEF+'authorityRevocationList': 'authorityRevocationList',
DEF+'buildingName': 'buildingName',
DEF+'businessCategory': 'businessCategory',
DEF+'c': 'c',
DEF+'cACertificate': 'cACertificate',
DEF+'cNAMERecord': 'cNAMERecord',
DEF+'carLicense': 'carLicense',
DEF+'certificateRevocationList': 'certificateRevocationList',
DEF+'cn': 'cn',
DEF+'co': 'co',
DEF+'commonName': 'commonName',
DEF+'countryName': 'countryName',
DEF+'crossCertificatePair': 'crossCertificatePair',
DEF+'dITRedirect': 'dITRedirect',
DEF+'dSAQuality': 'dSAQuality',
DEF+'dc': 'dc',
DEF+'deltaRevocationList': 'deltaRevocationList',
DEF+'departmentNumber': 'departmentNumber',
DEF+'description': 'description',
DEF+'destinationIndicator': 'destinationIndicator',
DEF+'displayName': 'displayName',
DEF+'distinguishedName': 'distinguishedName',
DEF+'dmdName': 'dmdName',
DEF+'dnQualifier': 'dnQualifier',
DEF+'documentAuthor': 'documentAuthor',
DEF+'documentIdentifier': 'documentIdentifier',
DEF+'documentLocation': 'documentLocation',
DEF+'documentPublisher': 'documentPublisher',
DEF+'documentTitle': 'documentTitle',
DEF+'documentVersion': 'documentVersion',
DEF+'domainComponent': 'domainComponent',
DEF+'drink': 'drink',
DEF+'eduOrgHomePageURI': 'eduOrgHomePageURI',
DEF+'eduOrgIdentityAuthNPolicyURI': 'eduOrgIdentityAuthNPolicyURI',
DEF+'eduOrgLegalName': 'eduOrgLegalName',
DEF+'eduOrgSuperiorURI': 'eduOrgSuperiorURI',
DEF+'eduOrgWhitePagesURI': 'eduOrgWhitePagesURI',
DEF+'eduPersonAffiliation': 'eduPersonAffiliation',
DEF+'eduPersonEntitlement': 'eduPersonEntitlement',
DEF+'eduPersonNickname': 'eduPersonNickname',
DEF+'eduPersonOrgDN': 'eduPersonOrgDN',
DEF+'eduPersonOrgUnitDN': 'eduPersonOrgUnitDN',
DEF+'eduPersonPrimaryAffiliation': 'eduPersonPrimaryAffiliation',
DEF+'eduPersonPrimaryOrgUnitDN': 'eduPersonPrimaryOrgUnitDN',
DEF+'eduPersonPrincipalName': 'eduPersonPrincipalName',
DEF+'eduPersonScopedAffiliation': 'eduPersonScopedAffiliation',
DEF+'eduPersonTargetedID': 'eduPersonTargetedID',
DEF+'email': 'email',
DEF+'emailAddress': 'emailAddress',
DEF+'employeeNumber': 'employeeNumber',
DEF+'employeeType': 'employeeType',
DEF+'enhancedSearchGuide': 'enhancedSearchGuide',
DEF+'facsimileTelephoneNumber': 'facsimileTelephoneNumber',
DEF+'favouriteDrink': 'favouriteDrink',
DEF+'fax': 'fax',
DEF+'federationFeideSchemaVersion': 'federationFeideSchemaVersion',
DEF+'friendlyCountryName': 'friendlyCountryName',
DEF+'generationQualifier': 'generationQualifier',
DEF+'givenName': 'givenName',
DEF+'gn': 'gn',
DEF+'homePhone': 'homePhone',
DEF+'homePostalAddress': 'homePostalAddress',
DEF+'homeTelephoneNumber': 'homeTelephoneNumber',
DEF+'host': 'host',
DEF+'houseIdentifier': 'houseIdentifier',
DEF+'info': 'info',
DEF+'initials': 'initials',
DEF+'internationaliSDNNumber': 'internationaliSDNNumber',
DEF+'janetMailbox': 'janetMailbox',
DEF+'jpegPhoto': 'jpegPhoto',
DEF+'knowledgeInformation': 'knowledgeInformation',
DEF+'l': 'l',
DEF+'labeledURI': 'labeledURI',
DEF+'localityName': 'localityName',
DEF+'mDRecord': 'mDRecord',
DEF+'mXRecord': 'mXRecord',
DEF+'mail': 'mail',
DEF+'mailPreferenceOption': 'mailPreferenceOption',
DEF+'manager': 'manager',
DEF+'member': 'member',
DEF+'mobile': 'mobile',
DEF+'mobileTelephoneNumber': 'mobileTelephoneNumber',
DEF+'nSRecord': 'nSRecord',
DEF+'name': 'name',
DEF+'norEduOrgAcronym': 'norEduOrgAcronym',
DEF+'norEduOrgNIN': 'norEduOrgNIN',
DEF+'norEduOrgSchemaVersion': 'norEduOrgSchemaVersion',
DEF+'norEduOrgUniqueIdentifier': 'norEduOrgUniqueIdentifier',
DEF+'norEduOrgUniqueNumber': 'norEduOrgUniqueNumber',
DEF+'norEduOrgUnitUniqueIdentifier': 'norEduOrgUnitUniqueIdentifier',
DEF+'norEduOrgUnitUniqueNumber': 'norEduOrgUnitUniqueNumber',
DEF+'norEduPersonBirthDate': 'norEduPersonBirthDate',
DEF+'norEduPersonLIN': 'norEduPersonLIN',
DEF+'norEduPersonNIN': 'norEduPersonNIN',
DEF+'o': 'o',
DEF+'objectClass': 'objectClass',
DEF+'organizationName': 'organizationName',
DEF+'organizationalStatus': 'organizationalStatus',
DEF+'organizationalUnitName': 'organizationalUnitName',
DEF+'otherMailbox': 'otherMailbox',
DEF+'ou': 'ou',
DEF+'owner': 'owner',
DEF+'pager': 'pager',
DEF+'pagerTelephoneNumber': 'pagerTelephoneNumber',
DEF+'personalSignature': 'personalSignature',
DEF+'personalTitle': 'personalTitle',
DEF+'photo': 'photo',
DEF+'physicalDeliveryOfficeName': 'physicalDeliveryOfficeName',
DEF+'pkcs9email': 'pkcs9email',
DEF+'postOfficeBox': 'postOfficeBox',
DEF+'postalAddress': 'postalAddress',
DEF+'postalCode': 'postalCode',
DEF+'preferredDeliveryMethod': 'preferredDeliveryMethod',
DEF+'preferredLanguage': 'preferredLanguage',
DEF+'presentationAddress': 'presentationAddress',
DEF+'protocolInformation': 'protocolInformation',
DEF+'pseudonym': 'pseudonym',
DEF+'registeredAddress': 'registeredAddress',
DEF+'rfc822Mailbox': 'rfc822Mailbox',
DEF+'roleOccupant': 'roleOccupant',
DEF+'roomNumber': 'roomNumber',
DEF+'sOARecord': 'sOARecord',
DEF+'searchGuide': 'searchGuide',
DEF+'secretary': 'secretary',
DEF+'seeAlso': 'seeAlso',
DEF+'serialNumber': 'serialNumber',
DEF+'singleLevelQuality': 'singleLevelQuality',
DEF+'sn': 'sn',
DEF+'st': 'st',
DEF+'stateOrProvinceName': 'stateOrProvinceName',
DEF+'street': 'street',
DEF+'streetAddress': 'streetAddress',
DEF+'subtreeMaximumQuality': 'subtreeMaximumQuality',
DEF+'subtreeMinimumQuality': 'subtreeMinimumQuality',
DEF+'supportedAlgorithms': 'supportedAlgorithms',
DEF+'supportedApplicationContext': 'supportedApplicationContext',
DEF+'surname': 'surname',
DEF+'telephoneNumber': 'telephoneNumber',
DEF+'teletexTerminalIdentifier': 'teletexTerminalIdentifier',
DEF+'telexNumber': 'telexNumber',
DEF+'textEncodedORAddress': 'textEncodedORAddress',
DEF+'title': 'title',
DEF+'uid': 'uid',
DEF+'uniqueIdentifier': 'uniqueIdentifier',
DEF+'uniqueMember': 'uniqueMember',
DEF+'userCertificate': 'userCertificate',
DEF+'userClass': 'userClass',
DEF+'userPKCS12': 'userPKCS12',
DEF+'userPassword': 'userPassword',
DEF+'userSMIMECertificate': 'userSMIMECertificate',
DEF+'userid': 'userid',
DEF+'x121Address': 'x121Address',
DEF+'x500UniqueIdentifier': 'x500UniqueIdentifier',
},
'to': {
'aRecord': DEF+'aRecord',
'aliasedEntryName': DEF+'aliasedEntryName',
'aliasedObjectName': DEF+'aliasedObjectName',
'associatedDomain': DEF+'associatedDomain',
'associatedName': DEF+'associatedName',
'audio': DEF+'audio',
'authorityRevocationList': DEF+'authorityRevocationList',
'buildingName': DEF+'buildingName',
'businessCategory': DEF+'businessCategory',
'c': DEF+'c',
'cACertificate': DEF+'cACertificate',
'cNAMERecord': DEF+'cNAMERecord',
'carLicense': DEF+'carLicense',
'certificateRevocationList': DEF+'certificateRevocationList',
'cn': DEF+'cn',
'co': DEF+'co',
'commonName': DEF+'commonName',
'countryName': DEF+'countryName',
'crossCertificatePair': DEF+'crossCertificatePair',
'dITRedirect': DEF+'dITRedirect',
'dSAQuality': DEF+'dSAQuality',
'dc': DEF+'dc',
'deltaRevocationList': DEF+'deltaRevocationList',
'departmentNumber': DEF+'departmentNumber',
'description': DEF+'description',
'destinationIndicator': DEF+'destinationIndicator',
'displayName': DEF+'displayName',
'distinguishedName': DEF+'distinguishedName',
'dmdName': DEF+'dmdName',
'dnQualifier': DEF+'dnQualifier',
'documentAuthor': DEF+'documentAuthor',
'documentIdentifier': DEF+'documentIdentifier',
'documentLocation': DEF+'documentLocation',
'documentPublisher': DEF+'documentPublisher',
'documentTitle': DEF+'documentTitle',
'documentVersion': DEF+'documentVersion',
'domainComponent': DEF+'domainComponent',
'drink': DEF+'drink',
'eduOrgHomePageURI': DEF+'eduOrgHomePageURI',
'eduOrgIdentityAuthNPolicyURI': DEF+'eduOrgIdentityAuthNPolicyURI',
'eduOrgLegalName': DEF+'eduOrgLegalName',
'eduOrgSuperiorURI': DEF+'eduOrgSuperiorURI',
'eduOrgWhitePagesURI': DEF+'eduOrgWhitePagesURI',
'eduPersonAffiliation': DEF+'eduPersonAffiliation',
'eduPersonEntitlement': DEF+'eduPersonEntitlement',
'eduPersonNickname': DEF+'eduPersonNickname',
'eduPersonOrgDN': DEF+'eduPersonOrgDN',
'eduPersonOrgUnitDN': DEF+'eduPersonOrgUnitDN',
'eduPersonPrimaryAffiliation': DEF+'eduPersonPrimaryAffiliation',
'eduPersonPrimaryOrgUnitDN': DEF+'eduPersonPrimaryOrgUnitDN',
'eduPersonPrincipalName': DEF+'eduPersonPrincipalName',
'eduPersonScopedAffiliation': DEF+'eduPersonScopedAffiliation',
'eduPersonTargetedID': DEF+'eduPersonTargetedID',
'email': DEF+'email',
'emailAddress': DEF+'emailAddress',
'employeeNumber': DEF+'employeeNumber',
'employeeType': DEF+'employeeType',
'enhancedSearchGuide': DEF+'enhancedSearchGuide',
'facsimileTelephoneNumber': DEF+'facsimileTelephoneNumber',
'favouriteDrink': DEF+'favouriteDrink',
'fax': DEF+'fax',
'federationFeideSchemaVersion': DEF+'federationFeideSchemaVersion',
'friendlyCountryName': DEF+'friendlyCountryName',
'generationQualifier': DEF+'generationQualifier',
'givenName': DEF+'givenName',
'gn': DEF+'gn',
'homePhone': DEF+'homePhone',
'homePostalAddress': DEF+'homePostalAddress',
'homeTelephoneNumber': DEF+'homeTelephoneNumber',
'host': DEF+'host',
'houseIdentifier': DEF+'houseIdentifier',
'info': DEF+'info',
'initials': DEF+'initials',
'internationaliSDNNumber': DEF+'internationaliSDNNumber',
'janetMailbox': DEF+'janetMailbox',
'jpegPhoto': DEF+'jpegPhoto',
'knowledgeInformation': DEF+'knowledgeInformation',
'l': DEF+'l',
'labeledURI': DEF+'labeledURI',
'localityName': DEF+'localityName',
'mDRecord': DEF+'mDRecord',
'mXRecord': DEF+'mXRecord',
'mail': DEF+'mail',
'mailPreferenceOption': DEF+'mailPreferenceOption',
'manager': DEF+'manager',
'member': DEF+'member',
'mobile': DEF+'mobile',
'mobileTelephoneNumber': DEF+'mobileTelephoneNumber',
'nSRecord': DEF+'nSRecord',
'name': DEF+'name',
'norEduOrgAcronym': DEF+'norEduOrgAcronym',
'norEduOrgNIN': DEF+'norEduOrgNIN',
'norEduOrgSchemaVersion': DEF+'norEduOrgSchemaVersion',
'norEduOrgUniqueIdentifier': DEF+'norEduOrgUniqueIdentifier',
'norEduOrgUniqueNumber': DEF+'norEduOrgUniqueNumber',
'norEduOrgUnitUniqueIdentifier': DEF+'norEduOrgUnitUniqueIdentifier',
'norEduOrgUnitUniqueNumber': DEF+'norEduOrgUnitUniqueNumber',
'norEduPersonBirthDate': DEF+'norEduPersonBirthDate',
'norEduPersonLIN': DEF+'norEduPersonLIN',
'norEduPersonNIN': DEF+'norEduPersonNIN',
'o': DEF+'o',
'objectClass': DEF+'objectClass',
'organizationName': DEF+'organizationName',
'organizationalStatus': DEF+'organizationalStatus',
'organizationalUnitName': DEF+'organizationalUnitName',
'otherMailbox': DEF+'otherMailbox',
'ou': DEF+'ou',
'owner': DEF+'owner',
'pager': DEF+'pager',
'pagerTelephoneNumber': DEF+'pagerTelephoneNumber',
'personalSignature': DEF+'personalSignature',
'personalTitle': DEF+'personalTitle',
'photo': DEF+'photo',
'physicalDeliveryOfficeName': DEF+'physicalDeliveryOfficeName',
'pkcs9email': DEF+'pkcs9email',
'postOfficeBox': DEF+'postOfficeBox',
'postalAddress': DEF+'postalAddress',
'postalCode': DEF+'postalCode',
'preferredDeliveryMethod': DEF+'preferredDeliveryMethod',
'preferredLanguage': DEF+'preferredLanguage',
'presentationAddress': DEF+'presentationAddress',
'protocolInformation': DEF+'protocolInformation',
'pseudonym': DEF+'pseudonym',
'registeredAddress': DEF+'registeredAddress',
'rfc822Mailbox': DEF+'rfc822Mailbox',
'roleOccupant': DEF+'roleOccupant',
'roomNumber': DEF+'roomNumber',
'sOARecord': DEF+'sOARecord',
'searchGuide': DEF+'searchGuide',
'secretary': DEF+'secretary',
'seeAlso': DEF+'seeAlso',
'serialNumber': DEF+'serialNumber',
'singleLevelQuality': DEF+'singleLevelQuality',
'sn': DEF+'sn',
'st': DEF+'st',
'stateOrProvinceName': DEF+'stateOrProvinceName',
'street': DEF+'street',
'streetAddress': DEF+'streetAddress',
'subtreeMaximumQuality': DEF+'subtreeMaximumQuality',
'subtreeMinimumQuality': DEF+'subtreeMinimumQuality',
'supportedAlgorithms': DEF+'supportedAlgorithms',
'supportedApplicationContext': DEF+'supportedApplicationContext',
'surname': DEF+'surname',
'telephoneNumber': DEF+'telephoneNumber',
'teletexTerminalIdentifier': DEF+'teletexTerminalIdentifier',
'telexNumber': DEF+'telexNumber',
'textEncodedORAddress': DEF+'textEncodedORAddress',
'title': DEF+'title',
'uid': DEF+'uid',
'uniqueIdentifier': DEF+'uniqueIdentifier',
'uniqueMember': DEF+'uniqueMember',
'userCertificate': DEF+'userCertificate',
'userClass': DEF+'userClass',
'userPKCS12': DEF+'userPKCS12',
'userPassword': DEF+'userPassword',
'userSMIMECertificate': DEF+'userSMIMECertificate',
'userid': DEF+'userid',
'x121Address': DEF+'x121Address',
'x500UniqueIdentifier': DEF+'x500UniqueIdentifier',
}
}
}

View File

@@ -1,104 +1,52 @@
__author__ = 'rolandh'
EDUPERSON_OID = "urn:oid:1.3.6.1.4.1.5923.1.1.1."
X500ATTR_OID = "urn:oid:2.5.4."
NOREDUPERSON_OID = "urn:oid:1.3.6.1.4.1.2428.90.1."
NETSCAPE_LDAP = "urn:oid:2.16.840.1.113730.3.1."
EDUCOURSE_OID = 'urn:oid:1.3.6.1.4.1.5923.1.6.1.'
EDUPERSON_OID = 'urn:oid:1.3.6.1.4.1.5923.1.1.1.'
NETSCAPE_LDAP = 'urn:oid:2.16.840.1.113730.3.1.'
NOREDUPERSON_OID = 'urn:oid:1.3.6.1.4.1.2428.90.1.'
PKCS_9 = 'urn:oid:1.2.840.113549.1.9.1.'
SCHAC = 'urn:oid:1.3.6.1.4.1.25178.1.2.'
SIS = 'urn:oid:1.2.752.194.10.2.'
UCL_DIR_PILOT = 'urn:oid:0.9.2342.19200300.100.1.'
PKCS_9 = "urn:oid:1.2.840.113549.1.9.1."
UMICH = "urn:oid:1.3.6.1.4.1.250.1.57."
SCHAC = "urn:oid:1.3.6.1.4.1.25178.1.2."
UMICH = 'urn:oid:1.3.6.1.4.1.250.1.57.'
X500ATTR_OID = 'urn:oid:2.5.4.'
#urn:oid:1.3.6.1.4.1.1466.115.121.1.26
MAP = {
"identifier": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
"fro": {
'identifier': 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
'fro': {
EDUCOURSE_OID+'1': 'eduCourseOffering',
EDUCOURSE_OID+'2': 'eduCourseMember',
EDUPERSON_OID+'1': 'eduPersonAffiliation',
EDUPERSON_OID+'2': 'eduPersonNickname',
EDUPERSON_OID+'9': 'eduPersonScopedAffiliation',
EDUPERSON_OID+'11': 'eduPersonAssurance',
EDUPERSON_OID+'10': 'eduPersonTargetedID',
EDUPERSON_OID+'3': 'eduPersonOrgDN',
EDUPERSON_OID+'4': 'eduPersonOrgUnitDN',
EDUPERSON_OID+'5': 'eduPersonPrimaryAffiliation',
EDUPERSON_OID+'6': 'eduPersonPrincipalName',
EDUPERSON_OID+'7': 'eduPersonEntitlement',
EDUPERSON_OID+'8': 'eduPersonPrimaryOrgUnitDN',
EDUPERSON_OID+'9': 'eduPersonScopedAffiliation',
EDUPERSON_OID+'10': 'eduPersonTargetedID',
EDUPERSON_OID+'11': 'eduPersonAssurance',
NETSCAPE_LDAP+'1': 'carLicense',
NETSCAPE_LDAP+'2': 'departmentNumber',
NETSCAPE_LDAP+'3': 'employeeNumber',
NETSCAPE_LDAP+'4': 'employeeType',
NETSCAPE_LDAP+'39': 'preferredLanguage',
NETSCAPE_LDAP+'40': 'userSMIMECertificate',
NETSCAPE_LDAP+'216': 'userPKCS12',
NETSCAPE_LDAP+'241': 'displayName',
NOREDUPERSON_OID+'1': 'norEduOrgUniqueNumber',
NOREDUPERSON_OID+'2': 'norEduOrgUnitUniqueNumber',
NOREDUPERSON_OID+'3': 'norEduPersonBirthDate',
NOREDUPERSON_OID+'4': 'norEduPersonLIN',
NOREDUPERSON_OID+'5': 'norEduPersonNIN',
NOREDUPERSON_OID+'6': 'norEduOrgAcronym',
NOREDUPERSON_OID+'7': 'norEduOrgUniqueIdentifier',
NOREDUPERSON_OID+'4': 'norEduPersonLIN',
EDUPERSON_OID+'1': 'eduPersonAffiliation',
NOREDUPERSON_OID+'2': 'norEduOrgUnitUniqueNumber',
NETSCAPE_LDAP+'40': 'userSMIMECertificate',
NOREDUPERSON_OID+'1': 'norEduOrgUniqueNumber',
NETSCAPE_LDAP+'241': 'displayName',
UCL_DIR_PILOT+'37': 'associatedDomain',
EDUPERSON_OID+'6': 'eduPersonPrincipalName',
NOREDUPERSON_OID+'8': 'norEduOrgUnitUniqueIdentifier',
NOREDUPERSON_OID+'9': 'federationFeideSchemaVersion',
X500ATTR_OID+'53': 'deltaRevocationList',
X500ATTR_OID+'52': 'supportedAlgorithms',
X500ATTR_OID+'51': 'houseIdentifier',
X500ATTR_OID+'50': 'uniqueMember',
X500ATTR_OID+'19': 'physicalDeliveryOfficeName',
X500ATTR_OID+'18': 'postOfficeBox',
X500ATTR_OID+'17': 'postalCode',
X500ATTR_OID+'16': 'postalAddress',
X500ATTR_OID+'15': 'businessCategory',
X500ATTR_OID+'14': 'searchGuide',
EDUPERSON_OID+'5': 'eduPersonPrimaryAffiliation',
X500ATTR_OID+'12': 'title',
X500ATTR_OID+'11': 'ou',
X500ATTR_OID+'10': 'o',
X500ATTR_OID+'37': 'cACertificate',
X500ATTR_OID+'36': 'userCertificate',
X500ATTR_OID+'31': 'member',
X500ATTR_OID+'30': 'supportedApplicationContext',
X500ATTR_OID+'33': 'roleOccupant',
X500ATTR_OID+'32': 'owner',
NETSCAPE_LDAP+'1': 'carLicense',
NOREDUPERSON_OID+'10': 'norEduPersonLegalName',
NOREDUPERSON_OID+'11': 'norEduOrgSchemaVersion',
NOREDUPERSON_OID+'12': 'norEduOrgNIN',
PKCS_9+'1': 'email',
NETSCAPE_LDAP+'3': 'employeeNumber',
NETSCAPE_LDAP+'2': 'departmentNumber',
X500ATTR_OID+'39': 'certificateRevocationList',
X500ATTR_OID+'38': 'authorityRevocationList',
NETSCAPE_LDAP+'216': 'userPKCS12',
EDUPERSON_OID+'8': 'eduPersonPrimaryOrgUnitDN',
X500ATTR_OID+'9': 'street',
X500ATTR_OID+'8': 'st',
NETSCAPE_LDAP+'39': 'preferredLanguage',
EDUPERSON_OID+'7': 'eduPersonEntitlement',
X500ATTR_OID+'2': 'knowledgeInformation',
X500ATTR_OID+'7': 'l',
X500ATTR_OID+'6': 'c',
X500ATTR_OID+'5': 'serialNumber',
X500ATTR_OID+'4': 'sn',
X500ATTR_OID+'3': 'cn',
UCL_DIR_PILOT+'60': 'jpegPhoto',
X500ATTR_OID+'65': 'pseudonym',
NOREDUPERSON_OID+'5': 'norEduPersonNIN',
UCL_DIR_PILOT+'3': 'mail',
UCL_DIR_PILOT+'25': 'dc',
X500ATTR_OID+'40': 'crossCertificatePair',
X500ATTR_OID+'42': 'givenName',
X500ATTR_OID+'43': 'initials',
X500ATTR_OID+'44': 'generationQualifier',
X500ATTR_OID+'45': 'x500UniqueIdentifier',
X500ATTR_OID+'46': 'dnQualifier',
X500ATTR_OID+'47': 'enhancedSearchGuide',
X500ATTR_OID+'48': 'protocolInformation',
X500ATTR_OID+'54': 'dmdName',
NETSCAPE_LDAP+'4': 'employeeType',
X500ATTR_OID+'22': 'teletexTerminalIdentifier',
X500ATTR_OID+'23': 'facsimileTelephoneNumber',
X500ATTR_OID+'20': 'telephoneNumber',
X500ATTR_OID+'21': 'telexNumber',
X500ATTR_OID+'26': 'registeredAddress',
X500ATTR_OID+'27': 'destinationIndicator',
X500ATTR_OID+'24': 'x121Address',
X500ATTR_OID+'25': 'internationaliSDNNumber',
X500ATTR_OID+'28': 'preferredDeliveryMethod',
X500ATTR_OID+'29': 'presentationAddress',
EDUPERSON_OID+'3': 'eduPersonOrgDN',
NOREDUPERSON_OID+'3': 'norEduPersonBirthDate',
UMICH+'57': 'labeledURI',
UCL_DIR_PILOT+'1': 'uid',
UCL_DIR_PILOT+'43': 'co',
SCHAC+'1': 'schacMotherTongue',
SCHAC+'2': 'schacGender',
SCHAC+'3': 'schacDateOfBirth',
@@ -119,132 +67,177 @@ MAP = {
SCHAC+'19': 'schacUserStatus',
SCHAC+'20': 'schacProjectMembership',
SCHAC+'21': 'schacProjectSpecificRole',
},
"to": {
'cn': X500ATTR_OID+'3',
'commonName': X500ATTR_OID+'3',
'roleOccupant': X500ATTR_OID+'33',
'gn': X500ATTR_OID+'42',
'norEduPersonNIN': NOREDUPERSON_OID+'5',
'title': X500ATTR_OID+'12',
'facsimileTelephoneNumber': X500ATTR_OID+'23',
'mail': UCL_DIR_PILOT+'3',
'postOfficeBox': X500ATTR_OID+'18',
'fax': X500ATTR_OID+'23',
'telephoneNumber': X500ATTR_OID+'20',
'norEduPersonBirthDate': NOREDUPERSON_OID+'3',
'rfc822Mailbox': UCL_DIR_PILOT+'3',
'dc': UCL_DIR_PILOT+'25',
'countryName': X500ATTR_OID+'6',
'emailAddress': PKCS_9+'1',
'employeeNumber': NETSCAPE_LDAP+'3',
'organizationName': X500ATTR_OID+'10',
'eduPersonAssurance': EDUPERSON_OID+'11',
'norEduOrgAcronym': NOREDUPERSON_OID+'6',
'registeredAddress': X500ATTR_OID+'26',
'physicalDeliveryOfficeName': X500ATTR_OID+'19',
SIS+'1': 'sisLegalGuardianFor',
SIS+'2': 'sisSchoolGrade',
UCL_DIR_PILOT+'1': 'uid',
UCL_DIR_PILOT+'3': 'mail',
UCL_DIR_PILOT+'25': 'dc',
UCL_DIR_PILOT+'37': 'associatedDomain',
UCL_DIR_PILOT+'43': 'co',
UCL_DIR_PILOT+'60': 'jpegPhoto',
UMICH+'57': 'labeledURI',
X500ATTR_OID+'2': 'knowledgeInformation',
X500ATTR_OID+'3': 'cn',
X500ATTR_OID+'4': 'sn',
X500ATTR_OID+'5': 'serialNumber',
X500ATTR_OID+'6': 'c',
X500ATTR_OID+'7': 'l',
X500ATTR_OID+'8': 'st',
X500ATTR_OID+'9': 'street',
X500ATTR_OID+'10': 'o',
X500ATTR_OID+'11': 'ou',
X500ATTR_OID+'12': 'title',
X500ATTR_OID+'14': 'searchGuide',
X500ATTR_OID+'15': 'businessCategory',
X500ATTR_OID+'16': 'postalAddress',
X500ATTR_OID+'17': 'postalCode',
X500ATTR_OID+'18': 'postOfficeBox',
X500ATTR_OID+'19': 'physicalDeliveryOfficeName',
X500ATTR_OID+'20': 'telephoneNumber',
X500ATTR_OID+'21': 'telexNumber',
X500ATTR_OID+'22': 'teletexTerminalIdentifier',
X500ATTR_OID+'23': 'facsimileTelephoneNumber',
X500ATTR_OID+'24': 'x121Address',
X500ATTR_OID+'25': 'internationaliSDNNumber',
X500ATTR_OID+'26': 'registeredAddress',
X500ATTR_OID+'27': 'destinationIndicator',
X500ATTR_OID+'28': 'preferredDeliveryMethod',
X500ATTR_OID+'29': 'presentationAddress',
X500ATTR_OID+'30': 'supportedApplicationContext',
X500ATTR_OID+'31': 'member',
X500ATTR_OID+'32': 'owner',
X500ATTR_OID+'33': 'roleOccupant',
X500ATTR_OID+'36': 'userCertificate',
X500ATTR_OID+'37': 'cACertificate',
X500ATTR_OID+'38': 'authorityRevocationList',
X500ATTR_OID+'39': 'certificateRevocationList',
X500ATTR_OID+'40': 'crossCertificatePair',
X500ATTR_OID+'42': 'givenName',
X500ATTR_OID+'43': 'initials',
X500ATTR_OID+'44': 'generationQualifier',
X500ATTR_OID+'45': 'x500UniqueIdentifier',
X500ATTR_OID+'46': 'dnQualifier',
X500ATTR_OID+'47': 'enhancedSearchGuide',
X500ATTR_OID+'48': 'protocolInformation',
X500ATTR_OID+'50': 'uniqueMember',
X500ATTR_OID+'51': 'houseIdentifier',
X500ATTR_OID+'52': 'supportedAlgorithms',
X500ATTR_OID+'53': 'deltaRevocationList',
X500ATTR_OID+'54': 'dmdName',
X500ATTR_OID+'65': 'pseudonym',
},
'to': {
'associatedDomain': UCL_DIR_PILOT+'37',
'l': X500ATTR_OID+'7',
'stateOrProvinceName': X500ATTR_OID+'8',
'federationFeideSchemaVersion': NOREDUPERSON_OID+'9',
'pkcs9email': PKCS_9+'1',
'givenName': X500ATTR_OID+'42',
'givenname': X500ATTR_OID+'42',
'x500UniqueIdentifier': X500ATTR_OID+'45',
'eduPersonNickname': EDUPERSON_OID+'2',
'houseIdentifier': X500ATTR_OID+'51',
'street': X500ATTR_OID+'9',
'supportedAlgorithms': X500ATTR_OID+'52',
'preferredLanguage': NETSCAPE_LDAP+'39',
'postalAddress': X500ATTR_OID+'16',
'email': PKCS_9+'1',
'norEduOrgUnitUniqueIdentifier': NOREDUPERSON_OID+'8',
'eduPersonPrimaryOrgUnitDN': EDUPERSON_OID+'8',
'c': X500ATTR_OID+'6',
'teletexTerminalIdentifier': X500ATTR_OID+'22',
'o': X500ATTR_OID+'10',
'cACertificate': X500ATTR_OID+'37',
'telexNumber': X500ATTR_OID+'21',
'ou': X500ATTR_OID+'11',
'initials': X500ATTR_OID+'43',
'eduPersonOrgUnitDN': EDUPERSON_OID+'4',
'deltaRevocationList': X500ATTR_OID+'53',
'norEduPersonLIN': NOREDUPERSON_OID+'4',
'supportedApplicationContext': X500ATTR_OID+'30',
'eduPersonEntitlement': EDUPERSON_OID+'7',
'generationQualifier': X500ATTR_OID+'44',
'eduPersonAffiliation': EDUPERSON_OID+'1',
'edupersonaffiliation': EDUPERSON_OID+'1',
'eduPersonPrincipalName': EDUPERSON_OID+'6',
'edupersonprincipalname': EDUPERSON_OID+'6',
'eppn': EDUPERSON_OID+'6',
'localityName': X500ATTR_OID+'7',
'owner': X500ATTR_OID+'32',
'norEduOrgUnitUniqueNumber': NOREDUPERSON_OID+'2',
'searchGuide': X500ATTR_OID+'14',
'certificateRevocationList': X500ATTR_OID+'39',
'organizationalUnitName': X500ATTR_OID+'11',
'userCertificate': X500ATTR_OID+'36',
'preferredDeliveryMethod': X500ATTR_OID+'28',
'internationaliSDNNumber': X500ATTR_OID+'25',
'uniqueMember': X500ATTR_OID+'50',
'departmentNumber': NETSCAPE_LDAP+'2',
'enhancedSearchGuide': X500ATTR_OID+'47',
'userPKCS12': NETSCAPE_LDAP+'216',
'eduPersonTargetedID': EDUPERSON_OID+'10',
'norEduOrgUniqueNumber': NOREDUPERSON_OID+'1',
'x121Address': X500ATTR_OID+'24',
'destinationIndicator': X500ATTR_OID+'27',
'eduPersonPrimaryAffiliation': EDUPERSON_OID+'5',
'surname': X500ATTR_OID+'4',
'jpegPhoto': UCL_DIR_PILOT+'60',
'eduPersonScopedAffiliation': EDUPERSON_OID+'9',
'edupersonscopedaffiliation': EDUPERSON_OID+'9',
'protocolInformation': X500ATTR_OID+'48',
'knowledgeInformation': X500ATTR_OID+'2',
'employeeType': NETSCAPE_LDAP+'4',
'userSMIMECertificate': NETSCAPE_LDAP+'40',
'member': X500ATTR_OID+'31',
'streetAddress': X500ATTR_OID+'9',
'dmdName': X500ATTR_OID+'54',
'postalCode': X500ATTR_OID+'17',
'pseudonym': X500ATTR_OID+'65',
'dnQualifier': X500ATTR_OID+'46',
'crossCertificatePair': X500ATTR_OID+'40',
'eduPersonOrgDN': EDUPERSON_OID+'3',
'authorityRevocationList': X500ATTR_OID+'38',
'displayName': NETSCAPE_LDAP+'241',
'businessCategory': X500ATTR_OID+'15',
'serialNumber': X500ATTR_OID+'5',
'norEduOrgUniqueIdentifier': NOREDUPERSON_OID+'7',
'st': X500ATTR_OID+'8',
'c': X500ATTR_OID+'6',
'cACertificate': X500ATTR_OID+'37',
'carLicense': NETSCAPE_LDAP+'1',
'presentationAddress': X500ATTR_OID+'29',
'sn': X500ATTR_OID+'4',
'domainComponent': UCL_DIR_PILOT+'25',
'labeledURI': UMICH+'57',
'uid': UCL_DIR_PILOT+'1',
'certificateRevocationList': X500ATTR_OID+'39',
'cn': X500ATTR_OID+'3',
'co': UCL_DIR_PILOT+'43',
'friendlyCountryName': UCL_DIR_PILOT+'43',
'schacMotherTongue':SCHAC+'1',
'crossCertificatePair': X500ATTR_OID+'40',
'dc': UCL_DIR_PILOT+'25',
'deltaRevocationList': X500ATTR_OID+'53',
'departmentNumber': NETSCAPE_LDAP+'2',
'destinationIndicator': X500ATTR_OID+'27',
'displayName': NETSCAPE_LDAP+'241',
'dmdName': X500ATTR_OID+'54',
'dnQualifier': X500ATTR_OID+'46',
'eduCourseMember': EDUCOURSE_OID+'2',
'eduCourseOffering': EDUCOURSE_OID+'1',
'eduPersonAffiliation': EDUPERSON_OID+'1',
'eduPersonAssurance': EDUPERSON_OID+'11',
'eduPersonEntitlement': EDUPERSON_OID+'7',
'eduPersonNickname': EDUPERSON_OID+'2',
'eduPersonOrgDN': EDUPERSON_OID+'3',
'eduPersonOrgUnitDN': EDUPERSON_OID+'4',
'eduPersonPrimaryAffiliation': EDUPERSON_OID+'5',
'eduPersonPrimaryOrgUnitDN': EDUPERSON_OID+'8',
'eduPersonPrincipalName': EDUPERSON_OID+'6',
'eduPersonScopedAffiliation': EDUPERSON_OID+'9',
'eduPersonTargetedID': EDUPERSON_OID+'10',
'email': PKCS_9+'1',
'employeeNumber': NETSCAPE_LDAP+'3',
'employeeType': NETSCAPE_LDAP+'4',
'enhancedSearchGuide': X500ATTR_OID+'47',
'facsimileTelephoneNumber': X500ATTR_OID+'23',
'federationFeideSchemaVersion': NOREDUPERSON_OID+'9',
'generationQualifier': X500ATTR_OID+'44',
'givenName': X500ATTR_OID+'42',
'houseIdentifier': X500ATTR_OID+'51',
'initials': X500ATTR_OID+'43',
'internationaliSDNNumber': X500ATTR_OID+'25',
'jpegPhoto': UCL_DIR_PILOT+'60',
'knowledgeInformation': X500ATTR_OID+'2',
'l': X500ATTR_OID+'7',
'labeledURI': UMICH+'57',
'mail': UCL_DIR_PILOT+'3',
'member': X500ATTR_OID+'31',
'norEduOrgAcronym': NOREDUPERSON_OID+'6',
'norEduOrgNIN': NOREDUPERSON_OID+'12',
'norEduOrgSchemaVersion': NOREDUPERSON_OID+'11',
'norEduOrgUniqueIdentifier': NOREDUPERSON_OID+'7',
'norEduOrgUniqueNumber': NOREDUPERSON_OID+'1',
'norEduOrgUnitUniqueIdentifier': NOREDUPERSON_OID+'8',
'norEduOrgUnitUniqueNumber': NOREDUPERSON_OID+'2',
'norEduPersonBirthDate': NOREDUPERSON_OID+'3',
'norEduPersonLIN': NOREDUPERSON_OID+'4',
'norEduPersonLegalName': NOREDUPERSON_OID+'10',
'norEduPersonNIN': NOREDUPERSON_OID+'5',
'o': X500ATTR_OID+'10',
'ou': X500ATTR_OID+'11',
'owner': X500ATTR_OID+'32',
'physicalDeliveryOfficeName': X500ATTR_OID+'19',
'postOfficeBox': X500ATTR_OID+'18',
'postalAddress': X500ATTR_OID+'16',
'postalCode': X500ATTR_OID+'17',
'preferredDeliveryMethod': X500ATTR_OID+'28',
'preferredLanguage': NETSCAPE_LDAP+'39',
'presentationAddress': X500ATTR_OID+'29',
'protocolInformation': X500ATTR_OID+'48',
'pseudonym': X500ATTR_OID+'65',
'registeredAddress': X500ATTR_OID+'26',
'roleOccupant': X500ATTR_OID+'33',
'schacCountryOfCitizenship': SCHAC+'5',
'schacCountryOfResidence': SCHAC+'11',
'schacDateOfBirth': SCHAC+'3',
'schacExpiryDate': SCHAC+'17',
'schacGender': SCHAC+'2',
'schacDateOfBirth':SCHAC+'3',
'schacPlaceOfBirth': SCHAC+'4',
'schacCountryOfCitizenship':SCHAC+'5',
'schacSn1': SCHAC+'6',
'schacSn2': SCHAC+'7',
'schacPersonalTitle':SCHAC+'8',
'schacHomeOrganization': SCHAC+'9',
'schacHomeOrganizationType': SCHAC+'10',
'schacCountryOfResidence': SCHAC+'11',
'schacUserPresenceID': SCHAC+'12',
'schacMotherTongue': SCHAC+'1',
'schacPersonalPosition': SCHAC+'13',
'schacPersonalTitle': SCHAC+'8',
'schacPersonalUniqueCode': SCHAC+'14',
'schacPersonalUniqueID': SCHAC+'15',
'schacExpiryDate': SCHAC+'17',
'schacUserPrivateAttribute': SCHAC+'18',
'schacUserStatus': SCHAC+'19',
'schacPlaceOfBirth': SCHAC+'4',
'schacProjectMembership': SCHAC+'20',
'schacProjectSpecificRole': SCHAC+'21',
}
}
'schacSn1': SCHAC+'6',
'schacSn2': SCHAC+'7',
'schacUserPresenceID': SCHAC+'12',
'schacUserPrivateAttribute': SCHAC+'18',
'schacUserStatus': SCHAC+'19',
'searchGuide': X500ATTR_OID+'14',
'serialNumber': X500ATTR_OID+'5',
'sisLegalGuardianFor': SIS+'1',
'sisSchoolGrade': SIS+'2',
'sn': X500ATTR_OID+'4',
'st': X500ATTR_OID+'8',
'street': X500ATTR_OID+'9',
'supportedAlgorithms': X500ATTR_OID+'52',
'supportedApplicationContext': X500ATTR_OID+'30',
'telephoneNumber': X500ATTR_OID+'20',
'teletexTerminalIdentifier': X500ATTR_OID+'22',
'telexNumber': X500ATTR_OID+'21',
'title': X500ATTR_OID+'12',
'uid': UCL_DIR_PILOT+'1',
'uniqueMember': X500ATTR_OID+'50',
'userCertificate': X500ATTR_OID+'36',
'userPKCS12': NETSCAPE_LDAP+'216',
'userSMIMECertificate': NETSCAPE_LDAP+'40',
'x121Address': X500ATTR_OID+'24',
'x500UniqueIdentifier': X500ATTR_OID+'45',
}
}

View File

@@ -1,73 +1,82 @@
EDUPERSON_OID = "urn:oid:1.3.6.1.4.1.5923.1.1.1."
X500ATTR = "urn:oid:2.5.4."
NOREDUPERSON_OID = "urn:oid:1.3.6.1.4.1.2428.90.1."
NETSCAPE_LDAP = "urn:oid:2.16.840.1.113730.3.1."
UCL_DIR_PILOT = "urn:oid:0.9.2342.19200300.100.1."
PKCS_9 = "urn:oid:1.2.840.113549.1.9."
UMICH = "urn:oid:1.3.6.1.4.1.250.1.57."
EDUPERSON_OID = 'urn:oid:1.3.6.1.4.1.5923.1.1.1.'
NETSCAPE_LDAP = 'urn:oid:2.16.840.1.113730.3.1.'
NOREDUPERSON_OID = 'urn:oid:1.3.6.1.4.1.2428.90.1.'
PKCS_9 = 'urn:oid:1.2.840.113549.1.9.'
UCL_DIR_PILOT = 'urn:oid:0.9.2342.19200300.100.1.'
UMICH = 'urn:oid:1.3.6.1.4.1.250.1.57.'
X500ATTR = 'urn:oid:2.5.4.'
MAP = {
"identifier": "urn:mace:shibboleth:1.0:attributeNamespace:uri",
"fro": {
'fro': {
EDUPERSON_OID+'1': 'eduPersonAffiliation',
EDUPERSON_OID+'2': 'eduPersonNickname',
EDUPERSON_OID+'9': 'eduPersonScopedAffiliation',
EDUPERSON_OID+'11': 'eduPersonAssurance',
EDUPERSON_OID+'10': 'eduPersonTargetedID',
EDUPERSON_OID+'3': 'eduPersonOrgDN',
EDUPERSON_OID+'4': 'eduPersonOrgUnitDN',
EDUPERSON_OID+'5': 'eduPersonPrimaryAffiliation',
EDUPERSON_OID+'6': 'eduPersonPrincipalName',
EDUPERSON_OID+'7': 'eduPersonEntitlement',
EDUPERSON_OID+'8': 'eduPersonPrimaryOrgUnitDN',
EDUPERSON_OID+'9': 'eduPersonScopedAffiliation',
EDUPERSON_OID+'10': 'eduPersonTargetedID',
EDUPERSON_OID+'11': 'eduPersonAssurance',
NETSCAPE_LDAP+'1': 'carLicense',
NETSCAPE_LDAP+'2': 'departmentNumber',
NETSCAPE_LDAP+'3': 'employeeNumber',
NETSCAPE_LDAP+'4': 'employeeType',
NETSCAPE_LDAP+'39': 'preferredLanguage',
NETSCAPE_LDAP+'40': 'userSMIMECertificate',
NETSCAPE_LDAP+'216': 'userPKCS12',
NETSCAPE_LDAP+'241': 'displayName',
NOREDUPERSON_OID+'1': 'norEduOrgUniqueNumber',
NOREDUPERSON_OID+'2': 'norEduOrgUnitUniqueNumber',
NOREDUPERSON_OID+'3': 'norEduPersonBirthDate',
NOREDUPERSON_OID+'4': 'norEduPersonLIN',
NOREDUPERSON_OID+'5': 'norEduPersonNIN',
NOREDUPERSON_OID+'6': 'norEduOrgAcronym',
NOREDUPERSON_OID+'7': 'norEduOrgUniqueIdentifier',
NOREDUPERSON_OID+'4': 'norEduPersonLIN',
EDUPERSON_OID+'1': 'eduPersonAffiliation',
NOREDUPERSON_OID+'2': 'norEduOrgUnitUniqueNumber',
NETSCAPE_LDAP+'40': 'userSMIMECertificate',
NOREDUPERSON_OID+'1': 'norEduOrgUniqueNumber',
NETSCAPE_LDAP+'241': 'displayName',
UCL_DIR_PILOT+'37': 'associatedDomain',
EDUPERSON_OID+'6': 'eduPersonPrincipalName',
NOREDUPERSON_OID+'8': 'norEduOrgUnitUniqueIdentifier',
NOREDUPERSON_OID+'9': 'federationFeideSchemaVersion',
X500ATTR+'53': 'deltaRevocationList',
X500ATTR+'52': 'supportedAlgorithms',
X500ATTR+'51': 'houseIdentifier',
X500ATTR+'50': 'uniqueMember',
X500ATTR+'19': 'physicalDeliveryOfficeName',
X500ATTR+'18': 'postOfficeBox',
X500ATTR+'17': 'postalCode',
X500ATTR+'16': 'postalAddress',
X500ATTR+'15': 'businessCategory',
X500ATTR+'14': 'searchGuide',
EDUPERSON_OID+'5': 'eduPersonPrimaryAffiliation',
X500ATTR+'12': 'title',
X500ATTR+'11': 'ou',
X500ATTR+'10': 'o',
X500ATTR+'37': 'cACertificate',
X500ATTR+'36': 'userCertificate',
X500ATTR+'31': 'member',
X500ATTR+'30': 'supportedApplicationContext',
X500ATTR+'33': 'roleOccupant',
X500ATTR+'32': 'owner',
NETSCAPE_LDAP+'1': 'carLicense',
PKCS_9+'1': 'email',
NETSCAPE_LDAP+'3': 'employeeNumber',
NETSCAPE_LDAP+'2': 'departmentNumber',
X500ATTR+'39': 'certificateRevocationList',
X500ATTR+'38': 'authorityRevocationList',
NETSCAPE_LDAP+'216': 'userPKCS12',
EDUPERSON_OID+'8': 'eduPersonPrimaryOrgUnitDN',
X500ATTR+'9': 'street',
X500ATTR+'8': 'st',
NETSCAPE_LDAP+'39': 'preferredLanguage',
EDUPERSON_OID+'7': 'eduPersonEntitlement',
X500ATTR+'2': 'knowledgeInformation',
X500ATTR+'7': 'l',
X500ATTR+'6': 'c',
X500ATTR+'5': 'serialNumber',
X500ATTR+'4': 'sn',
UCL_DIR_PILOT+'60': 'jpegPhoto',
X500ATTR+'65': 'pseudonym',
NOREDUPERSON_OID+'5': 'norEduPersonNIN',
UCL_DIR_PILOT+'3': 'mail',
UCL_DIR_PILOT+'25': 'dc',
UCL_DIR_PILOT+'37': 'associatedDomain',
UCL_DIR_PILOT+'60': 'jpegPhoto',
X500ATTR+'2': 'knowledgeInformation',
X500ATTR+'4': 'sn',
X500ATTR+'5': 'serialNumber',
X500ATTR+'6': 'c',
X500ATTR+'7': 'l',
X500ATTR+'8': 'st',
X500ATTR+'9': 'street',
X500ATTR+'10': 'o',
X500ATTR+'11': 'ou',
X500ATTR+'12': 'title',
X500ATTR+'14': 'searchGuide',
X500ATTR+'15': 'businessCategory',
X500ATTR+'16': 'postalAddress',
X500ATTR+'17': 'postalCode',
X500ATTR+'18': 'postOfficeBox',
X500ATTR+'19': 'physicalDeliveryOfficeName',
X500ATTR+'20': 'telephoneNumber',
X500ATTR+'21': 'telexNumber',
X500ATTR+'22': 'teletexTerminalIdentifier',
X500ATTR+'23': 'facsimileTelephoneNumber',
X500ATTR+'24': 'x121Address',
X500ATTR+'25': 'internationaliSDNNumber',
X500ATTR+'26': 'registeredAddress',
X500ATTR+'27': 'destinationIndicator',
X500ATTR+'28': 'preferredDeliveryMethod',
X500ATTR+'29': 'presentationAddress',
X500ATTR+'30': 'supportedApplicationContext',
X500ATTR+'31': 'member',
X500ATTR+'32': 'owner',
X500ATTR+'33': 'roleOccupant',
X500ATTR+'36': 'userCertificate',
X500ATTR+'37': 'cACertificate',
X500ATTR+'38': 'authorityRevocationList',
X500ATTR+'39': 'certificateRevocationList',
X500ATTR+'40': 'crossCertificatePair',
X500ATTR+'42': 'givenName',
X500ATTR+'43': 'initials',
@@ -76,115 +85,107 @@ MAP = {
X500ATTR+'46': 'dnQualifier',
X500ATTR+'47': 'enhancedSearchGuide',
X500ATTR+'48': 'protocolInformation',
X500ATTR+'50': 'uniqueMember',
X500ATTR+'51': 'houseIdentifier',
X500ATTR+'52': 'supportedAlgorithms',
X500ATTR+'53': 'deltaRevocationList',
X500ATTR+'54': 'dmdName',
NETSCAPE_LDAP+'4': 'employeeType',
X500ATTR+'22': 'teletexTerminalIdentifier',
X500ATTR+'23': 'facsimileTelephoneNumber',
X500ATTR+'20': 'telephoneNumber',
X500ATTR+'21': 'telexNumber',
X500ATTR+'26': 'registeredAddress',
X500ATTR+'27': 'destinationIndicator',
X500ATTR+'24': 'x121Address',
X500ATTR+'25': 'internationaliSDNNumber',
X500ATTR+'28': 'preferredDeliveryMethod',
X500ATTR+'29': 'presentationAddress',
EDUPERSON_OID+'3': 'eduPersonOrgDN',
NOREDUPERSON_OID+'3': 'norEduPersonBirthDate',
X500ATTR+'65': 'pseudonym',
},
"to":{
'roleOccupant': X500ATTR+'33',
'gn': X500ATTR+'42',
'norEduPersonNIN': NOREDUPERSON_OID+'5',
'title': X500ATTR+'12',
'facsimileTelephoneNumber': X500ATTR+'23',
'mail': UCL_DIR_PILOT+'3',
'postOfficeBox': X500ATTR+'18',
'fax': X500ATTR+'23',
'telephoneNumber': X500ATTR+'20',
'norEduPersonBirthDate': NOREDUPERSON_OID+'3',
'rfc822Mailbox': UCL_DIR_PILOT+'3',
'dc': UCL_DIR_PILOT+'25',
'to': {
'associatedDomain': UCL_DIR_PILOT+'37',
'authorityRevocationList': X500ATTR+'38',
'businessCategory': X500ATTR+'15',
'c': X500ATTR+'6',
'cACertificate': X500ATTR+'37',
'carLicense': NETSCAPE_LDAP+'1',
'certificateRevocationList': X500ATTR+'39',
'countryName': X500ATTR+'6',
'crossCertificatePair': X500ATTR+'40',
'dc': UCL_DIR_PILOT+'25',
'deltaRevocationList': X500ATTR+'53',
'departmentNumber': NETSCAPE_LDAP+'2',
'destinationIndicator': X500ATTR+'27',
'displayName': NETSCAPE_LDAP+'241',
'dmdName': X500ATTR+'54',
'dnQualifier': X500ATTR+'46',
'domainComponent': UCL_DIR_PILOT+'25',
'eduPersonAffiliation': EDUPERSON_OID+'1',
'eduPersonAssurance': EDUPERSON_OID+'11',
'eduPersonEntitlement': EDUPERSON_OID+'7',
'eduPersonNickname': EDUPERSON_OID+'2',
'eduPersonOrgDN': EDUPERSON_OID+'3',
'eduPersonOrgUnitDN': EDUPERSON_OID+'4',
'eduPersonPrimaryAffiliation': EDUPERSON_OID+'5',
'eduPersonPrimaryOrgUnitDN': EDUPERSON_OID+'8',
'eduPersonPrincipalName': EDUPERSON_OID+'6',
'eduPersonScopedAffiliation': EDUPERSON_OID+'9',
'eduPersonTargetedID': EDUPERSON_OID+'10',
'email': PKCS_9+'1',
'emailAddress': PKCS_9+'1',
'employeeNumber': NETSCAPE_LDAP+'3',
'organizationName': X500ATTR+'10',
'eduPersonAssurance': EDUPERSON_OID+'11',
'norEduOrgAcronym': NOREDUPERSON_OID+'6',
'registeredAddress': X500ATTR+'26',
'physicalDeliveryOfficeName': X500ATTR+'19',
'associatedDomain': UCL_DIR_PILOT+'37',
'l': X500ATTR+'7',
'stateOrProvinceName': X500ATTR+'8',
'federationFeideSchemaVersion': NOREDUPERSON_OID+'9',
'pkcs9email': PKCS_9+'1',
'givenName': X500ATTR+'42',
'x500UniqueIdentifier': X500ATTR+'45',
'eduPersonNickname': EDUPERSON_OID+'2',
'houseIdentifier': X500ATTR+'51',
'street': X500ATTR+'9',
'supportedAlgorithms': X500ATTR+'52',
'preferredLanguage': NETSCAPE_LDAP+'39',
'postalAddress': X500ATTR+'16',
'email': PKCS_9+'1',
'norEduOrgUnitUniqueIdentifier': NOREDUPERSON_OID+'8',
'eduPersonPrimaryOrgUnitDN': EDUPERSON_OID+'8',
'c': X500ATTR+'6',
'teletexTerminalIdentifier': X500ATTR+'22',
'o': X500ATTR+'10',
'cACertificate': X500ATTR+'37',
'telexNumber': X500ATTR+'21',
'ou': X500ATTR+'11',
'initials': X500ATTR+'43',
'eduPersonOrgUnitDN': EDUPERSON_OID+'4',
'deltaRevocationList': X500ATTR+'53',
'norEduPersonLIN': NOREDUPERSON_OID+'4',
'supportedApplicationContext': X500ATTR+'30',
'eduPersonEntitlement': EDUPERSON_OID+'7',
'generationQualifier': X500ATTR+'44',
'eduPersonAffiliation': EDUPERSON_OID+'1',
'eduPersonPrincipalName': EDUPERSON_OID+'6',
'localityName': X500ATTR+'7',
'owner': X500ATTR+'32',
'norEduOrgUnitUniqueNumber': NOREDUPERSON_OID+'2',
'searchGuide': X500ATTR+'14',
'certificateRevocationList': X500ATTR+'39',
'organizationalUnitName': X500ATTR+'11',
'userCertificate': X500ATTR+'36',
'preferredDeliveryMethod': X500ATTR+'28',
'internationaliSDNNumber': X500ATTR+'25',
'uniqueMember': X500ATTR+'50',
'departmentNumber': NETSCAPE_LDAP+'2',
'enhancedSearchGuide': X500ATTR+'47',
'userPKCS12': NETSCAPE_LDAP+'216',
'eduPersonTargetedID': EDUPERSON_OID+'10',
'norEduOrgUniqueNumber': NOREDUPERSON_OID+'1',
'x121Address': X500ATTR+'24',
'destinationIndicator': X500ATTR+'27',
'eduPersonPrimaryAffiliation': EDUPERSON_OID+'5',
'surname': X500ATTR+'4',
'jpegPhoto': UCL_DIR_PILOT+'60',
'eduPersonScopedAffiliation': EDUPERSON_OID+'9',
'protocolInformation': X500ATTR+'48',
'knowledgeInformation': X500ATTR+'2',
'employeeType': NETSCAPE_LDAP+'4',
'userSMIMECertificate': NETSCAPE_LDAP+'40',
'enhancedSearchGuide': X500ATTR+'47',
'facsimileTelephoneNumber': X500ATTR+'23',
'fax': X500ATTR+'23',
'federationFeideSchemaVersion': NOREDUPERSON_OID+'9',
'generationQualifier': X500ATTR+'44',
'givenName': X500ATTR+'42',
'gn': X500ATTR+'42',
'houseIdentifier': X500ATTR+'51',
'initials': X500ATTR+'43',
'internationaliSDNNumber': X500ATTR+'25',
'jpegPhoto': UCL_DIR_PILOT+'60',
'knowledgeInformation': X500ATTR+'2',
'l': X500ATTR+'7',
'localityName': X500ATTR+'7',
'mail': UCL_DIR_PILOT+'3',
'member': X500ATTR+'31',
'streetAddress': X500ATTR+'9',
'dmdName': X500ATTR+'54',
'postalCode': X500ATTR+'17',
'pseudonym': X500ATTR+'65',
'dnQualifier': X500ATTR+'46',
'crossCertificatePair': X500ATTR+'40',
'eduPersonOrgDN': EDUPERSON_OID+'3',
'authorityRevocationList': X500ATTR+'38',
'displayName': NETSCAPE_LDAP+'241',
'businessCategory': X500ATTR+'15',
'serialNumber': X500ATTR+'5',
'norEduOrgAcronym': NOREDUPERSON_OID+'6',
'norEduOrgUniqueIdentifier': NOREDUPERSON_OID+'7',
'st': X500ATTR+'8',
'carLicense': NETSCAPE_LDAP+'1',
'norEduOrgUniqueNumber': NOREDUPERSON_OID+'1',
'norEduOrgUnitUniqueIdentifier': NOREDUPERSON_OID+'8',
'norEduOrgUnitUniqueNumber': NOREDUPERSON_OID+'2',
'norEduPersonBirthDate': NOREDUPERSON_OID+'3',
'norEduPersonLIN': NOREDUPERSON_OID+'4',
'norEduPersonNIN': NOREDUPERSON_OID+'5',
'o': X500ATTR+'10',
'organizationName': X500ATTR+'10',
'organizationalUnitName': X500ATTR+'11',
'ou': X500ATTR+'11',
'owner': X500ATTR+'32',
'physicalDeliveryOfficeName': X500ATTR+'19',
'pkcs9email': PKCS_9+'1',
'postOfficeBox': X500ATTR+'18',
'postalAddress': X500ATTR+'16',
'postalCode': X500ATTR+'17',
'preferredDeliveryMethod': X500ATTR+'28',
'preferredLanguage': NETSCAPE_LDAP+'39',
'presentationAddress': X500ATTR+'29',
'protocolInformation': X500ATTR+'48',
'pseudonym': X500ATTR+'65',
'registeredAddress': X500ATTR+'26',
'rfc822Mailbox': UCL_DIR_PILOT+'3',
'roleOccupant': X500ATTR+'33',
'searchGuide': X500ATTR+'14',
'serialNumber': X500ATTR+'5',
'sn': X500ATTR+'4',
'domainComponent': UCL_DIR_PILOT+'25',
'st': X500ATTR+'8',
'stateOrProvinceName': X500ATTR+'8',
'street': X500ATTR+'9',
'streetAddress': X500ATTR+'9',
'supportedAlgorithms': X500ATTR+'52',
'supportedApplicationContext': X500ATTR+'30',
'surname': X500ATTR+'4',
'telephoneNumber': X500ATTR+'20',
'teletexTerminalIdentifier': X500ATTR+'22',
'telexNumber': X500ATTR+'21',
'title': X500ATTR+'12',
'uniqueMember': X500ATTR+'50',
'userCertificate': X500ATTR+'36',
'userPKCS12': NETSCAPE_LDAP+'216',
'userSMIMECertificate': NETSCAPE_LDAP+'40',
'x121Address': X500ATTR+'24',
'x500UniqueIdentifier': X500ATTR+'45',
}
}
}

View File

@@ -269,7 +269,8 @@ class Config(object):
acs = ac_factory()
if not acs:
raise ConfigurationError("No attribute converters, something is wrong!!")
raise ConfigurationError(
"No attribute converters, something is wrong!!")
_acs = self.getattr("attribute_converters", typ)
if _acs:

View File

@@ -8,13 +8,15 @@ from saml2.soap import parse_soap_enveloped_saml_artifact_resolve
from saml2.soap import class_instances_from_soap_enveloped_saml_thingies
from saml2.soap import open_soap_envelope
from saml2 import samlp, SamlBase, SAMLError
from saml2 import samlp
from saml2 import SamlBase
from saml2 import SAMLError
from saml2 import saml
from saml2 import response
from saml2 import response as saml_response
from saml2 import BINDING_URI
from saml2 import BINDING_HTTP_ARTIFACT
from saml2 import BINDING_PAOS
from saml2 import request
from saml2 import request as saml_request
from saml2 import soap
from saml2 import element_to_extension_element
from saml2 import extension_elements_to_elements
@@ -296,7 +298,16 @@ class Entity(HTTPBase):
return info
def unravel(self, txt, binding, msgtype="response"):
@staticmethod
def unravel(txt, binding, msgtype="response"):
"""
Will unpack the received text. Depending on the context the original
response may have been transformed before transmission.
:param txt:
:param binding:
:param msgtype:
:return:
"""
#logger.debug("unravel '%s'" % txt)
if binding not in [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST,
BINDING_SOAP, BINDING_URI, BINDING_HTTP_ARTIFACT,
@@ -309,7 +320,8 @@ class Entity(HTTPBase):
elif binding == BINDING_HTTP_POST:
xmlstr = base64.b64decode(txt)
elif binding == BINDING_SOAP:
func = getattr(soap, "parse_soap_enveloped_saml_%s" % msgtype)
func = getattr(soap,
"parse_soap_enveloped_saml_%s" % msgtype)
xmlstr = func(txt)
elif binding == BINDING_HTTP_ARTIFACT:
xmlstr = base64.b64decode(txt)
@@ -320,7 +332,8 @@ class Entity(HTTPBase):
return xmlstr
def parse_soap_message(self, text):
@staticmethod
def parse_soap_message(text):
"""
:param text: The SOAP message
@@ -330,7 +343,8 @@ class Entity(HTTPBase):
ecp,
samlp])
def unpack_soap_message(self, text):
@staticmethod
def unpack_soap_message(text):
"""
Picks out the parts of the SOAP message, body and headers apart
:param text: The SOAP message
@@ -438,7 +452,8 @@ class Entity(HTTPBase):
msg.extension_elements = extensions
def _response(self, in_response_to, consumer_url=None, status=None,
issuer=None, sign=False, to_sign=None, encrypt_assertion=False, encrypt_cert=None, **kwargs):
issuer=None, sign=False, to_sign=None,
encrypt_assertion=False, encrypt_cert=None, **kwargs):
""" Create a Response.
:param in_response_to: The session identifier of the request
@@ -471,10 +486,13 @@ class Entity(HTTPBase):
if encrypt_assertion:
sign_class = [(class_name(response), response.id)]
if sign:
response.signature = pre_signature_part(response.id, self.sec.my_cert, 1)
response.signature = pre_signature_part(response.id,
self.sec.my_cert, 1)
cbxs = CryptoBackendXmlSec1(self.config.xmlsec_binary)
_, cert_file = make_temp("%s" % encrypt_cert, decode=False)
response = cbxs.encrypt_assertion(response, cert_file, pre_encryption_part())#template(response.assertion.id))
response = cbxs.encrypt_assertion(response, cert_file,
pre_encryption_part())
# template(response.assertion.id))
if sign:
return signed_instance_factory(response, self.sec, sign_class)
else:
@@ -520,13 +538,14 @@ class Entity(HTTPBase):
# ------------------------------------------------------------------------
def srv2typ(self, service):
@staticmethod
def srv2typ(service):
for typ in ["aa", "pdp", "aq"]:
if service in ENDPOINTS[typ]:
if typ == "aa":
return "attribute_authority"
elif typ == "aq":
return "authn_authority"
return "authn_authority"
else:
return typ
@@ -570,12 +589,14 @@ class Entity(HTTPBase):
origdoc = xmlstr
xmlstr = self.unravel(xmlstr, binding, request_cls.msgtype)
must = self.config.getattr("want_authn_requests_signed", "idp")
only_valid_cert = self.config.getattr("want_authn_requests_only_with_valid_cert", "idp")
only_valid_cert = self.config.getattr(
"want_authn_requests_only_with_valid_cert", "idp")
if only_valid_cert is None:
only_valid_cert = False
if only_valid_cert:
must = True
_request = _request.loads(xmlstr, binding, origdoc=origdoc, must=must, only_valid_cert=only_valid_cert)
_request = _request.loads(xmlstr, binding, origdoc=origdoc, must=must,
only_valid_cert=only_valid_cert)
_log_debug("Loaded request")
@@ -674,14 +695,14 @@ class Entity(HTTPBase):
return response
def create_artifact_resolve(self, artifact, destination, sid, consent=None,
extensions=None, sign=False):
def create_artifact_resolve(self, artifact, destination, sessid,
consent=None, extensions=None, sign=False):
"""
Create a ArtifactResolve request
:param artifact:
:param destination:
:param sid: session id
:param sessid: session id
:param consent:
:param extensions:
:param sign:
@@ -690,7 +711,7 @@ class Entity(HTTPBase):
artifact = Artifact(text=artifact)
return self._message(ArtifactResolve, destination, sid,
return self._message(ArtifactResolve, destination, sessid,
consent, extensions, sign, artifact=artifact)
def create_artifact_response(self, request, artifact, bindings=None,
@@ -763,7 +784,7 @@ class Entity(HTTPBase):
was not.
"""
return self._parse_request(xmlstr, request.ManageNameIDRequest,
return self._parse_request(xmlstr, saml_request.ManageNameIDRequest,
"manage_name_id_service", binding)
def create_manage_name_id_response(self, request, bindings=None,
@@ -781,17 +802,21 @@ class Entity(HTTPBase):
def parse_manage_name_id_request_response(self, string,
binding=BINDING_SOAP):
return self._parse_response(string, response.ManageNameIDResponse,
"manage_name_id_service", binding)
return self._parse_response(string, saml_response.ManageNameIDResponse,
"manage_name_id_service", binding,
asynchop=False)
# ------------------------------------------------------------------------
def _parse_response(self, xmlstr, response_cls, service, binding, outstanding_certs=None, **kwargs):
def _parse_response(self, xmlstr, response_cls, service, binding,
outstanding_certs=None, **kwargs):
""" Deal with a Response
:param xmlstr: The response as a xml string
:param response_cls: What type of response it is
:param binding: What type of binding this message came through.
:param outstanding_certs: Certificates that belongs to me that the
IdP may have used to encrypt a response/assertion/..
:param kwargs: Extra key word arguments
:return: None if the reply doesn't contain a valid SAML Response,
otherwise the response.
@@ -800,20 +825,20 @@ class Entity(HTTPBase):
response = None
if self.config.accepted_time_diff:
kwargs["timeslack"] = self.config.accepted_time_diff
timeslack = self.config.accepted_time_diff
if "asynchop" not in kwargs:
if binding in [BINDING_SOAP, BINDING_PAOS]:
kwargs["asynchop"] = False
asynchop = False
else:
kwargs["asynchop"] = True
asynchop = True
if xmlstr:
if "return_addrs" not in kwargs:
if binding in [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST]:
try:
# expected return address
kwargs["return_addrs"] = self.config.endpoint(
return_addrs = self.config.endpoint(
service, binding=binding)
except Exception:
logger.info("Not supposed to handle this!")
@@ -827,12 +852,6 @@ class Entity(HTTPBase):
xmlstr = self.unravel(xmlstr, binding, response_cls.msgtype)
origxml = xmlstr
if outstanding_certs is not None:
_response = samlp.any_response_from_string(xmlstr)
if len(_response.encrypted_assertion) > 0:
_, cert_file = make_temp("%s" % outstanding_certs[_response.in_response_to]["key"], decode=False)
cbxs = CryptoBackendXmlSec1(self.config.xmlsec_binary)
xmlstr = cbxs.decrypt(xmlstr, cert_file)
if not xmlstr: # Not a valid reponse
return None
@@ -851,16 +870,14 @@ class Entity(HTTPBase):
logger.debug("XMLSTR: %s" % xmlstr)
if hasattr(response.response, 'encrypted_assertion'):
for encrypted_assertion in response.response.encrypted_assertion:
if encrypted_assertion.extension_elements is not None:
assertion_list = extension_elements_to_elements(encrypted_assertion.extension_elements, [saml])
for assertion in assertion_list:
_assertion = saml.assertion_from_string(str(assertion))
response.response.assertion.append(_assertion)
if response:
response = response.verify()
if outstanding_certs:
_, key_file = make_temp(
"%s" % outstanding_certs[
response.in_response_to]["key"], decode=False)
else:
key_file = ""
response = response.verify(key_file)
if not response:
return None
@@ -887,7 +904,7 @@ class Entity(HTTPBase):
was not.
"""
return self._parse_request(xmlstr, request.LogoutRequest,
return self._parse_request(xmlstr, saml_request.LogoutRequest,
"single_logout_service", binding)
def use_artifact(self, message, endpoint_index=0):
@@ -964,7 +981,7 @@ class Entity(HTTPBase):
kwargs = {"entity_id": self.config.entityid,
"attribute_converters": self.config.attribute_converters}
resp = self._parse_response(xmlstr, response.ArtifactResponse,
resp = self._parse_response(xmlstr, saml_response.ArtifactResponse,
"artifact_resolve", BINDING_SOAP,
**kwargs)
# should just be one

View File

@@ -775,37 +775,58 @@ class MetadataStore(object):
return [m["text"] for m in ad["affiliate_member"]]
def entity_categories(self, entity_id):
ent = self.__getitem__(entity_id)
res = []
try:
ext = ent["extensions"]
except KeyError:
pass
else:
for elem in ext["extension_elements"]:
if elem["__class__"] == ENTITYATTRIBUTES:
for attr in elem["attribute"]:
if attr["name"] == ENTITY_CATEGORY:
res.extend([v["text"] for v in
attr["attribute_value"]])
"""
Get a list of entity categories for an entity id.
return res
:param entity_id: Entity id
:return: Entity categories
:type entity_id: string
:rtype: [string]
"""
attributes = self.entity_attributes(entity_id)
return attributes.get(ENTITY_CATEGORY, [])
def supported_entity_categories(self, entity_id):
ent = self.__getitem__(entity_id)
res = []
try:
ext = ent["extensions"]
except KeyError:
pass
else:
for elem in ext["extension_elements"]:
if elem["__class__"] == ENTITYATTRIBUTES:
for attr in elem["attribute"]:
if attr["name"] == ENTITY_CATEGORY_SUPPORT:
res.extend([v["text"] for v in
attr["attribute_value"]])
"""
Get a list of entity category support for an entity id.
:param entity_id: Entity id
:return: Entity category support
:type entity_id: string
:rtype: [string]
"""
attributes = self.entity_attributes(entity_id)
return attributes.get(ENTITY_CATEGORY_SUPPORT, [])
def entity_attributes(self, entity_id):
"""
Get all entity attributes for an entry in the metadata.
Example return data:
{'http://macedir.org/entity-category': ['something', 'something2'],
'http://example.org/saml-foo': ['bar']}
:param entity_id: Entity id
:return: dict with keys and value-lists from metadata
:type entity_id: string
:rtype: dict
"""
res = {}
try:
ext = self.__getitem__(entity_id)["extensions"]
except KeyError:
return res
for elem in ext["extension_elements"]:
if elem["__class__"] == ENTITYATTRIBUTES:
for attr in elem["attribute"]:
if attr["name"] not in res:
res[attr["name"]] = []
res[attr["name"]] += [v["text"] for v in attr[
"attribute_value"]]
return res
def bindings(self, entity_id, typ, service):

View File

@@ -42,8 +42,8 @@ import xmldsig as ds
import xmlenc as xenc
from saml2 import samlp
from saml2 import class_name
from saml2 import saml
from saml2 import extension_element_to_element
from saml2 import extension_elements_to_elements
from saml2 import SAMLError
from saml2 import time_util
@@ -387,7 +387,7 @@ class StatusResponse(object):
if self.asynchop:
if self.response.destination and \
self.response.destination not in self.return_addrs:
self.response.destination not in self.return_addrs:
logger.error("%s not in %s" % (self.response.destination,
self.return_addrs))
return None
@@ -399,7 +399,7 @@ class StatusResponse(object):
def loads(self, xmldata, decode=True, origxml=None):
return self._loads(xmldata, decode, origxml)
def verify(self):
def verify(self, key_file=""):
try:
return self._verify()
except AssertionError:
@@ -473,6 +473,7 @@ class AuthnResponse(StatusResponse):
self.came_from = ""
self.ava = None
self.assertion = None
self.assertions = []
self.session_not_on_or_after = 0
self.allow_unsolicited = allow_unsolicited
self.require_signature = want_assertions_signed
@@ -739,8 +740,13 @@ class AuthnResponse(StatusResponse):
return self.name_id
def _assertion(self, assertion):
self.assertion = assertion
"""
Check the assertion
:param assertion:
:return: True/False depending on if the assertion is sane or not
"""
self.assertion = assertion
logger.debug("assertion context: %s" % (self.context,))
logger.debug("assertion keys: %s" % (assertion.keyswv()))
logger.debug("outstanding_queries: %s" % (self.outstanding_queries,))
@@ -773,54 +779,61 @@ class AuthnResponse(StatusResponse):
logger.exception("get subject")
raise
def _encrypted_assertion(self, xmlstr):
if xmlstr.encrypted_data:
assertion_str = self.sec.decrypt(xmlstr.encrypted_data.to_string())
if not assertion_str:
raise DecryptionFailed()
assertion = saml.assertion_from_string(assertion_str)
else:
decrypt_xml = self.sec.decrypt(xmlstr)
def decrypt_assertions(self, encrypted_assertions, key_file=""):
res = []
for encrypted_assertion in encrypted_assertions:
if encrypted_assertion.extension_elements:
assertions = extension_elements_to_elements(
encrypted_assertion.extension_elements, [saml, samlp])
for assertion in assertions:
if assertion.signature:
if not self.sec.verify_signature(
"%s" % assertion, key_file,
node_name=class_name(assertion)):
logger.error(
"Failed to verify signature on '%s'" % assertion)
raise SignatureError()
res.append(assertion)
return res
logger.debug("Decryption successfull")
self.response = samlp.response_from_string(decrypt_xml)
logger.debug("Parsed decrypted assertion successfull")
enc = self.response.encrypted_assertion[0].extension_elements[0]
assertion = extension_element_to_element(
enc, saml.ELEMENT_FROM_STRING, namespace=saml.NAMESPACE)
logger.debug("Decrypted Assertion: %s" % assertion)
return self._assertion(assertion)
def parse_assertion(self):
def parse_assertion(self, key_file=""):
if self.context == "AuthnQuery":
# can contain one or more assertions
pass
else: # This is a saml2int limitation
try:
assert len(self.response.assertion) == 1 or \
len(self.response.encrypted_assertion) == 1
len(self.response.encrypted_assertion) == 1
except AssertionError:
raise Exception("No assertion part")
if self.response.encrypted_assertion:
logger.debug("***Encrypted assertion/-s***")
decr_text = self.sec.decrypt(self.xmlstr)
resp = samlp.response_from_string(decr_text)
res = self.decrypt_assertions(resp.encrypted_assertion, key_file)
if self.response.assertion:
self.response.assertion.extend(res)
else:
self.response.assertion = res
self.response.encrypted_assertion = []
if self.response.assertion:
logger.debug("***Unencrypted response***")
logger.debug("***Unencrypted assertion***")
for assertion in self.response.assertion:
if not self._assertion(assertion):
return False
return True
else:
logger.debug("***Encrypted response***")
for assertion in self.response.encrypted_assertion:
if not self._encrypted_assertion(assertion):
return False
return True
else:
self.assertions.append(assertion)
self.assertion = self.assertions[0]
def verify(self):
return True
def verify(self, key_file=""):
""" Verify that the assertion is syntactically correct and
the signature is correct if present."""
the signature is correct if present.
:param key_file: If not the default key file should be used this is it.
"""
try:
self._verify()
@@ -830,7 +843,7 @@ class AuthnResponse(StatusResponse):
if not isinstance(self.response, samlp.Response):
return self
if self.parse_assertion():
if self.parse_assertion(key_file):
return self
else:
logger.error("Could not parse the assertion")
@@ -1056,7 +1069,7 @@ class AssertionIDResponse(object):
return self._postamble()
def verify(self):
def verify(self, key_file=""):
try:
valid_instance(self.response)
except NotValid, exc:

View File

@@ -246,6 +246,8 @@ class AttributeValueBase(SamlBase):
# clear type
#self.clear_type()
self.set_text(tree.text)
if XSI_NIL in self.extension_attributes:
del self.extension_attributes[XSI_NIL]
try:
typ = self.extension_attributes[XSI_TYPE]
_verify_value_type(typ, getattr(self, "text"))

View File

@@ -35,12 +35,13 @@ from Crypto.PublicKey import RSA
from saml2.cert import OpenSSLWrapper
from saml2.extension import pefim
from saml2.saml import EncryptedAssertion
from saml2.samlp import Response, response_from_string
from saml2.samlp import Response
import xmldsig as ds
import xmlenc as enc
from saml2 import samlp, SAMLError, extension_elements_to_elements
from saml2 import samlp
from saml2 import SAMLError
from saml2 import extension_elements_to_elements
from saml2 import class_name
from saml2 import saml
from saml2 import ExtensionElement
@@ -74,7 +75,8 @@ RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
RSA_1_5 = "http://www.w3.org/2001/04/xmlenc#rsa-1_5"
TRIPLE_DES_CBC = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"
XMLTAG = "<?xml version='1.0'?>"
PREFIX = "<?xml version='1.0' encoding='UTF-8'?>"
PREFIX1 = "<?xml version='1.0' encoding='UTF-8'?>"
PREFIX2 = '<?xml version="1.0" encoding="UTF-8"?>'
class SigverError(SAMLError):
@@ -125,8 +127,12 @@ def rm_xmltag(statement):
statement = statement[len(XMLTAG):]
if statement[0] == '\n':
statement = statement[1:]
elif statement.startswith(PREFIX):
statement = statement[len(PREFIX):]
elif statement.startswith(PREFIX1):
statement = statement[len(PREFIX1):]
if statement[0] == '\n':
statement = statement[1:]
elif statement.startswith(PREFIX2):
statement = statement[len(PREFIX2):]
if statement[0] == '\n':
statement = statement[1:]
@@ -693,7 +699,7 @@ class CryptoBackend():
def encrypt(self, text, recv_key, template, key_type):
raise NotImplementedError()
def encrypt_assertion(self, statement, recv_key, key_type):
def encrypt_assertion(self, statement, recv_key, key_type, xpath=""):
raise NotImplementedError()
def decrypt(self, enctext, key_file):
@@ -733,12 +739,25 @@ class CryptoBackendXmlSec1(CryptoBackend):
except IndexError:
return ""
def encrypt(self, text, recv_key, template, key_type):
def encrypt(self, text, recv_key, template, session_key_type, xpath=""):
"""
:param text: The text to be compiled
:param recv_key: Filename of a file where the key resides
:param template: Filename of a file with the pre-encryption part
:param session_key_type: Type and size of a new session key
"des-192" generates a new 192 bits DES key for DES3 encryption
:param xpath: What should be encrypted
:return:
"""
logger.debug("Encryption input len: %d" % len(text))
_, fil = make_temp("%s" % text, decode=False)
com_list = [self.xmlsec, "--encrypt", "--pubkey-cert-pem", recv_key,
"--session-key", key_type, "--xml-data", fil]
"--session-key", session_key_type, "--xml-data", fil]
if xpath:
com_list.extend(['--node-xpath', xpath])
(_stdout, _stderr, output) = self._run_xmlsec(com_list, [template],
exception=DecryptError,
@@ -767,7 +786,8 @@ class CryptoBackendXmlSec1(CryptoBackend):
"--session-key", key_type, "--xml-data", fil,
"--node-xpath", ASSERT_XPATH]
(_stdout, _stderr, output) = self._run_xmlsec(com_list, [tmpl], exception=EncryptError, validate_output=False)
(_stdout, _stderr, output) = self._run_xmlsec(
com_list, [tmpl], exception=EncryptError, validate_output=False)
os.unlink(fil)
if not output:
@@ -1206,7 +1226,7 @@ class SecurityContext(object):
:param text: Text to encrypt
:param recv_key: A file containing the receivers public key
:param template: A file containing the XML document template
:param template: A file containing the XMLSEC template
:param key_type: The type of session key to use
:result: An encrypted XML text
"""
@@ -1262,8 +1282,7 @@ class SecurityContext(object):
return self.crypto.validate_signature(signedtext, cert_file=cert_file,
cert_type=cert_type,
node_name=node_name,
node_id=node_id, id_attr=id_attr,
)
node_id=node_id, id_attr=id_attr)
def _check_signature(self, decoded_xml, item, node_name=NODE_NAME,
origdoc=None, id_attr="", must=False,
@@ -1735,7 +1754,10 @@ def pre_encrypt_assertion(response):
assertion = response.assertion
response.assertion = None
response.encrypted_assertion = EncryptedAssertion()
response.encrypted_assertion.add_extension_element(assertion)
if isinstance(assertion, list):
response.encrypted_assertion.add_extension_elements(assertion)
else:
response.encrypted_assertion.add_extension_element(assertion)
# txt = "%s" % response
# _ass = "%s" % assertion
# _ass = rm_xmltag(_ass)

View File

@@ -81,8 +81,9 @@ def parse_duration(duration):
assert duration[index] == "P"
index += 1
dic = dict([(typ, 0) for (code, typ) in D_FORMAT])
dic = dict([(typ, 0) for (code, typ) in D_FORMAT if typ])
dlen = len(duration)
for code, typ in D_FORMAT:
#print duration[index:], code
if duration[index] == '-':
@@ -94,25 +95,36 @@ def parse_duration(duration):
raise Exception("Not allowed to end with 'T'")
else:
raise Exception("Missing T")
elif duration[index] == "T":
continue
else:
try:
mod = duration[index:].index(code)
_val = duration[index:index + mod]
try:
dic[typ] = int(duration[index:index + mod])
dic[typ] = int(_val)
except ValueError:
if code == "S":
# smallest value used may also have a decimal fraction
if mod + index + 1 == dlen:
try:
dic[typ] = float(duration[index:index + mod])
dic[typ] = float(_val)
except ValueError:
raise Exception("Not a float")
if "," in _val:
_val = _val.replace(",", ".")
try:
dic[typ] = float(_val)
except ValueError:
raise Exception("Not a float")
else:
raise Exception("Not a float")
else:
raise Exception(
"Fractions not allow on anything byt seconds")
raise ValueError(
"Fraction not allowed on other than smallest value")
index = mod + index + 1
except ValueError:
dic[typ] = 0
if index == len(duration):
if index == dlen:
break
return sign, dic

View File

@@ -224,4 +224,4 @@ def test_xmlsec_cryptobackend():
if __name__ == "__main__":
test_xmlsec_cryptobackend()
test_xmlsec_cryptobackend()

View File

@@ -323,4 +323,4 @@ MAP = {
'x121Address': 'urn:mace:dir:attribute-def:x121Address',
'x500UniqueIdentifier': 'urn:mace:dir:attribute-def:x500UniqueIdentifier',
}
}
}

View File

@@ -196,4 +196,4 @@ MAP = {
'labeledURI': UMICH+'57',
'uid': UCL_DIR_PILOT+'1'
}
}
}

View File

@@ -187,4 +187,4 @@ MAP = {
'sn': X500ATTR+'4',
'domainComponent': UCL_DIR_PILOT+'25',
}
}
}

View File

@@ -25,4 +25,4 @@
<ns0:Body>
<ns3:AuthnRequest/>
</ns0:Body>
</ns0:Envelope>
</ns0:Envelope>

View File

@@ -12,4 +12,4 @@ w9NTEImw8CmUzzzmMd7TBM2epwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAJpIWdXn
FL/j9Cm/Pdn6Yoxkf1mWy8L8WSwF8j9xfkvp53/GMd9IFkgkBbZo+F9CDH2la6H3
vseA3ZJrXrxSn5RBhI5XJ85DGfdcMYJy3K42Y6mAUghVv1n+rf39w/cyuSRIW0IY
XE3ANufnryezpDUffXpzdUltuTCpu2qfKEj2
-----END CERTIFICATE-----
-----END CERTIFICATE-----

View File

@@ -19,4 +19,4 @@
<ns0:CipherData>
<ns0:CipherValue/>
</ns0:CipherData>
</ns0:EncryptedData>
</ns0:EncryptedData>

View File

@@ -187,4 +187,4 @@ class FakeIDP(Server):
else: # Just POST
response = "%s" % _resp
return DummyResponse(200, response)
return DummyResponse(200, response)

View File

@@ -1,2 +1,2 @@
curl -G -O http://md.incommon.org/InCommon/InCommon-metadata.xml
curl -G -O http://metadata.aai.switch.ch/metadata.aaitest.xml
curl -G -O http://metadata.aai.switch.ch/metadata.aaitest.xml

View File

@@ -15,4 +15,4 @@ EgYDVQQDEwtrYWxtYXIyLm9yZ4IJALIv7VqXanQYMAwGA1UdEwQFMAMBAf8wDQYJ
KoZIhvcNAQEFBQADgYEALx5V6xKtPr7urC/QOWiHxUChQO+SJsbnlwIquwaEGgUf
0WrGidPu04zdv+VpKtR+/KZbIDuSWx0/AkbexiE9ZUzJ2GvdVSxr/uON9CtQIQTp
5WjZD0KaieaoIMy/w5shc+trjkV550g/MWFFqAjproXwHRrEQoAxWL0smtR1R/I=
-----END CERTIFICATE-----
-----END CERTIFICATE-----

View File

@@ -71,4 +71,4 @@
</ns1:AttributeStatement>
</ns1:Assertion>
</ns1:EncryptedAssertion>
</ns0:Response>
</ns0:Response>

View File

@@ -17,4 +17,4 @@ vvjpFAcbu/ILfzsXk19zKInCXmFWW5UNl2iq9GRCKltMyWZ/8m74cZpm1X3x4u33
u+VTbtj/ZeTzqsriFfXo2q8EsdEK+27tvluNbdUgEayz8YIFrLI9LJRlWH6X6BWh
PbIrR0sd263vE/1fgWbeB66CRXys0oQ92k9arc+gkVB2hZ8o4BK82LkwO30Ueo+l
oHJ24vcX55P2LXmP+x+hIjMXICkqly0QE7kUVUVBisQ=
-----END CERTIFICATE-----
-----END CERTIFICATE-----

View File

@@ -2,8 +2,8 @@ from pathutils import full_path
from pathutils import xmlsec_path
CONFIG = {
"entityid" : "urn:mace:example.com:saml:roland:sp",
"name" : "urn:mace:example.com:saml:roland:sp",
"entityid": "urn:mace:example.com:saml:roland:sp",
"name": "urn:mace:example.com:saml:roland:sp",
"description": "My own SP",
"service": {
"sp": {
@@ -25,14 +25,14 @@ CONFIG = {
"local": [full_path("idp.xml"), full_path("vo_metadata.xml")],
},
"virtual_organization": {
"urn:mace:example.com:it:tek":{
"urn:mace:example.com:it:tek": {
"nameid_format": "urn:oid:1.3.6.1.4.1.1466.115.121.1.15-NameID",
"common_identifier": "umuselin",
}
},
"subject_data": "subject_data.db",
"accepted_time_diff": 60,
"attribute_map_dir" : full_path("attributemaps"),
"attribute_map_dir": full_path("attributemaps"),
"valid_for": 6,
"organization": {
"name": ("AB Exempel", "se"),
@@ -40,12 +40,13 @@ CONFIG = {
"url": "http://www.example.org",
},
"contact_person": [{
"given_name": "Roland",
"sur_name": "Hedberg",
"telephone_number": "+46 70 100 0000",
"email_address": ["tech@eample.com", "tech@example.org"],
"contact_type": "technical"
},
"given_name": "Roland",
"sur_name": "Hedberg",
"telephone_number": "+46 70 100 0000",
"email_address": ["tech@eample.com",
"tech@example.org"],
"contact_type": "technical"
},
],
"logger": {
"rotating": {

View File

@@ -101,4 +101,4 @@
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
</samlp:Response>
</samlp:Response>

View File

@@ -118659,4 +118659,4 @@
}
}
]
]
]

View File

@@ -206,4 +206,4 @@ def test_6():
assert tr.algorithm == "http://www.w3.org/TR/1999/REC-xpath-19991116"
assert len(tr.x_path) == 1
assert tr.x_path[0].text.strip() == """self::xenc:EncryptedData[@Id="example1"]"""

View File

@@ -7,38 +7,43 @@ from saml2.time_util import f_quotient, modulo, parse_duration, add_duration
from saml2.time_util import str_to_time, instant, valid, in_a_while
from saml2.time_util import before, after, not_before, not_on_or_after
def test_f_quotient():
assert f_quotient(-1,3) == -1
assert f_quotient(0,3) == 0
assert f_quotient(1,3) == 0
assert f_quotient(2,3) == 0
assert f_quotient(3,3) == 1
assert f_quotient(3.123,3) == 1
assert f_quotient(-1, 3) == -1
assert f_quotient(0, 3) == 0
assert f_quotient(1, 3) == 0
assert f_quotient(2, 3) == 0
assert f_quotient(3, 3) == 1
assert f_quotient(3.123, 3) == 1
def test_modulo():
assert modulo(-1,3) == 2
assert modulo(0,3) == 0
assert modulo(1,3) == 1
assert modulo(2,3) == 2
assert modulo(3,3) == 0
assert modulo(-1, 3) == 2
assert modulo(0, 3) == 0
assert modulo(1, 3) == 1
assert modulo(2, 3) == 2
assert modulo(3, 3) == 0
x = 3.123
assert modulo(3.123,3) == x - 3
assert modulo(3.123, 3) == x - 3
def test_f_quotient_2():
assert f_quotient(0, 1, 13) == -1
for i in range(1,13):
for i in range(1, 13):
assert f_quotient(i, 1, 13) == 0
assert f_quotient(13, 1, 13) == 1
assert f_quotient(13.123, 1, 13) == 1
def test_modulo_2():
assert modulo(0, 1, 13) == 12
for i in range(1,13):
for i in range(1, 13):
assert modulo(i, 1, 13) == i
assert modulo(13, 1, 13) == 1
#x = 0.123
#assert modulo(13+x, 1, 13) == 1+x
def test_parse_duration():
(sign, d) = parse_duration("P1Y3M5DT7H10M3.3S")
assert sign == "+"
@@ -48,7 +53,46 @@ def test_parse_duration():
assert d['tm_mday'] == 5
assert d['tm_year'] == 1
assert d['tm_min'] == 10
def test_parse_duration2():
(sign, d) = parse_duration("PT30M")
assert sign == "+"
assert d['tm_sec'] == 0
assert d['tm_mon'] == 0
assert d['tm_hour'] == 0
assert d['tm_mday'] == 0
assert d['tm_year'] == 0
assert d['tm_min'] == 30
PATTERNS = {
"P3Y6M4DT12H30M5S": {'tm_sec': 5, 'tm_hour': 12, 'tm_mday': 4,
'tm_year': 3, 'tm_mon': 6, 'tm_min': 30},
"P23DT23H": {'tm_sec': 0, 'tm_hour': 23, 'tm_mday': 23, 'tm_year': 0,
'tm_mon': 0, 'tm_min': 0},
"P4Y": {'tm_sec': 0, 'tm_hour': 0, 'tm_mday': 0, 'tm_year': 4,
'tm_mon': 0, 'tm_min': 0},
"P1M": {'tm_sec': 0, 'tm_hour': 0, 'tm_mday': 0, 'tm_year': 0,
'tm_mon': 1, 'tm_min': 0},
"PT1M": {'tm_sec': 0, 'tm_hour': 0, 'tm_mday': 0, 'tm_year': 0,
'tm_mon': 0, 'tm_min': 1},
"P0.5Y": {'tm_sec': 0, 'tm_hour': 0, 'tm_mday': 0, 'tm_year': 0.5,
'tm_mon': 0, 'tm_min': 0},
"P0,5Y": {'tm_sec': 0, 'tm_hour': 0, 'tm_mday': 0, 'tm_year': 0.5,
'tm_mon': 0, 'tm_min': 0},
"PT36H": {'tm_sec': 0, 'tm_hour': 36, 'tm_mday': 0, 'tm_year': 0,
'tm_mon': 0, 'tm_min': 0},
"P1DT12H": {'tm_sec': 0, 'tm_hour': 12, 'tm_mday': 1, 'tm_year': 0,
'tm_mon': 0, 'tm_min': 0}
}
def test_parse_duration_n():
for dur, _val in PATTERNS.items():
(sign, d) = parse_duration(dur)
assert d == _val
def test_add_duration_1():
#2000-01-12T12:13:14Z P1Y3M5DT7H10M3S 2001-04-17T19:23:17Z
t = add_duration(str_to_time("2000-01-12T12:13:14Z"), "P1Y3M5DT7H10M3S")
@@ -58,17 +102,19 @@ def test_add_duration_1():
assert t.tm_hour == 19
assert t.tm_min == 23
assert t.tm_sec == 17
def test_add_duration_2():
#2000-01-12 PT33H 2000-01-13
t = add_duration(str_to_time("2000-01-12T00:00:00Z"),"PT33H")
t = add_duration(str_to_time("2000-01-12T00:00:00Z"), "PT33H")
assert t.tm_year == 2000
assert t.tm_mon == 1
assert t.tm_mday == 14
assert t.tm_hour == 9
assert t.tm_min == 0
assert t.tm_sec == 0
def test_str_to_time():
t = calendar.timegm(str_to_time("2000-01-12T00:00:00Z"))
#TODO: Find all instances of time.mktime(.....)
@@ -78,22 +124,25 @@ def test_str_to_time():
# do this as an external method in the
assert t == 947635200
def test_instant():
inst = str_to_time(instant())
now = time.gmtime()
assert now >= inst
def test_valid():
assert valid("2000-01-12T00:00:00Z") == False
current_year = datetime.datetime.today().year
assert valid("%d-01-12T00:00:00Z" % (current_year + 1)) == True
this_instance = instant()
time.sleep(1)
assert valid(this_instance) == False # unless on a very fast machine :-)
assert valid(this_instance) == False # unless on a very fast machine :-)
soon = in_a_while(seconds=10)
assert valid(soon) == True
def test_timeout():
soon = in_a_while(seconds=1)
time.sleep(2)
@@ -122,3 +171,7 @@ def test_not_on_or_after():
current_year = datetime.datetime.today().year
assert not_on_or_after("%d-01-01T00:00:00Z" % (current_year + 1)) == True
assert not_on_or_after("%d-01-01T00:00:00Z" % (current_year - 1)) == False
if __name__ == "__main__":
test_parse_duration_n()

View File

@@ -151,7 +151,7 @@ def test_audience():
assert aud_restr.audience.text == "urn:foo:bar"
def test_conditions():
conditions = utils.factory( saml.Conditions,
conditions = utils.factory(saml.Conditions,
not_before="2009-10-30T07:58:10.852Z",
not_on_or_after="2009-10-30T08:03:10.852Z",
audience_restriction=[utils.factory(saml.AudienceRestriction,

View File

@@ -18,9 +18,11 @@ from saml2.validate import valid_anytype
from py.test import raises
def _eq(l1,l2):
def _eq(l1, l2):
return set(l1) == set(l2)
def test_duration():
assert valid_duration("P1Y2M3DT10H30M")
assert valid_duration("P1Y2M3DT10H30M1.567S")
@@ -31,41 +33,45 @@ def test_duration():
assert valid_duration("P0Y1347M")
assert valid_duration("P0Y1347M0D")
assert valid_duration("-P1347M")
assert valid_duration("P1Y2MT2.5H")
raises(NotValid, 'valid_duration("P-1347M")')
raises(NotValid, ' valid_duration("P1Y2MT")')
raises(NotValid, ' valid_duration("P1Y2MT2xH")')
raises( NotValid, 'valid_duration("P-1347M")')
raises( NotValid, ' valid_duration("P1Y2MT")')
raises( NotValid, ' valid_duration("P1Y2MT2.5H")')
raises( NotValid, ' valid_duration("P1Y2MT2xH")')
def test_unsigned_short():
assert valid_unsigned_short("1234")
raises( NotValid, ' valid_unsigned_short("-1234")')
raises( NotValid, ' valid_unsigned_short("1234567890")')
raises(NotValid, ' valid_unsigned_short("-1234")')
raises(NotValid, ' valid_unsigned_short("1234567890")')
def test_valid_non_negative_integer():
assert valid_non_negative_integer("1234567890")
raises( NotValid, 'valid_non_negative_integer("-123")')
raises( NotValid, 'valid_non_negative_integer("123.56")')
raises(NotValid, 'valid_non_negative_integer("-123")')
raises(NotValid, 'valid_non_negative_integer("123.56")')
assert valid_non_negative_integer("12345678901234567890")
def test_valid_string():
assert valid_string(u'example')
raises( NotValid, 'valid_string("02656c6c6f".decode("hex"))')
raises(NotValid, 'valid_string("02656c6c6f".decode("hex"))')
def test_valid_anyuri():
assert valid_any_uri("urn:oasis:names:tc:SAML:2.0:attrname-format:uri")
def test_valid_instance():
attr_statem = saml.AttributeStatement()
text = ["value of test attribute",
"value1 of test attribute",
"value2 of test attribute",
"value1 of test attribute2",
"value2 of test attribute2",]
"value2 of test attribute2", ]
attr_statem.attribute.append(saml.Attribute())
attr_statem.attribute.append(saml.Attribute())
@@ -80,9 +86,9 @@ def test_valid_instance():
attr_statem.attribute[1].friendly_name = text[2]
attr_statem.attribute[1].attribute_value.append(saml.AttributeValue())
attr_statem.attribute[1].attribute_value[0].text = text[2]
assert valid_instance(attr_statem)
response = samlp.Response()
response.id = "response id"
response.in_response_to = "request id"
@@ -94,8 +100,9 @@ def test_valid_instance():
response.status = samlp.Status()
response.assertion.append(saml.Assertion())
raises( MustValueError, 'valid_instance(response)')
raises(MustValueError, 'valid_instance(response)')
def test_valid_anytype():
assert valid_anytype("130.239.16.3")
assert valid_anytype("textstring")
@@ -103,5 +110,4 @@ def test_valid_anytype():
assert valid_anytype("-1234")
assert valid_anytype("P1Y2M3DT10H30M")
assert valid_anytype("urn:oasis:names:tc:SAML:2.0:attrname-format:uri")

View File

@@ -1,3 +1,4 @@
# coding=utf-8
from saml2.authn_context import pword
from saml2.mdie import to_dict
from saml2 import md, assertion
@@ -75,7 +76,6 @@ def test_filter_on_attributes_1():
assert ava.keys() == ["serialNumber"]
assert ava["serialNumber"] == ["12345"]
# ----------------------------------------------------------------------
def test_lifetime_1():
@@ -172,6 +172,8 @@ def test_ava_filter_2():
"surName": "Jeter",
"mail": "derek@example.com"}
# mail removed because it doesn't match the regular expression
# So this should fail.
raises(MissingValue, policy.filter, ava, 'urn:mace:umu.se:saml:roland:sp',
None, [mail], [gn, sn])
@@ -183,6 +185,44 @@ def test_ava_filter_2():
None, [gn, sn, mail])
def test_ava_filter_dont_fail():
conf = {
"default": {
"lifetime": {"minutes": 15},
"attribute_restrictions": None, # means all I have
},
"urn:mace:umu.se:saml:roland:sp": {
"lifetime": {"minutes": 5},
"attribute_restrictions": {
"givenName": None,
"surName": None,
"mail": [".*@.*\.umu\.se"],
},
"fail_on_missing_requested": False
}}
policy = Policy(conf)
ava = {"givenName": "Derek",
"surName": "Jeter",
"mail": "derek@example.com"}
# mail removed because it doesn't match the regular expression
# So it should fail if the 'fail_on_ ...' flag wasn't set
_ava = policy.filter(ava,'urn:mace:umu.se:saml:roland:sp', None,
[mail], [gn, sn])
assert _ava
ava = {"givenName": "Derek",
"surName": "Jeter"}
# it wasn't there to begin with
_ava = policy.filter(ava, 'urn:mace:umu.se:saml:roland:sp',
None, [gn, sn, mail])
assert _ava
def test_filter_attribute_value_assertions_0(AVA):
p = Policy({
"default": {
@@ -255,6 +295,7 @@ def test_filter_attribute_value_assertions_2(AVA):
assert _eq(ava.keys(), ["givenName"])
assert ava["givenName"] == ["Roland"]
# ----------------------------------------------------------------------------
@@ -290,7 +331,9 @@ def test_assertion_1(AVA):
def test_assertion_2():
AVA = {'mail': u'roland.hedberg@adm.umu.se',
'eduPersonTargetedID': 'http://lingon.ladok.umu.se:8090/idp!http://lingon.ladok.umu.se:8088/sp!95e9ae91dbe62d35198fbbd5e1fb0976',
'eduPersonTargetedID': 'http://lingon.ladok.umu'
'.se:8090/idp!http://lingon.ladok.umu'
'.se:8088/sp!95e9ae91dbe62d35198fbbd5e1fb0976',
'displayName': u'Roland Hedberg',
'uid': 'http://roland.hedberg.myopenid.com/'}
@@ -453,6 +496,7 @@ def test_filter_values_req_opt_2():
raises(MissingValue, "filter_on_attributes(ava, r, o)")
# ---------------------------------------------------------------------------
@@ -485,6 +529,7 @@ def test_filter_values_req_opt_4():
assert _eq(ava.keys(), ['givenName', 'sn'])
assert ava == {'givenName': ['Roland'], 'sn': ['Hedberg']}
# ---------------------------------------------------------------------------
@@ -706,7 +751,7 @@ ACD = pword.AuthenticationContextDeclaration(authn_method=authn_method)
def test_assertion_with_noop_attribute_conv():
ava = {"urn:oid:2.5.4.4": "Roland", "urn:oid:2.5.4.42": "Hedberg" }
ava = {"urn:oid:2.5.4.4": "Roland", "urn:oid:2.5.4.42": "Hedberg"}
ast = Assertion(ava)
policy = Policy({
"default": {
@@ -719,7 +764,7 @@ def test_assertion_with_noop_attribute_conv():
issuer = Issuer(text="entityid", format=NAMEID_FORMAT_ENTITY)
msg = ast.construct("sp_entity_id", "in_response_to", "consumer_url",
name_id, [AttributeConverterNOOP(NAME_FORMAT_URI)],
policy, issuer=issuer, authn_decl=ACD ,
policy, issuer=issuer, authn_decl=ACD,
authn_auth="authn_authn")
print msg
@@ -767,7 +812,7 @@ def test_assertion_with_zero_attributes():
issuer = Issuer(text="entityid", format=NAMEID_FORMAT_ENTITY)
msg = ast.construct("sp_entity_id", "in_response_to", "consumer_url",
name_id, [AttributeConverterNOOP(NAME_FORMAT_URI)],
policy, issuer=issuer, authn_decl=ACD ,
policy, issuer=issuer, authn_decl=ACD,
authn_auth="authn_authn")
print msg
@@ -797,4 +842,4 @@ def test_assertion_with_authn_instant():
if __name__ == "__main__":
test_assertion_2()
test_ava_filter_dont_fail()

View File

@@ -368,4 +368,4 @@ def test_assertion_consumer_service():
"location"] == 'https://www.zimride.com/Shibboleth.sso/SAML2/POST'
if __name__ == "__main__":
test_1()
test_1()

View File

@@ -121,5 +121,4 @@ class TestClass:
(ava, inactive) = self.cache.get_identity(nid[2])
assert inactive == ["bcde"]
assert ava == {}

View File

@@ -172,4 +172,4 @@ class TestPopulationMemoryBased():
info = self.population.get_info_from(nid, IDP_OTHER)
assert info.keys() == ["not_on_or_after", "name_id", "ava"]
assert info["name_id"] == nid
assert info["ava"] == {"eduPersonEntitlement": "Anka"}
assert info["ava"] == {"eduPersonEntitlement": "Anka"}

View File

@@ -164,4 +164,4 @@ def test_idp_policy_filter():
assert ava.keys() == ["eduPersonTargetedID"] # because no entity category
if __name__ == "__main__":
test_idp_policy_filter()
test_idp_policy_filter()

View File

@@ -1,11 +1,12 @@
#!/usr/bin/env python
import base64
from saml2.sigver import pre_encryption_part, make_temp
from saml2.mdstore import MetadataStore
from saml2.saml import assertion_from_string
from saml2.saml import assertion_from_string, EncryptedAssertion
from saml2.samlp import response_from_string
from saml2 import sigver
from saml2 import sigver, extension_elements_to_elements
from saml2 import class_name
from saml2 import time_util
from saml2 import saml, samlp
@@ -25,9 +26,10 @@ PUB_KEY = full_path("test.pem")
PRIV_KEY = full_path("test.key")
def _eq(l1,l2):
def _eq(l1, l2):
return set(l1) == set(l2)
CERT1 = """MIICsDCCAhmgAwIBAgIJAJrzqSSwmDY9MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMDkxMDA2MTk0OTQxWhcNMDkxMTA1MTk0OTQxWjBF
@@ -60,7 +62,7 @@ wKe3ACFXBvqGQN0IbcH49hu0FKhYFM/GPDJcIHFBsiyMBXChpye9vBaTNEBCtU3K
jjyG0hRT2mAQ9h+bkPmOvlEo/aH0xR68Z9hw4PF13w=="""
from pyasn1.codec.der import decoder
def test_cert_from_instance_1():
xml_response = open(SIGNED).read()
@@ -104,7 +106,6 @@ class FakeConfig():
class TestSecurity():
def setup_class(self):
# This would be one way to initialize the security context :
#
@@ -126,8 +127,8 @@ class TestSecurity():
issue_instant="2009-10-30T13:20:28Z",
signature=sigver.pre_signature_part("11111", self.sec.my_cert, 1),
attribute_statement=do_attribute_statement({
("", "", "surName"): ("Foo", ""),
("", "", "givenName"): ("Bar", ""),
("", "", "surName"): ("Foo", ""),
("", "", "givenName"): ("Bar", ""),
})
)
@@ -144,7 +145,7 @@ class TestSecurity():
def test_non_verify_2(self):
xml_response = open(FALSE_SIGNED).read()
raises(sigver.SignatureError,self.sec.correctly_signed_response,
raises(sigver.SignatureError, self.sec.correctly_signed_response,
xml_response)
def test_sign_assertion(self):
@@ -154,8 +155,8 @@ class TestSecurity():
#print sign_ass
sass = saml.assertion_from_string(sign_ass)
#print sass
assert _eq(sass.keyswv(), ['attribute_statement', 'issue_instant',
'version', 'signature', 'id'])
assert _eq(sass.keyswv(), ['attribute_statement', 'issue_instant',
'version', 'signature', 'id'])
assert sass.version == "2.0"
assert sass.id == "11111"
assert time_util.str_to_time(sass.issue_instant)
@@ -227,19 +228,21 @@ class TestSecurity():
def test_sign_response(self):
response = factory(samlp.Response,
assertion=self._assertion,
id="22222",
signature=sigver.pre_signature_part("22222", self.sec.my_cert))
assertion=self._assertion,
id="22222",
signature=sigver.pre_signature_part("22222",
self.sec
.my_cert))
to_sign = [(class_name(self._assertion), self._assertion.id),
(class_name(response), response.id)]
(class_name(response), response.id)]
s_response = sigver.signed_instance_factory(response, self.sec, to_sign)
assert s_response is not None
print s_response
response = response_from_string(s_response)
sass = response.assertion[0]
print sass
assert _eq(sass.keyswv(), ['attribute_statement', 'issue_instant',
'version', 'signature', 'id'])
@@ -252,23 +255,27 @@ class TestSecurity():
assert item.id == "22222"
def test_sign_response_2(self):
assertion2 = factory( saml.Assertion,
version= "2.0",
id= "11122",
issue_instant= "2009-10-30T13:20:28Z",
signature= sigver.pre_signature_part("11122", self.sec.my_cert),
attribute_statement=do_attribute_statement({
("","","surName"): ("Fox",""),
("","","givenName") :("Bear",""),
})
)
assertion2 = factory(saml.Assertion,
version="2.0",
id="11122",
issue_instant="2009-10-30T13:20:28Z",
signature=sigver.pre_signature_part("11122",
self.sec
.my_cert),
attribute_statement=do_attribute_statement({
("", "", "surName"): ("Fox", ""),
("", "", "givenName"): ("Bear", ""),
})
)
response = factory(samlp.Response,
assertion=assertion2,
id="22233",
signature=sigver.pre_signature_part("22233", self.sec.my_cert))
assertion=assertion2,
id="22233",
signature=sigver.pre_signature_part("22233",
self.sec
.my_cert))
to_sign = [(class_name(assertion2), assertion2.id),
(class_name(response), response.id)]
(class_name(response), response.id)]
s_response = sigver.signed_instance_factory(response, self.sec, to_sign)
@@ -277,7 +284,7 @@ class TestSecurity():
sass = response2.assertion[0]
assert _eq(sass.keyswv(), ['attribute_statement', 'issue_instant',
'version', 'signature', 'id'])
'version', 'signature', 'id'])
assert sass.version == "2.0"
assert sass.id == "11122"
@@ -285,105 +292,115 @@ class TestSecurity():
s_response)
assert isinstance(item, samlp.Response)
def test_sign_verify(self):
def test_sign_verify(self):
response = factory(samlp.Response,
assertion=self._assertion,
id="22233",
signature=sigver.pre_signature_part("22233", self.sec.my_cert))
assertion=self._assertion,
id="22233",
signature=sigver.pre_signature_part("22233",
self.sec
.my_cert))
to_sign = [(class_name(self._assertion), self._assertion.id),
(class_name(response), response.id)]
(class_name(response), response.id)]
s_response = sigver.signed_instance_factory(response, self.sec, to_sign)
print s_response
res = self.sec.verify_signature("%s" % s_response,
node_name=class_name(samlp.Response()))
res = self.sec.verify_signature("%s" % s_response,
node_name=class_name(samlp.Response()))
print res
print res
assert res
def test_sign_verify_with_cert_from_instance(self):
response = factory(samlp.Response,
assertion=self._assertion,
id="22222",
signature=sigver.pre_signature_part("22222", self.sec.my_cert))
assertion=self._assertion,
id="22222",
signature=sigver.pre_signature_part("22222",
self.sec
.my_cert))
to_sign = [(class_name(self._assertion), self._assertion.id),
(class_name(response), response.id)]
(class_name(response), response.id)]
s_response = sigver.signed_instance_factory(response, self.sec, to_sign)
response2 = response_from_string(s_response)
ci = "".join(sigver.cert_from_instance(response2)[0].split())
assert ci == self.sec.my_cert
res = self.sec.verify_signature("%s" % s_response,
node_name=class_name(samlp.Response()))
res = self.sec.verify_signature("%s" % s_response,
node_name=class_name(samlp.Response()))
assert res
res = self.sec._check_signature(s_response, response2,
class_name(response2), s_response)
assert res == response2
def test_sign_verify_assertion_with_cert_from_instance(self):
assertion = factory( saml.Assertion,
version= "2.0",
id= "11100",
issue_instant= "2009-10-30T13:20:28Z",
signature= sigver.pre_signature_part("11100", self.sec.my_cert),
attribute_statement=do_attribute_statement({
("","","surName"): ("Fox",""),
("","","givenName") :("Bear",""),
})
)
assertion = factory(saml.Assertion,
version="2.0",
id="11100",
issue_instant="2009-10-30T13:20:28Z",
signature=sigver.pre_signature_part("11100",
self.sec
.my_cert),
attribute_statement=do_attribute_statement({
("", "", "surName"): ("Fox", ""),
("", "", "givenName"): ("Bear", ""),
})
)
to_sign = [(class_name(assertion), assertion.id)]
s_assertion = sigver.signed_instance_factory(assertion, self.sec, to_sign)
s_assertion = sigver.signed_instance_factory(assertion, self.sec,
to_sign)
print s_assertion
ass = assertion_from_string(s_assertion)
ci = "".join(sigver.cert_from_instance(ass)[0].split())
assert ci == self.sec.my_cert
res = self.sec.verify_signature("%s" % s_assertion,
node_name=class_name(ass))
assert res
assert res
res = self.sec._check_signature(s_assertion, ass, class_name(ass))
assert res
def test_exception_sign_verify_with_cert_from_instance(self):
assertion = factory( saml.Assertion,
version= "2.0",
id= "11100",
issue_instant= "2009-10-30T13:20:28Z",
#signature= sigver.pre_signature_part("11100", self.sec.my_cert),
attribute_statement=do_attribute_statement({
("","","surName"): ("Foo",""),
("","","givenName") :("Bar",""),
})
)
assertion = factory(saml.Assertion,
version="2.0",
id="11100",
issue_instant="2009-10-30T13:20:28Z",
#signature= sigver.pre_signature_part("11100",
# self.sec.my_cert),
attribute_statement=do_attribute_statement({
("", "", "surName"): ("Foo", ""),
("", "", "givenName"): ("Bar", ""),
})
)
response = factory(samlp.Response,
assertion=assertion,
id="22222",
signature=sigver.pre_signature_part("22222", self.sec.my_cert))
assertion=assertion,
id="22222",
signature=sigver.pre_signature_part("22222",
self.sec
.my_cert))
to_sign = [(class_name(response), response.id)]
s_response = sigver.signed_instance_factory(response, self.sec, to_sign)
response2 = response_from_string(s_response)
# Change something that should make everything fail
response2.id = "23456"
raises(sigver.SignatureError, self.sec._check_signature,
s_response, response2, class_name(response2))
s_response, response2, class_name(response2))
class TestSecurityMetadata():
@@ -397,36 +414,71 @@ class TestSecurityMetadata():
conf.only_use_keys_in_metadata = False
self.sec = sigver.security_context(conf)
self._assertion = factory( saml.Assertion,
version="2.0",
id="11111",
issue_instant="2009-10-30T13:20:28Z",
signature=sigver.pre_signature_part("11111", self.sec.my_cert, 1),
attribute_statement=do_attribute_statement({
("","","surName"): ("Foo",""),
("","","givenName") :("Bar",""),
})
assertion = factory(
saml.Assertion, version="2.0", id="11111",
issue_instant="2009-10-30T13:20:28Z",
signature=sigver.pre_signature_part("11111", self.sec.my_cert, 1),
attribute_statement=do_attribute_statement(
{("", "", "surName"): ("Foo", ""),
("", "", "givenName"): ("Bar", ""), })
)
def test_sign_assertion(self):
ass = self._assertion
print ass
sign_ass = self.sec.sign_assertion("%s" % ass, node_id=ass.id)
#print sign_ass
sass = saml.assertion_from_string(sign_ass)
#print sass
assert _eq(sass.keyswv(), ['attribute_statement', 'issue_instant',
'version', 'signature', 'id'])
assert sass.version == "2.0"
assert sass.id == "11111"
assert time_util.str_to_time(sass.issue_instant)
print "Crypto version : %s" % (self.sec.crypto.version())
def test_xbox():
conf = config.SPConfig()
conf.load_file("server_conf")
md = MetadataStore([saml, samlp], None, conf)
md.load("local", full_path("idp_example.xml"))
item = self.sec.check_signature(sass, class_name(sass), sign_ass)
conf.metadata = md
conf.only_use_keys_in_metadata = False
sec = sigver.security_context(conf)
assertion = factory(
saml.Assertion, version="2.0", id="11111",
issue_instant="2009-10-30T13:20:28Z",
signature=sigver.pre_signature_part("11111", sec.my_cert, 1),
attribute_statement=do_attribute_statement(
{("", "", "surName"): ("Foo", ""),
("", "", "givenName"): ("Bar", ""), })
)
sigass = sec.sign_statement(assertion, class_name(assertion),
key_file="pki/mykey.pem", node_id=assertion.id)
_ass0 = saml.assertion_from_string(sigass)
encrypted_assertion = EncryptedAssertion()
encrypted_assertion.add_extension_element(_ass0)
_, pre = make_temp("%s" % pre_encryption_part(), decode=False)
enctext = sec.crypto.encrypt(
"%s" % encrypted_assertion, conf.cert_file, pre, "des-192",
'/*[local-name()="EncryptedAssertion"]/*[local-name()="Assertion"]')
decr_text = sec.decrypt(enctext)
_seass = saml.encrypted_assertion_from_string(decr_text)
assertions = []
assers = extension_elements_to_elements(_seass.extension_elements,
[saml, samlp])
sign_cert_file = "pki/mycert.pem"
for ass in assers:
_ass = "%s" % ass
#_ass = _ass.replace('xsi:nil="true" ', '')
#assert sigass == _ass
_txt = sec.verify_signature(_ass, sign_cert_file,
node_name=class_name(assertion))
if _txt:
assertions.append(ass)
print assertions
assert isinstance(item, saml.Assertion)
if __name__ == "__main__":
t = TestSecurity()
t.setup_class()
#t = TestSecurity()
#t.setup_class()
#t.test_sign_then_encrypt_assertion()
test_xbox()

View File

@@ -1,7 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from saml2 import saml
from saml2 import config
from saml2.authn_context import INTERNETPROTOCOLPASSWORD
@@ -9,7 +8,8 @@ from saml2.server import Server
from saml2.response import response_factory
from saml2.response import StatusResponse
from saml2.response import AuthnResponse
from saml2.sigver import security_context, MissingKey
from saml2.sigver import security_context
from saml2.sigver import MissingKey
from pytest import raises
@@ -26,7 +26,6 @@ IDENTITY = {"eduPersonAffiliation": ["staff", "member"],
"mail": ["foo@gmail.com"],
"title": ["shortstop"]}
AUTHN = {
"class_ref": INTERNETPROTOCOLPASSWORD,
"authn_auth": "http://www.example.com/login"
@@ -39,29 +38,28 @@ class TestResponse:
name_id = server.ident.transient_nameid(
"urn:mace:example.com:saml:roland:sp", "id12")
self._resp_ = server.create_authn_response(IDENTITY,
"id12", # in_response_to
"http://lingon.catalogix.se:8087/",
# consumer_url
"urn:mace:example"
".com:saml:roland:sp",
# sp_entity_id
name_id=name_id)
self._resp_ = server.create_authn_response(
IDENTITY,
"id12", # in_response_to
"http://lingon.catalogix.se:8087/",
# consumer_url
"urn:mace:example.com:saml:roland:sp",
# sp_entity_id
name_id=name_id)
self._sign_resp_ = server.create_authn_response(
IDENTITY,
"id12", # in_response_to
"http://lingon.catalogix.se:8087/", # consumer_url
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
"http://lingon.catalogix.se:8087/", # consumer_url
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
name_id=name_id,
sign_assertion=True)
self._resp_authn = server.create_authn_response(
IDENTITY,
"id12", # in_response_to
"http://lingon.catalogix.se:8087/", # consumer_url
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
"id12", # in_response_to
"http://lingon.catalogix.se:8087/", # consumer_url
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
name_id=name_id,
authn=AUTHN)
@@ -72,7 +70,8 @@ class TestResponse:
def test_1(self):
xml_response = ("%s" % (self._resp_,))
resp = response_factory(xml_response, self.conf,
return_addrs=["http://lingon.catalogix.se:8087/"],
return_addrs=[
"http://lingon.catalogix.se:8087/"],
outstanding_queries={
"id12": "http://localhost:8088/sso"},
timeslack=10000, decode=False)
@@ -83,7 +82,8 @@ class TestResponse:
def test_2(self):
xml_response = self._sign_resp_
resp = response_factory(xml_response, self.conf,
return_addrs=["http://lingon.catalogix.se:8087/"],
return_addrs=[
"http://lingon.catalogix.se:8087/"],
outstanding_queries={
"id12": "http://localhost:8088/sso"},
timeslack=10000, decode=False)
@@ -92,16 +92,7 @@ class TestResponse:
assert isinstance(resp, AuthnResponse)
def test_only_use_keys_in_metadata(self):
conf = config.SPConfig()
conf.load_file("sp_2_conf")
sc = security_context(conf)
# should fail
raises(MissingKey,
'sc.correctly_signed_response("%s" % self._sign_resp_)')
if __name__ == "__main__":
t = TestResponse()
t.setup_class()
t.test_1()
t.test_1()

View File

@@ -4,6 +4,7 @@ from saml2.sigver import pre_encryption_part, ASSERT_XPATH, EncryptError
from saml2.sigver import CryptoBackendXmlSec1
from saml2.sigver import pre_encrypt_assertion
from pathutils import xmlsec_path
from pathutils import full_path
__author__ = 'roland'
@@ -65,7 +66,7 @@ def test_enc1():
# data_file.close()
key_type = "des-192"
com_list = [xmlsec_path, "encrypt", "--pubkey-cert-pem", "pubkey.pem",
com_list = [xmlsec_path, "encrypt", "--pubkey-cert-pem", full_path("pubkey.pem"),
"--session-key", key_type, "--xml-data", data,
"--node-xpath", ASSERT_XPATH]
@@ -89,11 +90,11 @@ def test_enc2():
IDENTITY, "id12", "http://lingon.catalogix.se:8087/",
"urn:mace:example.com:saml:roland:sp", name_id=name_id)
enc_resp = crypto.encrypt_assertion(resp_, "pubkey.pem",
enc_resp = crypto.encrypt_assertion(resp_, full_path("pubkey.pem"),
pre_encryption_part())
print enc_resp
assert enc_resp
if __name__ == "__main__":
test_enc1()
test_enc1()

View File

@@ -2,18 +2,25 @@
# -*- coding: utf-8 -*-
import base64
from urlparse import parse_qs
from saml2.sigver import pre_encryption_part
from saml2.assertion import Policy
from saml2.authn_context import INTERNETPROTOCOLPASSWORD
from saml2.saml import NameID, NAMEID_FORMAT_TRANSIENT
from saml2.samlp import response_from_string
from saml2.server import Server
from saml2 import samlp, saml, client, config
from saml2 import samlp
from saml2 import saml
from saml2 import client
from saml2 import config
from saml2 import class_name
from saml2 import extension_elements_to_elements
from saml2 import s_utils
from saml2 import sigver
from saml2 import time_util
from saml2.s_utils import OtherError
from saml2.s_utils import do_attribute_statement, factory
from saml2.s_utils import do_attribute_statement
from saml2.s_utils import factory
from saml2.soap import make_soap_enveloped_saml_thingy
from saml2 import BINDING_HTTP_POST
from saml2 import BINDING_HTTP_REDIRECT
@@ -182,7 +189,8 @@ class TestServer1():
name_id_policy = resp_args["name_id_policy"]
assert _eq(name_id_policy.keyswv(), ["format", "allow_create"])
assert name_id_policy.format == saml.NAMEID_FORMAT_TRANSIENT
assert resp_args["sp_entity_id"] == "urn:mace:example.com:saml:roland:sp"
assert resp_args[
"sp_entity_id"] == "urn:mace:example.com:saml:roland:sp"
def test_sso_response_with_identity(self):
name_id = self.server.ident.transient_nameid(
@@ -195,8 +203,8 @@ class TestServer1():
"mail": "derek.jeter@nyy.mlb.com",
"title": "The man"
},
"id12", # in_response_to
"http://localhost:8087/", # destination
"id12", # in_response_to
"http://localhost:8087/", # destination
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
name_id=name_id,
authn=AUTHN
@@ -227,7 +235,8 @@ class TestServer1():
break
assert len(attr.attribute_value) == 1
assert attr.name == "urn:oid:1.3.6.1.4.1.5923.1.1.1.7"
assert attr.name_format == "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
assert attr.name_format == "urn:oasis:names:tc:SAML:2" \
".0:attrname-format:uri"
value = attr.attribute_value[0]
assert value.text.strip() == "Short stop"
assert value.get_type() == "xs:string"
@@ -242,13 +251,13 @@ class TestServer1():
def test_sso_response_without_identity(self):
resp = self.server.create_authn_response(
{},
"id12", # in_response_to
"http://localhost:8087/", # consumer_url
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
userid="USER1",
authn=AUTHN,
release_policy=Policy(),
best_effort=True
"id12", # in_response_to
"http://localhost:8087/", # consumer_url
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
userid="USER1",
authn=AUTHN,
release_policy=Policy(),
best_effort=True
)
print resp.keyswv()
@@ -268,12 +277,12 @@ class TestServer1():
resp = self.server.create_authn_response(
{},
"id12", # in_response_to
"http://localhost:8087/", # consumer_url
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
userid="USER1",
authn=_authn,
best_effort=True
"id12", # in_response_to
"http://localhost:8087/", # consumer_url
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
userid="USER1",
authn=_authn,
best_effort=True
)
print resp.keyswv()
@@ -297,11 +306,11 @@ class TestServer1():
print resp.status
assert resp.status.status_code.value == samlp.STATUS_RESPONDER
assert resp.status.status_code.status_code.value == \
samlp.STATUS_REQUEST_UNSUPPORTED
samlp.STATUS_REQUEST_UNSUPPORTED
assert resp.status.status_message.text == \
"eduPersonAffiliation missing"
"eduPersonAffiliation missing"
assert resp.issuer.text == "urn:mace:example.com:saml:roland:idp"
assert not resp.assertion
assert not resp.assertion
def test_authn_response_0(self):
self.server = Server("idp_conf")
@@ -346,8 +355,8 @@ class TestServer1():
signed_resp = self.server.create_authn_response(
ava,
"id12", # in_response_to
"http://lingon.catalogix.se:8087/", # consumer_url
"id12", # in_response_to
"http://lingon.catalogix.se:8087/", # consumer_url
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
name_id=name_id,
sign_assertion=True
@@ -480,7 +489,6 @@ def _logout_request(conf_file):
class TestServerLogout():
def test_1(self):
server = Server("idp_slo_redirect_conf")
req_id, request = _logout_request("sp_slo_redirect_conf")
@@ -502,4 +510,3 @@ class TestServerLogout():
if __name__ == "__main__":
ts = TestServer1()
ts.setup_class()
ts.test_sso_response_specific_instant()

View File

@@ -4,21 +4,32 @@
import base64
import urllib
import urlparse
from saml2.authn_context import INTERNETPROTOCOLPASSWORD
from saml2.response import LogoutResponse
from saml2 import BINDING_HTTP_POST
from saml2 import BINDING_HTTP_REDIRECT
from saml2 import config
from saml2 import class_name
from saml2 import extension_elements_to_elements
from saml2 import saml
from saml2 import samlp
from saml2 import sigver
from saml2 import s_utils
from saml2.assertion import Assertion
from saml2.authn_context import INTERNETPROTOCOLPASSWORD
from saml2.client import Saml2Client
from saml2 import samlp, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT
from saml2 import saml, config, class_name
from saml2.config import SPConfig
from saml2.response import LogoutResponse
from saml2.saml import NAMEID_FORMAT_PERSISTENT
from saml2.saml import NAMEID_FORMAT_TRANSIENT
from saml2.saml import NameID
from saml2.server import Server
from saml2.sigver import pre_encryption_part
from saml2.s_utils import do_attribute_statement
from saml2.s_utils import factory
from saml2.time_util import in_a_while
from py.test import raises
from fakeIDP import FakeIDP, unpack_form
from fakeIDP import FakeIDP
from fakeIDP import unpack_form
AUTHN = {
@@ -341,8 +352,123 @@ class TestClient:
print my_name
assert my_name == "urn:mace:example.com:saml:roland:sp"
# Below can only be done with dummy Server
def test_sign_then_encrypt_assertion(self):
# Begin with the IdPs side
_sec = self.server.sec
assertion = s_utils.assertion_factory(
subject=factory(saml.Subject, text="_aaa",
name_id=factory(
saml.NameID,
format=saml.NAMEID_FORMAT_TRANSIENT)),
attribute_statement=do_attribute_statement(
{
("", "", "surName"): ("Jeter", ""),
("", "", "givenName"): ("Derek", ""),
}
),
issuer=self.server._issuer(),
)
assertion.signature = sigver.pre_signature_part(
assertion.id, _sec.my_cert, 1)
sigass = _sec.sign_statement(assertion, class_name(assertion),
key_file="pki/mykey.pem",
node_id=assertion.id)
# Create an Assertion instance from the signed assertion
_ass = saml.assertion_from_string(sigass)
response = sigver.response_factory(
in_response_to="_012345",
destination="https:#www.example.com",
status=s_utils.success_status_factory(),
issuer=self.server._issuer(),
assertion=_ass
)
enctext = _sec.crypto.encrypt_assertion(response, _sec.cert_file,
pre_encryption_part())
seresp = samlp.response_from_string(enctext)
# Now over to the client side
_csec = self.client.sec
if seresp.encrypted_assertion:
decr_text = _csec.decrypt(enctext)
seresp = samlp.response_from_string(decr_text)
resp_ass = []
sign_cert_file = "pki/mycert.pem"
for enc_ass in seresp.encrypted_assertion:
assers = extension_elements_to_elements(
enc_ass.extension_elements, [saml, samlp])
for ass in assers:
if ass.signature:
if not _csec.verify_signature("%s" % ass,
sign_cert_file,
node_name=class_name(ass)):
continue
resp_ass.append(ass)
seresp.assertion = resp_ass
seresp.encrypted_assertion = None
#print _sresp
assert seresp.assertion
def test_sign_then_encrypt_assertion2(self):
# Begin with the IdPs side
_sec = self.server.sec
nameid_policy = samlp.NameIDPolicy(allow_create="false",
format=saml.NAMEID_FORMAT_PERSISTENT)
asser = Assertion({"givenName": "Derek", "surName": "Jeter"})
assertion = asser.construct(
self.client.config.entityid, "_012345",
"http://lingon.catalogix.se:8087/",
factory(saml.NameID, format=saml.NAMEID_FORMAT_TRANSIENT),
policy=self.server.config.getattr("policy", "idp"),
issuer=self.server._issuer(),
attrconvs=self.server.config.attribute_converters,
authn_class=INTERNETPROTOCOLPASSWORD,
authn_auth="http://www.example.com/login")
assertion.signature = sigver.pre_signature_part(
assertion.id, _sec.my_cert, 1)
sigass = _sec.sign_statement(assertion, class_name(assertion),
#key_file="pki/mykey.pem",
key_file="test.key",
node_id=assertion.id)
# Create an Assertion instance from the signed assertion
_ass = saml.assertion_from_string(sigass)
response = sigver.response_factory(
in_response_to="_012345",
destination="https://www.example.com",
status=s_utils.success_status_factory(),
issuer=self.server._issuer(),
assertion=_ass
)
enctext = _sec.crypto.encrypt_assertion(response, _sec.cert_file,
pre_encryption_part())
#seresp = samlp.response_from_string(enctext)
resp_str = base64.encodestring(enctext)
# Now over to the client side
resp = self.client.parse_authn_request_response(
resp_str, BINDING_HTTP_POST,
{"_012345": "http://foo.example.com/service"})
#assert resp.encrypted_assertion == []
assert resp.assertion
assert resp.ava == {'givenName': ['Derek'], 'sn': ['Jeter']}
# Below can only be done with dummy Server
IDP = "urn:mace:example.com:saml:roland:idp"
@@ -448,4 +574,4 @@ class TestClientWithDummy():
if __name__ == "__main__":
tc = TestClient()
tc.setup_class()
tc.test_sign_auth_request_0()
tc.test_sign_then_encrypt_assertion2()

View File

@@ -73,6 +73,7 @@ class TestSP():
'sn': ['Jeter'],
'title': ['The man']}
if __name__ == "__main__":
_sp = TestSP()
_sp.setup_class()

View File

@@ -139,4 +139,4 @@ def test_flow():
assert final.response.id == p_res.id
if __name__ == "__main__":
test_flow()
test_flow()

View File

@@ -70,4 +70,4 @@ def test_request_response():
r_name_id = _resp.response.name_id
assert r_name_id.format == NAMEID_FORMAT_PERSISTENT
assert r_name_id.text == "foobar"
assert r_name_id.text == "foobar"

View File

@@ -6,6 +6,7 @@ from saml2.server import Server
__author__ = 'rolandh'
def test_basic():
sp = Saml2Client(config_file="servera_conf")
idp = Server(config_file="idp_all_conf")
@@ -18,7 +19,7 @@ def test_basic():
newid = NewID(text="Barfoo")
mid, mreq = sp.create_manage_name_id_request(destination, name_id=nameid,
new_id=newid)
new_id=newid)
print mreq
rargs = sp.apply_binding(binding, "%s" % mreq, destination, "")
@@ -31,6 +32,7 @@ def test_basic():
assert mid == _req.message.id
def test_flow():
sp = Saml2Client(config_file="servera_conf")
idp = Server(config_file="idp_all_conf")
@@ -42,7 +44,7 @@ def test_flow():
newid = NewID(text="Barfoo")
mid, midq = sp.create_manage_name_id_request(destination, name_id=nameid,
new_id=newid)
new_id=newid)
print midq
rargs = sp.apply_binding(binding, "%s" % midq, destination, "")
@@ -67,8 +69,13 @@ def test_flow():
# ---------- @SP ---------------
_response = sp.parse_manage_name_id_request_response(respargs["data"], binding)
_response = sp.parse_manage_name_id_request_response(respargs["data"],
binding)
print _response.response
assert _response.response.id == mnir.id
assert _response.response.id == mnir.id
if __name__ == "__main__":
test_flow()

View File

@@ -108,4 +108,4 @@ def test_basic_flow():
final = sp.parse_assertion_id_request_response(xmlstr, binding)
print final.response
assert isinstance(final.response, Assertion)
assert isinstance(final.response, Assertion)

View File

@@ -70,4 +70,4 @@ def test_construct_deconstruct_response():
if __name__ == "__main__":
test_construct_deconstruct_response()
test_construct_deconstruct_response()

View File

@@ -38,4 +38,4 @@ def test_eptid_shelve():
if __name__ == "__main__":
test_eptid_shelve()
test_eptid_shelve()

View File

@@ -149,4 +149,4 @@ def test_authn_3():
if __name__ == "__main__":
test_authn_3()
test_authn_3()

View File

@@ -8,7 +8,6 @@ from saml2.cert import OpenSSLWrapper
class TestGenerateCertificates(unittest.TestCase):
def test_validate_with_root_cert(self):
cert_info_ca = {
@@ -33,23 +32,35 @@ class TestGenerateCertificates(unittest.TestCase):
ca_cert, ca_key = osw.create_certificate(cert_info_ca, request=False,
write_to_file=True,
cert_dir=os.path.dirname(os.path.abspath(__file__)) + "/pki")
cert_dir=os.path.dirname(
os.path.abspath(
__file__)) + "/pki")
req_cert_str, req_key_str = osw.create_certificate(cert_info, request=True)
req_cert_str, req_key_str = osw.create_certificate(cert_info,
request=True)
ca_cert_str = osw.read_str_from_file(ca_cert)
ca_key_str = osw.read_str_from_file(ca_key)
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str, req_cert_str)
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str,
req_cert_str)
valid, mess = osw.verify(ca_cert_str, cert_str)
self.assertTrue(valid)
false_ca_cert, false_ca_key = osw.create_certificate(cert_info_ca, request=False, write_to_file=False)
false_req_cert_str_1, false_req_key_str_1 = osw.create_certificate(cert_info_ca, request=True)
false_cert_str_1 = osw.create_cert_signed_certificate(false_ca_cert, false_ca_key, false_req_cert_str_1)
false_req_cert_str_2, false_req_key_str_2 = osw.create_certificate(cert_info, request=True)
false_cert_str_2 = osw.create_cert_signed_certificate(false_ca_cert, false_ca_key, false_req_cert_str_2)
false_ca_cert, false_ca_key = osw.create_certificate(cert_info_ca,
request=False,
write_to_file=False)
false_req_cert_str_1, false_req_key_str_1 = osw.create_certificate(
cert_info_ca, request=True)
false_cert_str_1 = osw.create_cert_signed_certificate(false_ca_cert,
false_ca_key,
false_req_cert_str_1)
false_req_cert_str_2, false_req_key_str_2 = osw.create_certificate(
cert_info, request=True)
false_cert_str_2 = osw.create_cert_signed_certificate(false_ca_cert,
false_ca_key,
false_req_cert_str_2)
valid, mess = osw.verify(false_ca_cert, cert_str)
self.assertFalse(valid)
@@ -106,20 +117,28 @@ class TestGenerateCertificates(unittest.TestCase):
osw = OpenSSLWrapper()
ca_cert_str, ca_key_str = osw.create_certificate(cert_info_ca, request=False)
ca_cert_str, ca_key_str = osw.create_certificate(cert_info_ca,
request=False)
req_cert_str, intermediate_1_key_str = osw.create_certificate(cert_intermediate_1_info, request=True)
intermediate_cert_1_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str, req_cert_str)
req_cert_str, intermediate_1_key_str = osw.create_certificate(
cert_intermediate_1_info, request=True)
intermediate_cert_1_str = osw.create_cert_signed_certificate(
ca_cert_str, ca_key_str, req_cert_str)
req_cert_str, intermediate_2_key_str = osw.create_certificate(cert_intermediate_2_info, request=True)
intermediate_cert_2_str = osw.create_cert_signed_certificate(intermediate_cert_1_str, intermediate_1_key_str,
req_cert_str)
req_cert_str, intermediate_2_key_str = osw.create_certificate(
cert_intermediate_2_info, request=True)
intermediate_cert_2_str = osw.create_cert_signed_certificate(
intermediate_cert_1_str, intermediate_1_key_str,
req_cert_str)
req_cert_str, client_key_str = osw.create_certificate(cert_client_cert_info, request=True)
client_cert_str = osw.create_cert_signed_certificate(intermediate_cert_2_str, intermediate_2_key_str,
req_cert_str)
req_cert_str, client_key_str = osw.create_certificate(
cert_client_cert_info, request=True)
client_cert_str = osw.create_cert_signed_certificate(
intermediate_cert_2_str, intermediate_2_key_str,
req_cert_str)
cert_chain = [intermediate_cert_2_str, intermediate_cert_1_str, ca_cert_str]
cert_chain = [intermediate_cert_2_str, intermediate_cert_1_str,
ca_cert_str]
valid, mess = osw.verify_chain(cert_chain, client_cert_str)
self.assertTrue(valid)
@@ -145,21 +164,23 @@ class TestGenerateCertificates(unittest.TestCase):
"organization_unit": "asdfg"
}
osw = OpenSSLWrapper()
ca_cert_str, ca_key_str = osw.create_certificate(cert_info_ca, request=False,
cipher_passphrase=
{"cipher": "blowfish", "passphrase": "qwerty"})
ca_cert_str, ca_key_str = osw.create_certificate(
cert_info_ca, request=False,
cipher_passphrase={"cipher": "blowfish", "passphrase": "qwerty"})
req_cert_str, req_key_str = osw.create_certificate(cert_info, request=True)
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str, req_cert_str,
passphrase="qwerty")
req_cert_str, req_key_str = osw.create_certificate(cert_info,
request=True)
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str,
req_cert_str,
passphrase="qwerty")
valid = False
try:
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str, req_cert_str,
passphrase="qwertyqwerty")
cert_str = osw.create_cert_signed_certificate(
ca_cert_str, ca_key_str, req_cert_str,
passphrase="qwertyqwerty")
except Exception:
valid = True
@@ -185,39 +206,59 @@ class TestGenerateCertificates(unittest.TestCase):
"organization_unit": "asdfg"
}
osw = OpenSSLWrapper()
ca_cert_str, ca_key_str = osw.create_certificate(cert_info_ca, request=False)
ca_cert_str, ca_key_str = osw.create_certificate(cert_info_ca,
request=False)
req_cert_str, req_key_str = osw.create_certificate(cert_info, request=True)
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str, req_cert_str)
req_cert_str, req_key_str = osw.create_certificate(cert_info,
request=True)
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str,
req_cert_str)
valid, mess = osw.verify(ca_cert_str, cert_str)
ca_cert_str, ca_key_str = osw.create_certificate(cert_info_ca, request=False, valid_from=1000, valid_to=100000)
req_cert_str, req_key_str = osw.create_certificate(cert_info, request=True)
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str, req_cert_str)
ca_cert_str, ca_key_str = osw.create_certificate(cert_info_ca,
request=False,
valid_from=1000,
valid_to=100000)
req_cert_str, req_key_str = osw.create_certificate(cert_info,
request=True)
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str,
req_cert_str)
valid, mess = osw.verify(ca_cert_str, cert_str)
self.assertFalse(valid)
ca_cert_str, ca_key_str = osw.create_certificate(cert_info_ca, request=False)
req_cert_str, req_key_str = osw.create_certificate(cert_info, request=True)
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str, req_cert_str, valid_from=1000,
ca_cert_str, ca_key_str = osw.create_certificate(cert_info_ca,
request=False)
req_cert_str, req_key_str = osw.create_certificate(cert_info,
request=True)
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str,
req_cert_str,
valid_from=1000,
valid_to=100000)
valid, mess = osw.verify(ca_cert_str, cert_str)
self.assertFalse(valid)
ca_cert_str, ca_key_str = osw.create_certificate(cert_info_ca, request=False, valid_from=0, valid_to=1)
req_cert_str, req_key_str = osw.create_certificate(cert_info, request=True)
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str, req_cert_str)
ca_cert_str, ca_key_str = osw.create_certificate(cert_info_ca,
request=False,
valid_from=0,
valid_to=1)
req_cert_str, req_key_str = osw.create_certificate(cert_info,
request=True)
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str,
req_cert_str)
time.sleep(2)
valid, mess = osw.verify(ca_cert_str, cert_str)
self.assertFalse(valid)
ca_cert_str, ca_key_str = osw.create_certificate(cert_info_ca, request=False)
req_cert_str, req_key_str = osw.create_certificate(cert_info, request=True)
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str, req_cert_str, valid_from=0, valid_to=1)
ca_cert_str, ca_key_str = osw.create_certificate(cert_info_ca,
request=False)
req_cert_str, req_key_str = osw.create_certificate(cert_info,
request=True)
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str,
req_cert_str,
valid_from=0, valid_to=1)
time.sleep(2)
valid, mess = osw.verify(ca_cert_str, cert_str)
self.assertFalse(valid)
self.assertFalse(valid)

View File

@@ -9,6 +9,7 @@ from saml2.extension.pefim import SPCertEnc
from saml2.samlp import Extensions
from saml2.samlp import authn_request_from_string
from saml2.sigver import read_cert_from_file
from pathutils import full_path
__author__ = 'roland'
@@ -17,7 +18,7 @@ conf.load_file("server_conf")
client = Saml2Client(conf)
# place a certificate in an authn request
cert = read_cert_from_file("test.pem", "pem")
cert = read_cert_from_file(full_path("test.pem"), "pem")
spcertenc = SPCertEnc(
x509_data=ds.X509Data(
@@ -48,4 +49,4 @@ _elem = extension_elements_to_elements(parsed.extensions.extension_elements,
assert len(_elem) == 1
_spcertenc = _elem[0]
_cert = _spcertenc.x509_data[0].x509_certificate.text
assert cert == _cert
assert cert == _cert

View File

@@ -87,7 +87,7 @@ def base_init(imports):
line.append("%s%s=%s," % (indent4, _name, _name))
line.append("%s)" % indent4)
else:
# TODO have to keep apart which properties comes from which superior
# TODO have to keep apart which properties come from which superior
for sup, elems in imports.items():
line.append("%s%s.__init__(self, " % (INDENT+INDENT, sup))
lattr = elems[:]
@@ -2187,5 +2187,3 @@ def main(argv):
if __name__ == "__main__":
main(sys.argv[1:])

140
tools/sync_attrmaps.py Executable file
View File

@@ -0,0 +1,140 @@
#!/usr/bin/env python
from importlib import import_module
import sys
import os
__author__ = 'roland'
def load(head, tail):
if head == "":
if sys.path[0] != ".":
sys.path.insert(0, ".")
else:
sys.path.insert(0, head)
if tail.endswith(".py"):
tail = tail[:-3]
return import_module(tail)
def intcmp(s1, s2):
try:
_i1 = int(s1)
_i2 = int(s2)
except ValueError:
_i1 = s1
_i2 = s2
if _i1 < _i2:
return -1
if _i1 > _i2:
return 1
else:
return 0
class AMap(object):
def __init__(self, head, tail, indent=4 * " "):
self.mod = load(head, tail)
self.variable = {}
self.vars = []
self.text = []
self.indent = indent
for key, val in self.mod.__dict__.items():
if key.startswith("__"):
continue
elif key == "MAP":
continue
else:
self.variable[key] = val
self.vars.append(key)
self.vars.sort()
def sync(self):
for key, val in self.mod.MAP["fro"].items():
try:
assert self.mod.MAP["to"][val] == key
except KeyError: # missing value
print "# Added %s=%s" % (self.mod.MAP["to"][val], key)
self.mod.MAP["to"][val] = key
except AssertionError:
raise Exception("Mismatch key:%s '%s' != '%s'" % (
key, val, self.mod.MAP["to"][val]))
for val in self.mod.MAP["to"].values():
if val not in self.mod.MAP["fro"]:
print "# Missing URN '%s'" % val
def do_fro(self):
txt = ["%s'fro': {" % self.indent]
i2 = self.indent + self.indent
_fro = self.mod.MAP["fro"]
for var in self.vars:
_v = self.variable[var]
li = [k[len(_v):] for k in _fro.keys() if k.startswith(_v)]
li.sort(intcmp)
for item in li:
txt.append("%s%s+'%s': '%s'," % (i2, var, item,
_fro[_v + item]))
txt.append('%s},' % self.indent)
return txt
def do_to(self):
txt = ["%s'to': {" % self.indent]
i2 = self.indent + self.indent
_to = self.mod.MAP["to"]
_keys = _to.keys()
_keys.sort()
invmap = dict([(v, k) for k, v in self.variable.items()])
for key in _keys:
val = _to[key]
for _urn, _name in invmap.items():
if val.startswith(_urn):
txt.append("%s'%s': %s+'%s'," % (i2, key, _name,
val[len(_urn):]))
txt.append('%s}' % self.indent)
return txt
def __str__(self):
self.sync()
text = []
for key in self.vars:
text.append("%s = '%s'" % (key, self.variable[key]))
text.extend(["", ""])
text.append("MAP = {")
text.append("%s'identifier': '%s'," % (self.indent,
self.mod.MAP["identifier"]))
text.extend(self.do_fro())
text.extend(self.do_to())
text.append("}")
text.append("")
return "\n".join(text)
if __name__ == "__main__":
_name = sys.argv[1]
if os.path.isfile(_name):
directory, fname = os.path.split(_name)
amap = AMap(directory, fname, 4 * " ")
f = open(_name)
f.write("%s" % amap)
f.close()
elif os.path.isdir(_name):
for fname in os.listdir(_name):
if fname == "__init__.py":
continue
elif fname.endswith(".pyc"):
continue
print 10 * "=" + fname + 10 * "="
amap = AMap(_name, fname, 4 * " ")
f = open(fname, "w")
f.write("%s" % amap)
f.close()