Merge remote-tracking branch 'upstream/master'
This commit is contained in:
4
TODO
4
TODO
@@ -1,3 +1,3 @@
|
|||||||
1. Write documentations.
|
1. Write documentation.
|
||||||
2. Write unittests for signature related utility methods.
|
2. Write unit tests for signature related utility methods.
|
||||||
3. Complete saml2 message class.
|
3. Complete saml2 message class.
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ instance, the friendly name is used as the key.
|
|||||||
Setup
|
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:
|
the necessary files:
|
||||||
|
|
||||||
application.py
|
application.py
|
||||||
@@ -64,7 +64,7 @@ it line by line::
|
|||||||
|
|
||||||
"service": ["sp"],
|
"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
|
supply. It is used to check for the
|
||||||
completeness of the configuration and also when constructing metadata from
|
completeness of the configuration and also when constructing metadata from
|
||||||
the configuration. More about that later. Allowed values are: "sp"
|
the configuration. More about that later. Allowed values are: "sp"
|
||||||
@@ -119,13 +119,13 @@ building metadata. ::
|
|||||||
#telephone_number
|
#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.
|
metadata.
|
||||||
|
|
||||||
So, now to that part. In order to allow the IdP to talk to you you may have
|
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.
|
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
|
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.
|
by running the make_metadata script you can find in the tools directory.
|
||||||
|
|
||||||
Change directory to where you have the configuration file and do ::
|
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
|
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.
|
to get a good introduction to the concept.
|
||||||
|
|
||||||
The configuration of the pysaml2 part in the applications middleware are
|
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
|
The application
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Is as said before extremly simple. The only thing that is connected to
|
The app is, as said before, extremely simple. The only thing that is connected to
|
||||||
the PySaml2 configuration are at the bottom, namely where the server are.
|
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
|
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
|
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
|
switched to using the LDAP or CAS plugins nothing would change in the
|
||||||
application. In the application configuration yes! But not in the application.
|
application. In the application configuration yes! But not in the application.
|
||||||
And that is really how it should be done.
|
And that is really how it should be done.
|
||||||
|
|
||||||
There is one assumption and that is that the middleware plugin that gathers
|
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
|
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"
|
"user" property in the dictionary found under the key "repoze.who.identity"
|
||||||
in the environment.
|
in the environment.
|
||||||
|
|||||||
@@ -3,15 +3,15 @@
|
|||||||
Configuration of pySAML2 entities
|
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
|
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.
|
file is the same regardless of which type of service you plan to run.
|
||||||
What differs is some of the directives.
|
What differs are some of the directives.
|
||||||
Below you will find a list of all the used directives in alphabetic order.
|
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
|
The configuration is written as a python module which contains a named
|
||||||
dictionary ("CONFIG") that contains the configuration directives.
|
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
|
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.
|
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
|
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
|
software allowes you to only specify one of them and it will
|
||||||
automatically create the other.
|
automatically create the other.
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ contact_person
|
|||||||
|
|
||||||
This is only used by *make_metadata.py* when it constructs the metadata for
|
This is only used by *make_metadata.py* when it constructs the metadata for
|
||||||
the service described by the configuration file.
|
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
|
about the service or if support is needed. The possible types are according to
|
||||||
the standard **technical**, **support**, **administrative**, **billing**
|
the standard **technical**, **support**, **administrative**, **billing**
|
||||||
and **other**.::
|
and **other**.::
|
||||||
@@ -148,7 +148,7 @@ Format::
|
|||||||
|
|
||||||
The globally unique identifier of the entity.
|
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.
|
webpage where the metadata for the entity can be found.
|
||||||
|
|
||||||
key_file
|
key_file
|
||||||
@@ -160,13 +160,13 @@ Format::
|
|||||||
|
|
||||||
*key_file* is the name of a PEM formatted file that contains the private key
|
*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
|
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
|
metadata
|
||||||
^^^^^^^^
|
^^^^^^^^
|
||||||
|
|
||||||
Contains a list of places where metadata can be found. This can be either
|
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" : {
|
"metadata" : {
|
||||||
"local": [
|
"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
|
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
|
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
|
authenticity of the file downloaded from the net, the local copy of the
|
||||||
public key should be used.
|
public key should be used.
|
||||||
This public key must be acquired by some out-of-band method.
|
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
|
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".
|
and "aa".
|
||||||
So if a server is a Service Provider (SP) then the configuration
|
So if a server is a Service Provider (SP) then the configuration
|
||||||
could look something like this::
|
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'.
|
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.
|
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
|
timeslack
|
||||||
^^^^^^^^^
|
^^^^^^^^^
|
||||||
|
|
||||||
If your computer and another computer that you are communicating with are not
|
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.
|
difference you are prepared to accept.
|
||||||
|
|
||||||
.. note:: This will indiscriminately effect all time comparisons.
|
.. 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
|
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.
|
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
|
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
|
no default and only SP entity identifiers as keys, then the server will only
|
||||||
except connections from the specified SPs.
|
except connections from the specified SPs.
|
||||||
@@ -301,12 +301,12 @@ An example might be::
|
|||||||
}
|
}
|
||||||
|
|
||||||
*lifetime*
|
*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
|
regarded as stale. In an Assertion this is represented in the NotOnOrAfter
|
||||||
attribute.
|
attribute.
|
||||||
*attribute_restrictions*
|
*attribute_restrictions*
|
||||||
By default there is no restrictions as to which attributes should be
|
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.
|
database backends will be returned if nothing else is stated.
|
||||||
In the example above the SP with the entity identifier
|
In the example above the SP with the entity identifier
|
||||||
"urn:mace:umu.se:saml:roland:sp"
|
"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
|
sp
|
||||||
^^
|
^^
|
||||||
@@ -345,7 +345,7 @@ authn_requests_signed
|
|||||||
Indicates if the Authentication Requests sent by this SP should be 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.
|
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.
|
of the metadata so the IdP will know this SP preference.
|
||||||
|
|
||||||
Valid values are "true" or "false". Default value is "false".
|
Valid values are "true" or "false". Default value is "false".
|
||||||
@@ -362,8 +362,8 @@ Example::
|
|||||||
idp
|
idp
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Defines the set of IdPs that this SP is allowed to use. If not all the IdPs in
|
Defines the set of IdPs that this SP is allowed to use; if unset, all listed
|
||||||
the metadata is allowed, then the value is expected to be a list with entity
|
IdPs may be used. If set, then the value is expected to be a list with entity
|
||||||
identifiers for the allowed IdPs.
|
identifiers for the allowed IdPs.
|
||||||
A typical configuration, when the allowed set of IdPs are limited, would look
|
A typical configuration, when the allowed set of IdPs are limited, would look
|
||||||
something like this::
|
something like this::
|
||||||
@@ -376,8 +376,6 @@ something like this::
|
|||||||
|
|
||||||
In this case the SP has only one IdP it can use.
|
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
|
optional_attributes
|
||||||
"""""""""""""""""""
|
"""""""""""""""""""
|
||||||
|
|
||||||
@@ -415,7 +413,7 @@ want_assertions_signed
|
|||||||
""""""""""""""""""""""
|
""""""""""""""""""""""
|
||||||
|
|
||||||
Indicates if this SP wants the IdP to send the assertions signed. This
|
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.
|
of the metadata so the IdP will know this SP preference.
|
||||||
|
|
||||||
Valid values are "true" or "false". Default value is "true".
|
Valid values are "true" or "false". Default value is "true".
|
||||||
@@ -440,7 +438,7 @@ endpoints
|
|||||||
"""""""""
|
"""""""""
|
||||||
|
|
||||||
Where the endpoints for the services provided are.
|
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)
|
* artifact_resolution_service (aa, idp and sp)
|
||||||
* assertion_consumer_service (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.
|
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::
|
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.
|
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
|
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
|
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.
|
and the second element is the address of the database.
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
@@ -519,7 +517,7 @@ Gives information about common identifiers for virtual_organizations::
|
|||||||
},
|
},
|
||||||
|
|
||||||
Keys in this dictionary are the identifiers for the 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
|
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.
|
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.
|
This is the typical setup for a SP.
|
||||||
A metadata file to load is *always* needed, but it can of course be
|
A metadata file to load is *always* needed, but it can of course
|
||||||
containing anything from 1 up to many entity descriptions.
|
contain anything from 1 up to many entity descriptions.
|
||||||
|
|
||||||
------
|
------
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
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
|
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
|
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
|
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
|
the AttributeQuery would be over SOAP and you would get the result over the
|
||||||
connection you have to the AttributeAuthority.
|
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.
|
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.
|
WSGI or non-WSGI environment.
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ pysaml2
|
|||||||
|
|
||||||
PySAML2 is a pure python implementation of SAML2. It contains all necessary pieces for building a SAML2 service
|
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
|
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:
|
Contents:
|
||||||
|
|
||||||
|
|||||||
@@ -53,5 +53,5 @@ The tests are based on the pypy test environment, so::
|
|||||||
py.test
|
py.test
|
||||||
|
|
||||||
is what you should use. If you don't have py.test, get it it's part of pypy!
|
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!
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +1,40 @@
|
|||||||
This is a very simple setup just to check that all your gear are in order.
|
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 setup consists of one IdP and one SP, in idp2/ and sp-wsgi/ respectively.
|
||||||
The IdP authenticates users by using a htpasswd plugin and gets the identity information
|
|
||||||
from the ini-plugin.
|
|
||||||
|
|
||||||
All this is in the idp/who.ini configuration file, the file used for authentication
|
To run the setup do:
|
||||||
is idp/passwd and the ini file is idp/idp_user.ini.
|
|
||||||
|
|
||||||
The passwords in passwd in clear text:
|
./all.sh start
|
||||||
|
|
||||||
roland:friend
|
and then use your favourite webbrowser to look at "http://localhost:8087/"
|
||||||
ozzie:two
|
|
||||||
derek:three
|
To shut it down do:
|
||||||
ryan:four
|
|
||||||
ischiro:five
|
./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.
|
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 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.
|
|
||||||
@@ -2,19 +2,19 @@
|
|||||||
|
|
||||||
startme() {
|
startme() {
|
||||||
cd sp-wsgi
|
cd sp-wsgi
|
||||||
if [ ! -f conf.py ] ; then
|
if [ ! -f sp_conf.py ] ; then
|
||||||
cp conf.py.example conf.py
|
cp sp_conf.py.example sp_conf.py
|
||||||
fi
|
fi
|
||||||
../../tools/make_metadata.py conf > sp.xml
|
../../tools/make_metadata.py sp_conf > sp.xml
|
||||||
|
|
||||||
cd ../idp2
|
cd ../idp2
|
||||||
if [ ! -f idp_conf.py ] ; then
|
if [ ! -f idp_conf.py ] ; then
|
||||||
cp idp_conf.py.example conf.py
|
cp idp_conf.py.example idp_conf.py
|
||||||
fi
|
fi
|
||||||
../../tools/make_metadata.py idp_conf > idp.xml
|
../../tools/make_metadata.py idp_conf > idp.xml
|
||||||
|
|
||||||
cd ../sp-wsgi
|
cd ../sp-wsgi
|
||||||
./sp.py conf &
|
./sp.py sp_conf &
|
||||||
|
|
||||||
cd ../idp2
|
cd ../idp2
|
||||||
./idp.py idp_conf &
|
./idp.py idp_conf &
|
||||||
|
|||||||
@@ -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
|
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
|
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
|
||||||
|
|||||||
@@ -871,7 +871,7 @@ def application(environ, start_response):
|
|||||||
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.
|
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 environ: The HTTP application environment
|
||||||
:param start_response: The application to run when the handling of the
|
:param start_response: The application to run when the handling of the
|
||||||
@@ -976,10 +976,11 @@ if __name__ == '__main__':
|
|||||||
module_directory=_rot + 'modules',
|
module_directory=_rot + 'modules',
|
||||||
input_encoding='utf-8', output_encoding='utf-8')
|
input_encoding='utf-8', output_encoding='utf-8')
|
||||||
|
|
||||||
|
HOST = '127.0.0.1'
|
||||||
PORT = 8088
|
PORT = 8088
|
||||||
|
|
||||||
SRV = make_server('', PORT, application)
|
SRV = make_server(HOST, PORT, application)
|
||||||
print "IdP listening on port: %s" % PORT
|
print "IdP listening on %s:%s" % (HOST, PORT)
|
||||||
SRV.serve_forever()
|
SRV.serve_forever()
|
||||||
else:
|
else:
|
||||||
_rot = args.mako_root
|
_rot = args.mako_root
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ CONFIG = {
|
|||||||
"email_address": "support@example.com"
|
"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
|
# the identifier returned to a SP
|
||||||
"xmlsec_binary": xmlsec_path,
|
"xmlsec_binary": xmlsec_path,
|
||||||
#"attribute_map_dir": "../attributemaps",
|
#"attribute_map_dir": "../attributemaps",
|
||||||
|
|||||||
@@ -869,10 +869,10 @@ def application(environ, start_response):
|
|||||||
"""
|
"""
|
||||||
The main WSGI application. Dispatch the current request to
|
The main WSGI application. Dispatch the current request to
|
||||||
the functions from above and store the regular expression
|
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.
|
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 environ: The HTTP application environment
|
||||||
:param start_response: The application to run when the handling of the
|
:param start_response: The application to run when the handling of the
|
||||||
@@ -977,10 +977,11 @@ if __name__ == '__main__':
|
|||||||
module_directory=_rot + 'modules',
|
module_directory=_rot + 'modules',
|
||||||
input_encoding='utf-8', output_encoding='utf-8')
|
input_encoding='utf-8', output_encoding='utf-8')
|
||||||
|
|
||||||
|
HOST = '127.0.0.1'
|
||||||
PORT = 8088
|
PORT = 8088
|
||||||
|
|
||||||
SRV = make_server('', PORT, application)
|
SRV = make_server(HOST, PORT, application)
|
||||||
print "IdP listening on port: %s" % PORT
|
print "IdP listening on %s:%s" % (HOST, PORT)
|
||||||
SRV.serve_forever()
|
SRV.serve_forever()
|
||||||
else:
|
else:
|
||||||
_rot = args.mako_root
|
_rot = args.mako_root
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ CONFIG = {
|
|||||||
"email_address": "support@example.com"
|
"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
|
# the identifier returned to a SP
|
||||||
"xmlsec_binary": xmlsec_path,
|
"xmlsec_binary": xmlsec_path,
|
||||||
#"attribute_map_dir": "../attributemaps",
|
#"attribute_map_dir": "../attributemaps",
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ def whoami(environ, start_response, user):
|
|||||||
if not nameid:
|
if not nameid:
|
||||||
return not_authn(environ, start_response)
|
return not_authn(environ, start_response)
|
||||||
if ava:
|
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))
|
response.extend(dict_to_table(ava))
|
||||||
else:
|
else:
|
||||||
response = [
|
response = [
|
||||||
@@ -222,10 +222,10 @@ def application(environ, start_response):
|
|||||||
"""
|
"""
|
||||||
The main WSGI application. Dispatch the current request to
|
The main WSGI application. Dispatch the current request to
|
||||||
the functions from above and store the regular expression
|
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.
|
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 environ: The HTTP application environment
|
||||||
:param start_response: The application to run when the handling of the
|
: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")
|
log_file="repoze_who.log")
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
HOST = '127.0.0.1'
|
||||||
PORT = 8087
|
PORT = 8087
|
||||||
|
|
||||||
# allow uwsgi or gunicorn mount
|
# allow uwsgi or gunicorn mount
|
||||||
@@ -291,6 +292,6 @@ if __name__ == '__main__':
|
|||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
from wsgiref.simple_server import make_server
|
from wsgiref.simple_server import make_server
|
||||||
srv = make_server('', PORT, app_with_auth)
|
srv = make_server(HOST, PORT, app_with_auth)
|
||||||
print "SP listening on port: %s" % PORT
|
print "SP listening on %s:%s" % (HOST, PORT)
|
||||||
srv.serve_forever()
|
srv.serve_forever()
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
from saml2.assertion import Policy
|
from saml2.assertion import Policy
|
||||||
|
|
||||||
|
HOST = '127.0.0.1'
|
||||||
PORT = 8087
|
PORT = 8087
|
||||||
HTTPS = False
|
HTTPS = False
|
||||||
|
|
||||||
@@ -11,6 +12,6 @@ POLICY = Policy(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# HTTPS cert information
|
# HTTPS cert information
|
||||||
SERVER_CERT = "pki/ssl.crt"
|
SERVER_CERT = "pki/mycert.pem"
|
||||||
SERVER_KEY = "pki/ssl.pem"
|
SERVER_KEY = "pki/mykey.pem"
|
||||||
CERT_CHAIN = ""
|
CERT_CHAIN = ""
|
||||||
@@ -93,7 +93,7 @@ def dict_to_table(ava, lev=0, width=1):
|
|||||||
def handle_static(environ, start_response, path):
|
def handle_static(environ, start_response, path):
|
||||||
"""
|
"""
|
||||||
Creates a response for a static file. There might be a longer 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 environ: wsgi enviroment
|
||||||
:param start_response: wsgi start response
|
:param start_response: wsgi start response
|
||||||
@@ -534,7 +534,7 @@ class SSO(object):
|
|||||||
self.cache.relay_state[_rstate] = came_from
|
self.cache.relay_state[_rstate] = came_from
|
||||||
ht_args = _cli.apply_binding(_binding, "%s" % req, destination,
|
ht_args = _cli.apply_binding(_binding, "%s" % req, destination,
|
||||||
relay_state=_rstate)
|
relay_state=_rstate)
|
||||||
_sid = req.id
|
_sid = req_id
|
||||||
logger.debug("ht_args: %s" % ht_args)
|
logger.debug("ht_args: %s" % ht_args)
|
||||||
except Exception, exc:
|
except Exception, exc:
|
||||||
logger.exception(exc)
|
logger.exception(exc)
|
||||||
@@ -645,7 +645,7 @@ def application(environ, start_response):
|
|||||||
The main WSGI application. Dispatch the current request to
|
The main WSGI application. Dispatch the current request to
|
||||||
the functions from above.
|
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 environ: The HTTP application environment
|
||||||
:param start_response: The application to run when the handling of the
|
: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
|
PORT = service_conf.PORT
|
||||||
# ------- HTTPS -------
|
# ------- HTTPS -------
|
||||||
# These should point to relevant files
|
# These should point to relevant files
|
||||||
SERVER_CERT = service_conf.SERVER_CERT
|
SERVER_CERT = service_conf.SERVER_CERT
|
||||||
SERVER_KEY = service_conf.SERVER_KEY
|
SERVER_KEY = service_conf.SERVER_KEY
|
||||||
# This is of course the certificate chain for the CA that signed
|
# 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
|
CERT_CHAIN = service_conf.CERT_CHAIN
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
@@ -727,13 +728,13 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
add_urls()
|
add_urls()
|
||||||
|
|
||||||
SRV = wsgiserver.CherryPyWSGIServer(('0.0.0.0', PORT), application)
|
SRV = wsgiserver.CherryPyWSGIServer((HOST, PORT), application)
|
||||||
|
|
||||||
if service_conf.HTTPS:
|
if service_conf.HTTPS:
|
||||||
SRV.ssl_adapter = ssl_pyopenssl.pyOpenSSLAdapter(SERVER_CERT,
|
SRV.ssl_adapter = ssl_pyopenssl.pyOpenSSLAdapter(SERVER_CERT,
|
||||||
SERVER_KEY, CERT_CHAIN)
|
SERVER_KEY, CERT_CHAIN)
|
||||||
logger.info("Server starting")
|
logger.info("Server starting")
|
||||||
print "SP listening on port: %s" % PORT
|
print "SP listening on %s:%s" % (HOST, PORT)
|
||||||
try:
|
try:
|
||||||
SRV.start()
|
SRV.start()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
|||||||
3
setup.py
3
setup.py
@@ -46,7 +46,8 @@ install_requires = [
|
|||||||
'pycrypto', # 'Crypto'
|
'pycrypto', # 'Crypto'
|
||||||
'pytz',
|
'pytz',
|
||||||
'pyOpenSSL',
|
'pyOpenSSL',
|
||||||
'python-dateutil'
|
'python-dateutil',
|
||||||
|
'argparse'
|
||||||
]
|
]
|
||||||
|
|
||||||
tests_require = [
|
tests_require = [
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ def _match(attr, ava):
|
|||||||
return None
|
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
|
""" Filter
|
||||||
|
|
||||||
:param ava: An attribute value assertion as a dictionary
|
: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
|
required
|
||||||
:param optional: list of RequestedAttribute instances defined to be
|
:param optional: list of RequestedAttribute instances defined to be
|
||||||
optional
|
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
|
:return: The modified attribute value assertion
|
||||||
"""
|
"""
|
||||||
res = {}
|
res = {}
|
||||||
@@ -116,7 +119,7 @@ def filter_on_attributes(ava, required=None, optional=None, acs=None):
|
|||||||
values = []
|
values = []
|
||||||
res[_fn] = _filter_values(ava[_fn], values, True)
|
res[_fn] = _filter_values(ava[_fn], values, True)
|
||||||
continue
|
continue
|
||||||
else:
|
elif fail_on_unfulfilled_requirements:
|
||||||
desc = "Required attribute missing: '%s' (%s)" % (attr["name"],
|
desc = "Required attribute missing: '%s' (%s)" % (attr["name"],
|
||||||
_name)
|
_name)
|
||||||
raise MissingValue(desc)
|
raise MissingValue(desc)
|
||||||
@@ -434,6 +437,16 @@ class Policy(object):
|
|||||||
|
|
||||||
return self.get("attribute_restrictions", sp_entity_id)
|
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):
|
def entity_category_attributes(self, ec):
|
||||||
if not self._restrictions:
|
if not self._restrictions:
|
||||||
return None
|
return None
|
||||||
@@ -492,7 +505,9 @@ class Policy(object):
|
|||||||
|
|
||||||
if required or optional:
|
if required or optional:
|
||||||
logger.debug("required: %s, optional: %s" % (required, 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
|
return ava
|
||||||
|
|
||||||
@@ -543,6 +558,103 @@ class EntityCategories(object):
|
|||||||
pass
|
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):
|
class Assertion(dict):
|
||||||
""" Handles assertions about subjects """
|
""" Handles assertions about subjects """
|
||||||
|
|
||||||
@@ -550,101 +662,6 @@ class Assertion(dict):
|
|||||||
dict.__init__(self, dic)
|
dict.__init__(self, dic)
|
||||||
self.acs = []
|
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,
|
def construct(self, sp_entity_id, in_response_to, consumer_url,
|
||||||
name_id, attrconvs, policy, issuer, authn_class=None,
|
name_id, attrconvs, policy, issuer, authn_class=None,
|
||||||
authn_auth=None, authn_decl=None, encrypt=None,
|
authn_auth=None, authn_decl=None, encrypt=None,
|
||||||
@@ -695,10 +712,10 @@ class Assertion(dict):
|
|||||||
conds = policy.conditions(sp_entity_id)
|
conds = policy.conditions(sp_entity_id)
|
||||||
|
|
||||||
if authn_auth or authn_class or authn_decl or authn_decl_ref:
|
if authn_auth or authn_class or authn_decl or authn_decl_ref:
|
||||||
_authn_statement = self._authn_statement(authn_class, authn_auth,
|
_authn_statement = authn_statement(authn_class, authn_auth,
|
||||||
authn_decl, authn_decl_ref,
|
authn_decl, authn_decl_ref,
|
||||||
authn_instant,
|
authn_instant,
|
||||||
subject_locality)
|
subject_locality)
|
||||||
else:
|
else:
|
||||||
_authn_statement = None
|
_authn_statement = None
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
# See http://technet.microsoft.com/en-us/library/cc733065(v=ws.10).aspx
|
CLAIMS = 'http://schemas.xmlsoap.org/claims/'
|
||||||
# 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.
|
|
||||||
|
|
||||||
MAP = {
|
MAP = {
|
||||||
"identifier": "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified",
|
"identifier": "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified",
|
||||||
"fro": {
|
'fro': {
|
||||||
'http://schemas.xmlsoap.org/claims/commonname': 'commonName',
|
CLAIMS+'commonname': 'commonName',
|
||||||
'http://schemas.xmlsoap.org/claims/emailaddress': 'emailAddress',
|
CLAIMS+'emailaddress': 'emailAddress',
|
||||||
'http://schemas.xmlsoap.org/claims/group': 'group',
|
CLAIMS+'group': 'group',
|
||||||
'http://schemas.xmlsoap.org/claims/upn': 'upn',
|
CLAIMS+'upn': 'upn',
|
||||||
},
|
},
|
||||||
"to": {
|
'to': {
|
||||||
'commonName': 'http://schemas.xmlsoap.org/claims/commonname',
|
'commonName': CLAIMS+'commonname',
|
||||||
'emailAddress': 'http://schemas.xmlsoap.org/claims/emailaddress',
|
'emailAddress': CLAIMS+'emailaddress',
|
||||||
'group': 'http://schemas.xmlsoap.org/claims/group',
|
'group': CLAIMS+'group',
|
||||||
'upn': 'http://schemas.xmlsoap.org/claims/upn',
|
'upn': CLAIMS+'upn',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +1,49 @@
|
|||||||
# See http://technet.microsoft.com/en-us/library/ee913589(v=ws.10).aspx
|
CLAIMS = 'http://schemas.xmlsoap.org/claims/'
|
||||||
# for information regarding the default claim types supported by
|
COM_WS_CLAIMS = 'http://schemas.xmlsoap.com/ws/2005/05/identity/claims/'
|
||||||
# Microsoft ADFS v2.0.
|
MS_CLAIMS = 'http://schemas.microsoft.com/ws/2008/06/identity/claims/'
|
||||||
|
ORG_WS_CLAIMS = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/'
|
||||||
|
|
||||||
|
|
||||||
MAP = {
|
MAP = {
|
||||||
"identifier": "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified",
|
"identifier": "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified",
|
||||||
"fro": {
|
'fro': {
|
||||||
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress': 'emailAddress',
|
CLAIMS+'commonname': 'commonName',
|
||||||
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname': 'givenName',
|
CLAIMS+'group': 'group',
|
||||||
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name': 'name',
|
COM_WS_CLAIMS+'denyonlysid': 'denyOnlySid',
|
||||||
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn': 'upn',
|
MS_CLAIMS+'authenticationmethod': 'authenticationMethod',
|
||||||
'http://schemas.xmlsoap.org/claims/commonname': 'commonName',
|
MS_CLAIMS+'denyonlyprimarygroupsid': 'denyOnlyPrimaryGroupSid',
|
||||||
'http://schemas.xmlsoap.org/claims/group': 'group',
|
MS_CLAIMS+'denyonlyprimarysid': 'denyOnlyPrimarySid',
|
||||||
'http://schemas.microsoft.com/ws/2008/06/identity/claims/role': 'role',
|
MS_CLAIMS+'groupsid': 'groupSid',
|
||||||
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname': 'surname',
|
MS_CLAIMS+'primarygroupsid': 'primaryGroupSid',
|
||||||
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier': 'privatePersonalId',
|
MS_CLAIMS+'primarysid': 'primarySid',
|
||||||
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier': 'nameId',
|
MS_CLAIMS+'role': 'role',
|
||||||
'http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod': 'authenticationMethod',
|
MS_CLAIMS+'windowsaccountname': 'windowsAccountName',
|
||||||
'http://schemas.xmlsoap.com/ws/2005/05/identity/claims/denyonlysid': 'denyOnlySid',
|
ORG_WS_CLAIMS+'emailaddress': 'emailAddress',
|
||||||
'http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarysid': 'denyOnlyPrimarySid',
|
ORG_WS_CLAIMS+'givenname': 'givenName',
|
||||||
'http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarygroupsid': 'denyOnlyPrimaryGroupSid',
|
ORG_WS_CLAIMS+'name': 'name',
|
||||||
'http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid': 'groupSid',
|
ORG_WS_CLAIMS+'nameidentifier': 'nameId',
|
||||||
'http://schemas.microsoft.com/ws/2008/06/identity/claims/primarygroupsid': 'primaryGroupSid',
|
ORG_WS_CLAIMS+'privatepersonalidentifier': 'privatePersonalId',
|
||||||
'http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid': 'primarySid',
|
ORG_WS_CLAIMS+'surname': 'surname',
|
||||||
'http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname': 'windowsAccountName',
|
ORG_WS_CLAIMS+'upn': 'upn',
|
||||||
},
|
},
|
||||||
"to": {
|
'to': {
|
||||||
'emailAddress': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress',
|
'authenticationMethod': MS_CLAIMS+'authenticationmethod',
|
||||||
'givenName': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname',
|
'commonName': CLAIMS+'commonname',
|
||||||
'name': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name',
|
'denyOnlyPrimaryGroupSid': MS_CLAIMS+'denyonlyprimarygroupsid',
|
||||||
'upn': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn',
|
'denyOnlyPrimarySid': MS_CLAIMS+'denyonlyprimarysid',
|
||||||
'commonName': 'http://schemas.xmlsoap.org/claims/commonname',
|
'denyOnlySid': COM_WS_CLAIMS+'denyonlysid',
|
||||||
'group': 'http://schemas.xmlsoap.org/claims/group',
|
'emailAddress': ORG_WS_CLAIMS+'emailaddress',
|
||||||
'role': 'http://schemas.microsoft.com/ws/2008/06/identity/claims/role',
|
'givenName': ORG_WS_CLAIMS+'givenname',
|
||||||
'surname': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname',
|
'group': CLAIMS+'group',
|
||||||
'privatePersonalId': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier',
|
'groupSid': MS_CLAIMS+'groupsid',
|
||||||
'nameId': 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier',
|
'name': ORG_WS_CLAIMS+'name',
|
||||||
'authenticationMethod': 'http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod',
|
'nameId': ORG_WS_CLAIMS+'nameidentifier',
|
||||||
'denyOnlySid': 'http://schemas.xmlsoap.com/ws/2005/05/identity/claims/denyonlysid',
|
'primaryGroupSid': MS_CLAIMS+'primarygroupsid',
|
||||||
'denyOnlyPrimarySid': 'http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarysid',
|
'primarySid': MS_CLAIMS+'primarysid',
|
||||||
'denyOnlyPrimaryGroupSid': 'http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarygroupsid',
|
'privatePersonalId': ORG_WS_CLAIMS+'privatepersonalidentifier',
|
||||||
'groupSid': 'http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid',
|
'role': MS_CLAIMS+'role',
|
||||||
'primaryGroupSid': 'http://schemas.microsoft.com/ws/2008/06/identity/claims/primarygroupsid',
|
'surname': ORG_WS_CLAIMS+'surname',
|
||||||
'primarySid': 'http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid',
|
'upn': ORG_WS_CLAIMS+'upn',
|
||||||
'windowsAccountName': 'http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname',
|
'windowsAccountName': MS_CLAIMS+'windowsaccountname',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,326 +1,328 @@
|
|||||||
|
DEF = 'urn:mace:dir:attribute-def:'
|
||||||
|
|
||||||
|
|
||||||
MAP = {
|
MAP = {
|
||||||
"identifier": "urn:oasis:names:tc:SAML:2.0:attrname-format:basic",
|
"identifier": "urn:oasis:names:tc:SAML:2.0:attrname-format:basic",
|
||||||
"fro": {
|
'fro': {
|
||||||
'urn:mace:dir:attribute-def:aRecord': 'aRecord',
|
DEF+'aRecord': 'aRecord',
|
||||||
'urn:mace:dir:attribute-def:aliasedEntryName': 'aliasedEntryName',
|
DEF+'aliasedEntryName': 'aliasedEntryName',
|
||||||
'urn:mace:dir:attribute-def:aliasedObjectName': 'aliasedObjectName',
|
DEF+'aliasedObjectName': 'aliasedObjectName',
|
||||||
'urn:mace:dir:attribute-def:associatedDomain': 'associatedDomain',
|
DEF+'associatedDomain': 'associatedDomain',
|
||||||
'urn:mace:dir:attribute-def:associatedName': 'associatedName',
|
DEF+'associatedName': 'associatedName',
|
||||||
'urn:mace:dir:attribute-def:audio': 'audio',
|
DEF+'audio': 'audio',
|
||||||
'urn:mace:dir:attribute-def:authorityRevocationList': 'authorityRevocationList',
|
DEF+'authorityRevocationList': 'authorityRevocationList',
|
||||||
'urn:mace:dir:attribute-def:buildingName': 'buildingName',
|
DEF+'buildingName': 'buildingName',
|
||||||
'urn:mace:dir:attribute-def:businessCategory': 'businessCategory',
|
DEF+'businessCategory': 'businessCategory',
|
||||||
'urn:mace:dir:attribute-def:c': 'c',
|
DEF+'c': 'c',
|
||||||
'urn:mace:dir:attribute-def:cACertificate': 'cACertificate',
|
DEF+'cACertificate': 'cACertificate',
|
||||||
'urn:mace:dir:attribute-def:cNAMERecord': 'cNAMERecord',
|
DEF+'cNAMERecord': 'cNAMERecord',
|
||||||
'urn:mace:dir:attribute-def:carLicense': 'carLicense',
|
DEF+'carLicense': 'carLicense',
|
||||||
'urn:mace:dir:attribute-def:certificateRevocationList': 'certificateRevocationList',
|
DEF+'certificateRevocationList': 'certificateRevocationList',
|
||||||
'urn:mace:dir:attribute-def:cn': 'cn',
|
DEF+'cn': 'cn',
|
||||||
'urn:mace:dir:attribute-def:co': 'co',
|
DEF+'co': 'co',
|
||||||
'urn:mace:dir:attribute-def:commonName': 'commonName',
|
DEF+'commonName': 'commonName',
|
||||||
'urn:mace:dir:attribute-def:countryName': 'countryName',
|
DEF+'countryName': 'countryName',
|
||||||
'urn:mace:dir:attribute-def:crossCertificatePair': 'crossCertificatePair',
|
DEF+'crossCertificatePair': 'crossCertificatePair',
|
||||||
'urn:mace:dir:attribute-def:dITRedirect': 'dITRedirect',
|
DEF+'dITRedirect': 'dITRedirect',
|
||||||
'urn:mace:dir:attribute-def:dSAQuality': 'dSAQuality',
|
DEF+'dSAQuality': 'dSAQuality',
|
||||||
'urn:mace:dir:attribute-def:dc': 'dc',
|
DEF+'dc': 'dc',
|
||||||
'urn:mace:dir:attribute-def:deltaRevocationList': 'deltaRevocationList',
|
DEF+'deltaRevocationList': 'deltaRevocationList',
|
||||||
'urn:mace:dir:attribute-def:departmentNumber': 'departmentNumber',
|
DEF+'departmentNumber': 'departmentNumber',
|
||||||
'urn:mace:dir:attribute-def:description': 'description',
|
DEF+'description': 'description',
|
||||||
'urn:mace:dir:attribute-def:destinationIndicator': 'destinationIndicator',
|
DEF+'destinationIndicator': 'destinationIndicator',
|
||||||
'urn:mace:dir:attribute-def:displayName': 'displayName',
|
DEF+'displayName': 'displayName',
|
||||||
'urn:mace:dir:attribute-def:distinguishedName': 'distinguishedName',
|
DEF+'distinguishedName': 'distinguishedName',
|
||||||
'urn:mace:dir:attribute-def:dmdName': 'dmdName',
|
DEF+'dmdName': 'dmdName',
|
||||||
'urn:mace:dir:attribute-def:dnQualifier': 'dnQualifier',
|
DEF+'dnQualifier': 'dnQualifier',
|
||||||
'urn:mace:dir:attribute-def:documentAuthor': 'documentAuthor',
|
DEF+'documentAuthor': 'documentAuthor',
|
||||||
'urn:mace:dir:attribute-def:documentIdentifier': 'documentIdentifier',
|
DEF+'documentIdentifier': 'documentIdentifier',
|
||||||
'urn:mace:dir:attribute-def:documentLocation': 'documentLocation',
|
DEF+'documentLocation': 'documentLocation',
|
||||||
'urn:mace:dir:attribute-def:documentPublisher': 'documentPublisher',
|
DEF+'documentPublisher': 'documentPublisher',
|
||||||
'urn:mace:dir:attribute-def:documentTitle': 'documentTitle',
|
DEF+'documentTitle': 'documentTitle',
|
||||||
'urn:mace:dir:attribute-def:documentVersion': 'documentVersion',
|
DEF+'documentVersion': 'documentVersion',
|
||||||
'urn:mace:dir:attribute-def:domainComponent': 'domainComponent',
|
DEF+'domainComponent': 'domainComponent',
|
||||||
'urn:mace:dir:attribute-def:drink': 'drink',
|
DEF+'drink': 'drink',
|
||||||
'urn:mace:dir:attribute-def:eduOrgHomePageURI': 'eduOrgHomePageURI',
|
DEF+'eduOrgHomePageURI': 'eduOrgHomePageURI',
|
||||||
'urn:mace:dir:attribute-def:eduOrgIdentityAuthNPolicyURI': 'eduOrgIdentityAuthNPolicyURI',
|
DEF+'eduOrgIdentityAuthNPolicyURI': 'eduOrgIdentityAuthNPolicyURI',
|
||||||
'urn:mace:dir:attribute-def:eduOrgLegalName': 'eduOrgLegalName',
|
DEF+'eduOrgLegalName': 'eduOrgLegalName',
|
||||||
'urn:mace:dir:attribute-def:eduOrgSuperiorURI': 'eduOrgSuperiorURI',
|
DEF+'eduOrgSuperiorURI': 'eduOrgSuperiorURI',
|
||||||
'urn:mace:dir:attribute-def:eduOrgWhitePagesURI': 'eduOrgWhitePagesURI',
|
DEF+'eduOrgWhitePagesURI': 'eduOrgWhitePagesURI',
|
||||||
'urn:mace:dir:attribute-def:eduPersonAffiliation': 'eduPersonAffiliation',
|
DEF+'eduPersonAffiliation': 'eduPersonAffiliation',
|
||||||
'urn:mace:dir:attribute-def:eduPersonEntitlement': 'eduPersonEntitlement',
|
DEF+'eduPersonEntitlement': 'eduPersonEntitlement',
|
||||||
'urn:mace:dir:attribute-def:eduPersonNickname': 'eduPersonNickname',
|
DEF+'eduPersonNickname': 'eduPersonNickname',
|
||||||
'urn:mace:dir:attribute-def:eduPersonOrgDN': 'eduPersonOrgDN',
|
DEF+'eduPersonOrgDN': 'eduPersonOrgDN',
|
||||||
'urn:mace:dir:attribute-def:eduPersonOrgUnitDN': 'eduPersonOrgUnitDN',
|
DEF+'eduPersonOrgUnitDN': 'eduPersonOrgUnitDN',
|
||||||
'urn:mace:dir:attribute-def:eduPersonPrimaryAffiliation': 'eduPersonPrimaryAffiliation',
|
DEF+'eduPersonPrimaryAffiliation': 'eduPersonPrimaryAffiliation',
|
||||||
'urn:mace:dir:attribute-def:eduPersonPrimaryOrgUnitDN': 'eduPersonPrimaryOrgUnitDN',
|
DEF+'eduPersonPrimaryOrgUnitDN': 'eduPersonPrimaryOrgUnitDN',
|
||||||
'urn:mace:dir:attribute-def:eduPersonPrincipalName': 'eduPersonPrincipalName',
|
DEF+'eduPersonPrincipalName': 'eduPersonPrincipalName',
|
||||||
'urn:mace:dir:attribute-def:eduPersonScopedAffiliation': 'eduPersonScopedAffiliation',
|
DEF+'eduPersonScopedAffiliation': 'eduPersonScopedAffiliation',
|
||||||
'urn:mace:dir:attribute-def:eduPersonTargetedID': 'eduPersonTargetedID',
|
DEF+'eduPersonTargetedID': 'eduPersonTargetedID',
|
||||||
'urn:mace:dir:attribute-def:email': 'email',
|
DEF+'email': 'email',
|
||||||
'urn:mace:dir:attribute-def:emailAddress': 'emailAddress',
|
DEF+'emailAddress': 'emailAddress',
|
||||||
'urn:mace:dir:attribute-def:employeeNumber': 'employeeNumber',
|
DEF+'employeeNumber': 'employeeNumber',
|
||||||
'urn:mace:dir:attribute-def:employeeType': 'employeeType',
|
DEF+'employeeType': 'employeeType',
|
||||||
'urn:mace:dir:attribute-def:enhancedSearchGuide': 'enhancedSearchGuide',
|
DEF+'enhancedSearchGuide': 'enhancedSearchGuide',
|
||||||
'urn:mace:dir:attribute-def:facsimileTelephoneNumber': 'facsimileTelephoneNumber',
|
DEF+'facsimileTelephoneNumber': 'facsimileTelephoneNumber',
|
||||||
'urn:mace:dir:attribute-def:favouriteDrink': 'favouriteDrink',
|
DEF+'favouriteDrink': 'favouriteDrink',
|
||||||
'urn:mace:dir:attribute-def:fax': 'fax',
|
DEF+'fax': 'fax',
|
||||||
'urn:mace:dir:attribute-def:federationFeideSchemaVersion': 'federationFeideSchemaVersion',
|
DEF+'federationFeideSchemaVersion': 'federationFeideSchemaVersion',
|
||||||
'urn:mace:dir:attribute-def:friendlyCountryName': 'friendlyCountryName',
|
DEF+'friendlyCountryName': 'friendlyCountryName',
|
||||||
'urn:mace:dir:attribute-def:generationQualifier': 'generationQualifier',
|
DEF+'generationQualifier': 'generationQualifier',
|
||||||
'urn:mace:dir:attribute-def:givenName': 'givenName',
|
DEF+'givenName': 'givenName',
|
||||||
'urn:mace:dir:attribute-def:gn': 'gn',
|
DEF+'gn': 'gn',
|
||||||
'urn:mace:dir:attribute-def:homePhone': 'homePhone',
|
DEF+'homePhone': 'homePhone',
|
||||||
'urn:mace:dir:attribute-def:homePostalAddress': 'homePostalAddress',
|
DEF+'homePostalAddress': 'homePostalAddress',
|
||||||
'urn:mace:dir:attribute-def:homeTelephoneNumber': 'homeTelephoneNumber',
|
DEF+'homeTelephoneNumber': 'homeTelephoneNumber',
|
||||||
'urn:mace:dir:attribute-def:host': 'host',
|
DEF+'host': 'host',
|
||||||
'urn:mace:dir:attribute-def:houseIdentifier': 'houseIdentifier',
|
DEF+'houseIdentifier': 'houseIdentifier',
|
||||||
'urn:mace:dir:attribute-def:info': 'info',
|
DEF+'info': 'info',
|
||||||
'urn:mace:dir:attribute-def:initials': 'initials',
|
DEF+'initials': 'initials',
|
||||||
'urn:mace:dir:attribute-def:internationaliSDNNumber': 'internationaliSDNNumber',
|
DEF+'internationaliSDNNumber': 'internationaliSDNNumber',
|
||||||
'urn:mace:dir:attribute-def:janetMailbox': 'janetMailbox',
|
DEF+'janetMailbox': 'janetMailbox',
|
||||||
'urn:mace:dir:attribute-def:jpegPhoto': 'jpegPhoto',
|
DEF+'jpegPhoto': 'jpegPhoto',
|
||||||
'urn:mace:dir:attribute-def:knowledgeInformation': 'knowledgeInformation',
|
DEF+'knowledgeInformation': 'knowledgeInformation',
|
||||||
'urn:mace:dir:attribute-def:l': 'l',
|
DEF+'l': 'l',
|
||||||
'urn:mace:dir:attribute-def:labeledURI': 'labeledURI',
|
DEF+'labeledURI': 'labeledURI',
|
||||||
'urn:mace:dir:attribute-def:localityName': 'localityName',
|
DEF+'localityName': 'localityName',
|
||||||
'urn:mace:dir:attribute-def:mDRecord': 'mDRecord',
|
DEF+'mDRecord': 'mDRecord',
|
||||||
'urn:mace:dir:attribute-def:mXRecord': 'mXRecord',
|
DEF+'mXRecord': 'mXRecord',
|
||||||
'urn:mace:dir:attribute-def:mail': 'mail',
|
DEF+'mail': 'mail',
|
||||||
'urn:mace:dir:attribute-def:mailPreferenceOption': 'mailPreferenceOption',
|
DEF+'mailPreferenceOption': 'mailPreferenceOption',
|
||||||
'urn:mace:dir:attribute-def:manager': 'manager',
|
DEF+'manager': 'manager',
|
||||||
'urn:mace:dir:attribute-def:member': 'member',
|
DEF+'member': 'member',
|
||||||
'urn:mace:dir:attribute-def:mobile': 'mobile',
|
DEF+'mobile': 'mobile',
|
||||||
'urn:mace:dir:attribute-def:mobileTelephoneNumber': 'mobileTelephoneNumber',
|
DEF+'mobileTelephoneNumber': 'mobileTelephoneNumber',
|
||||||
'urn:mace:dir:attribute-def:nSRecord': 'nSRecord',
|
DEF+'nSRecord': 'nSRecord',
|
||||||
'urn:mace:dir:attribute-def:name': 'name',
|
DEF+'name': 'name',
|
||||||
'urn:mace:dir:attribute-def:norEduOrgAcronym': 'norEduOrgAcronym',
|
DEF+'norEduOrgAcronym': 'norEduOrgAcronym',
|
||||||
'urn:mace:dir:attribute-def:norEduOrgNIN': 'norEduOrgNIN',
|
DEF+'norEduOrgNIN': 'norEduOrgNIN',
|
||||||
'urn:mace:dir:attribute-def:norEduOrgSchemaVersion': 'norEduOrgSchemaVersion',
|
DEF+'norEduOrgSchemaVersion': 'norEduOrgSchemaVersion',
|
||||||
'urn:mace:dir:attribute-def:norEduOrgUniqueIdentifier': 'norEduOrgUniqueIdentifier',
|
DEF+'norEduOrgUniqueIdentifier': 'norEduOrgUniqueIdentifier',
|
||||||
'urn:mace:dir:attribute-def:norEduOrgUniqueNumber': 'norEduOrgUniqueNumber',
|
DEF+'norEduOrgUniqueNumber': 'norEduOrgUniqueNumber',
|
||||||
'urn:mace:dir:attribute-def:norEduOrgUnitUniqueIdentifier': 'norEduOrgUnitUniqueIdentifier',
|
DEF+'norEduOrgUnitUniqueIdentifier': 'norEduOrgUnitUniqueIdentifier',
|
||||||
'urn:mace:dir:attribute-def:norEduOrgUnitUniqueNumber': 'norEduOrgUnitUniqueNumber',
|
DEF+'norEduOrgUnitUniqueNumber': 'norEduOrgUnitUniqueNumber',
|
||||||
'urn:mace:dir:attribute-def:norEduPersonBirthDate': 'norEduPersonBirthDate',
|
DEF+'norEduPersonBirthDate': 'norEduPersonBirthDate',
|
||||||
'urn:mace:dir:attribute-def:norEduPersonLIN': 'norEduPersonLIN',
|
DEF+'norEduPersonLIN': 'norEduPersonLIN',
|
||||||
'urn:mace:dir:attribute-def:norEduPersonNIN': 'norEduPersonNIN',
|
DEF+'norEduPersonNIN': 'norEduPersonNIN',
|
||||||
'urn:mace:dir:attribute-def:o': 'o',
|
DEF+'o': 'o',
|
||||||
'urn:mace:dir:attribute-def:objectClass': 'objectClass',
|
DEF+'objectClass': 'objectClass',
|
||||||
'urn:mace:dir:attribute-def:organizationName': 'organizationName',
|
DEF+'organizationName': 'organizationName',
|
||||||
'urn:mace:dir:attribute-def:organizationalStatus': 'organizationalStatus',
|
DEF+'organizationalStatus': 'organizationalStatus',
|
||||||
'urn:mace:dir:attribute-def:organizationalUnitName': 'organizationalUnitName',
|
DEF+'organizationalUnitName': 'organizationalUnitName',
|
||||||
'urn:mace:dir:attribute-def:otherMailbox': 'otherMailbox',
|
DEF+'otherMailbox': 'otherMailbox',
|
||||||
'urn:mace:dir:attribute-def:ou': 'ou',
|
DEF+'ou': 'ou',
|
||||||
'urn:mace:dir:attribute-def:owner': 'owner',
|
DEF+'owner': 'owner',
|
||||||
'urn:mace:dir:attribute-def:pager': 'pager',
|
DEF+'pager': 'pager',
|
||||||
'urn:mace:dir:attribute-def:pagerTelephoneNumber': 'pagerTelephoneNumber',
|
DEF+'pagerTelephoneNumber': 'pagerTelephoneNumber',
|
||||||
'urn:mace:dir:attribute-def:personalSignature': 'personalSignature',
|
DEF+'personalSignature': 'personalSignature',
|
||||||
'urn:mace:dir:attribute-def:personalTitle': 'personalTitle',
|
DEF+'personalTitle': 'personalTitle',
|
||||||
'urn:mace:dir:attribute-def:photo': 'photo',
|
DEF+'photo': 'photo',
|
||||||
'urn:mace:dir:attribute-def:physicalDeliveryOfficeName': 'physicalDeliveryOfficeName',
|
DEF+'physicalDeliveryOfficeName': 'physicalDeliveryOfficeName',
|
||||||
'urn:mace:dir:attribute-def:pkcs9email': 'pkcs9email',
|
DEF+'pkcs9email': 'pkcs9email',
|
||||||
'urn:mace:dir:attribute-def:postOfficeBox': 'postOfficeBox',
|
DEF+'postOfficeBox': 'postOfficeBox',
|
||||||
'urn:mace:dir:attribute-def:postalAddress': 'postalAddress',
|
DEF+'postalAddress': 'postalAddress',
|
||||||
'urn:mace:dir:attribute-def:postalCode': 'postalCode',
|
DEF+'postalCode': 'postalCode',
|
||||||
'urn:mace:dir:attribute-def:preferredDeliveryMethod': 'preferredDeliveryMethod',
|
DEF+'preferredDeliveryMethod': 'preferredDeliveryMethod',
|
||||||
'urn:mace:dir:attribute-def:preferredLanguage': 'preferredLanguage',
|
DEF+'preferredLanguage': 'preferredLanguage',
|
||||||
'urn:mace:dir:attribute-def:presentationAddress': 'presentationAddress',
|
DEF+'presentationAddress': 'presentationAddress',
|
||||||
'urn:mace:dir:attribute-def:protocolInformation': 'protocolInformation',
|
DEF+'protocolInformation': 'protocolInformation',
|
||||||
'urn:mace:dir:attribute-def:pseudonym': 'pseudonym',
|
DEF+'pseudonym': 'pseudonym',
|
||||||
'urn:mace:dir:attribute-def:registeredAddress': 'registeredAddress',
|
DEF+'registeredAddress': 'registeredAddress',
|
||||||
'urn:mace:dir:attribute-def:rfc822Mailbox': 'rfc822Mailbox',
|
DEF+'rfc822Mailbox': 'rfc822Mailbox',
|
||||||
'urn:mace:dir:attribute-def:roleOccupant': 'roleOccupant',
|
DEF+'roleOccupant': 'roleOccupant',
|
||||||
'urn:mace:dir:attribute-def:roomNumber': 'roomNumber',
|
DEF+'roomNumber': 'roomNumber',
|
||||||
'urn:mace:dir:attribute-def:sOARecord': 'sOARecord',
|
DEF+'sOARecord': 'sOARecord',
|
||||||
'urn:mace:dir:attribute-def:searchGuide': 'searchGuide',
|
DEF+'searchGuide': 'searchGuide',
|
||||||
'urn:mace:dir:attribute-def:secretary': 'secretary',
|
DEF+'secretary': 'secretary',
|
||||||
'urn:mace:dir:attribute-def:seeAlso': 'seeAlso',
|
DEF+'seeAlso': 'seeAlso',
|
||||||
'urn:mace:dir:attribute-def:serialNumber': 'serialNumber',
|
DEF+'serialNumber': 'serialNumber',
|
||||||
'urn:mace:dir:attribute-def:singleLevelQuality': 'singleLevelQuality',
|
DEF+'singleLevelQuality': 'singleLevelQuality',
|
||||||
'urn:mace:dir:attribute-def:sn': 'sn',
|
DEF+'sn': 'sn',
|
||||||
'urn:mace:dir:attribute-def:st': 'st',
|
DEF+'st': 'st',
|
||||||
'urn:mace:dir:attribute-def:stateOrProvinceName': 'stateOrProvinceName',
|
DEF+'stateOrProvinceName': 'stateOrProvinceName',
|
||||||
'urn:mace:dir:attribute-def:street': 'street',
|
DEF+'street': 'street',
|
||||||
'urn:mace:dir:attribute-def:streetAddress': 'streetAddress',
|
DEF+'streetAddress': 'streetAddress',
|
||||||
'urn:mace:dir:attribute-def:subtreeMaximumQuality': 'subtreeMaximumQuality',
|
DEF+'subtreeMaximumQuality': 'subtreeMaximumQuality',
|
||||||
'urn:mace:dir:attribute-def:subtreeMinimumQuality': 'subtreeMinimumQuality',
|
DEF+'subtreeMinimumQuality': 'subtreeMinimumQuality',
|
||||||
'urn:mace:dir:attribute-def:supportedAlgorithms': 'supportedAlgorithms',
|
DEF+'supportedAlgorithms': 'supportedAlgorithms',
|
||||||
'urn:mace:dir:attribute-def:supportedApplicationContext': 'supportedApplicationContext',
|
DEF+'supportedApplicationContext': 'supportedApplicationContext',
|
||||||
'urn:mace:dir:attribute-def:surname': 'surname',
|
DEF+'surname': 'surname',
|
||||||
'urn:mace:dir:attribute-def:telephoneNumber': 'telephoneNumber',
|
DEF+'telephoneNumber': 'telephoneNumber',
|
||||||
'urn:mace:dir:attribute-def:teletexTerminalIdentifier': 'teletexTerminalIdentifier',
|
DEF+'teletexTerminalIdentifier': 'teletexTerminalIdentifier',
|
||||||
'urn:mace:dir:attribute-def:telexNumber': 'telexNumber',
|
DEF+'telexNumber': 'telexNumber',
|
||||||
'urn:mace:dir:attribute-def:textEncodedORAddress': 'textEncodedORAddress',
|
DEF+'textEncodedORAddress': 'textEncodedORAddress',
|
||||||
'urn:mace:dir:attribute-def:title': 'title',
|
DEF+'title': 'title',
|
||||||
'urn:mace:dir:attribute-def:uid': 'uid',
|
DEF+'uid': 'uid',
|
||||||
'urn:mace:dir:attribute-def:uniqueIdentifier': 'uniqueIdentifier',
|
DEF+'uniqueIdentifier': 'uniqueIdentifier',
|
||||||
'urn:mace:dir:attribute-def:uniqueMember': 'uniqueMember',
|
DEF+'uniqueMember': 'uniqueMember',
|
||||||
'urn:mace:dir:attribute-def:userCertificate': 'userCertificate',
|
DEF+'userCertificate': 'userCertificate',
|
||||||
'urn:mace:dir:attribute-def:userClass': 'userClass',
|
DEF+'userClass': 'userClass',
|
||||||
'urn:mace:dir:attribute-def:userPKCS12': 'userPKCS12',
|
DEF+'userPKCS12': 'userPKCS12',
|
||||||
'urn:mace:dir:attribute-def:userPassword': 'userPassword',
|
DEF+'userPassword': 'userPassword',
|
||||||
'urn:mace:dir:attribute-def:userSMIMECertificate': 'userSMIMECertificate',
|
DEF+'userSMIMECertificate': 'userSMIMECertificate',
|
||||||
'urn:mace:dir:attribute-def:userid': 'userid',
|
DEF+'userid': 'userid',
|
||||||
'urn:mace:dir:attribute-def:x121Address': 'x121Address',
|
DEF+'x121Address': 'x121Address',
|
||||||
'urn:mace:dir:attribute-def:x500UniqueIdentifier': 'x500UniqueIdentifier',
|
DEF+'x500UniqueIdentifier': 'x500UniqueIdentifier',
|
||||||
},
|
},
|
||||||
"to": {
|
'to': {
|
||||||
'aRecord': 'urn:mace:dir:attribute-def:aRecord',
|
'aRecord': DEF+'aRecord',
|
||||||
'aliasedEntryName': 'urn:mace:dir:attribute-def:aliasedEntryName',
|
'aliasedEntryName': DEF+'aliasedEntryName',
|
||||||
'aliasedObjectName': 'urn:mace:dir:attribute-def:aliasedObjectName',
|
'aliasedObjectName': DEF+'aliasedObjectName',
|
||||||
'associatedDomain': 'urn:mace:dir:attribute-def:associatedDomain',
|
'associatedDomain': DEF+'associatedDomain',
|
||||||
'associatedName': 'urn:mace:dir:attribute-def:associatedName',
|
'associatedName': DEF+'associatedName',
|
||||||
'audio': 'urn:mace:dir:attribute-def:audio',
|
'audio': DEF+'audio',
|
||||||
'authorityRevocationList': 'urn:mace:dir:attribute-def:authorityRevocationList',
|
'authorityRevocationList': DEF+'authorityRevocationList',
|
||||||
'buildingName': 'urn:mace:dir:attribute-def:buildingName',
|
'buildingName': DEF+'buildingName',
|
||||||
'businessCategory': 'urn:mace:dir:attribute-def:businessCategory',
|
'businessCategory': DEF+'businessCategory',
|
||||||
'c': 'urn:mace:dir:attribute-def:c',
|
'c': DEF+'c',
|
||||||
'cACertificate': 'urn:mace:dir:attribute-def:cACertificate',
|
'cACertificate': DEF+'cACertificate',
|
||||||
'cNAMERecord': 'urn:mace:dir:attribute-def:cNAMERecord',
|
'cNAMERecord': DEF+'cNAMERecord',
|
||||||
'carLicense': 'urn:mace:dir:attribute-def:carLicense',
|
'carLicense': DEF+'carLicense',
|
||||||
'certificateRevocationList': 'urn:mace:dir:attribute-def:certificateRevocationList',
|
'certificateRevocationList': DEF+'certificateRevocationList',
|
||||||
'cn': 'urn:mace:dir:attribute-def:cn',
|
'cn': DEF+'cn',
|
||||||
'co': 'urn:mace:dir:attribute-def:co',
|
'co': DEF+'co',
|
||||||
'commonName': 'urn:mace:dir:attribute-def:commonName',
|
'commonName': DEF+'commonName',
|
||||||
'countryName': 'urn:mace:dir:attribute-def:countryName',
|
'countryName': DEF+'countryName',
|
||||||
'crossCertificatePair': 'urn:mace:dir:attribute-def:crossCertificatePair',
|
'crossCertificatePair': DEF+'crossCertificatePair',
|
||||||
'dITRedirect': 'urn:mace:dir:attribute-def:dITRedirect',
|
'dITRedirect': DEF+'dITRedirect',
|
||||||
'dSAQuality': 'urn:mace:dir:attribute-def:dSAQuality',
|
'dSAQuality': DEF+'dSAQuality',
|
||||||
'dc': 'urn:mace:dir:attribute-def:dc',
|
'dc': DEF+'dc',
|
||||||
'deltaRevocationList': 'urn:mace:dir:attribute-def:deltaRevocationList',
|
'deltaRevocationList': DEF+'deltaRevocationList',
|
||||||
'departmentNumber': 'urn:mace:dir:attribute-def:departmentNumber',
|
'departmentNumber': DEF+'departmentNumber',
|
||||||
'description': 'urn:mace:dir:attribute-def:description',
|
'description': DEF+'description',
|
||||||
'destinationIndicator': 'urn:mace:dir:attribute-def:destinationIndicator',
|
'destinationIndicator': DEF+'destinationIndicator',
|
||||||
'displayName': 'urn:mace:dir:attribute-def:displayName',
|
'displayName': DEF+'displayName',
|
||||||
'distinguishedName': 'urn:mace:dir:attribute-def:distinguishedName',
|
'distinguishedName': DEF+'distinguishedName',
|
||||||
'dmdName': 'urn:mace:dir:attribute-def:dmdName',
|
'dmdName': DEF+'dmdName',
|
||||||
'dnQualifier': 'urn:mace:dir:attribute-def:dnQualifier',
|
'dnQualifier': DEF+'dnQualifier',
|
||||||
'documentAuthor': 'urn:mace:dir:attribute-def:documentAuthor',
|
'documentAuthor': DEF+'documentAuthor',
|
||||||
'documentIdentifier': 'urn:mace:dir:attribute-def:documentIdentifier',
|
'documentIdentifier': DEF+'documentIdentifier',
|
||||||
'documentLocation': 'urn:mace:dir:attribute-def:documentLocation',
|
'documentLocation': DEF+'documentLocation',
|
||||||
'documentPublisher': 'urn:mace:dir:attribute-def:documentPublisher',
|
'documentPublisher': DEF+'documentPublisher',
|
||||||
'documentTitle': 'urn:mace:dir:attribute-def:documentTitle',
|
'documentTitle': DEF+'documentTitle',
|
||||||
'documentVersion': 'urn:mace:dir:attribute-def:documentVersion',
|
'documentVersion': DEF+'documentVersion',
|
||||||
'domainComponent': 'urn:mace:dir:attribute-def:domainComponent',
|
'domainComponent': DEF+'domainComponent',
|
||||||
'drink': 'urn:mace:dir:attribute-def:drink',
|
'drink': DEF+'drink',
|
||||||
'eduOrgHomePageURI': 'urn:mace:dir:attribute-def:eduOrgHomePageURI',
|
'eduOrgHomePageURI': DEF+'eduOrgHomePageURI',
|
||||||
'eduOrgIdentityAuthNPolicyURI': 'urn:mace:dir:attribute-def:eduOrgIdentityAuthNPolicyURI',
|
'eduOrgIdentityAuthNPolicyURI': DEF+'eduOrgIdentityAuthNPolicyURI',
|
||||||
'eduOrgLegalName': 'urn:mace:dir:attribute-def:eduOrgLegalName',
|
'eduOrgLegalName': DEF+'eduOrgLegalName',
|
||||||
'eduOrgSuperiorURI': 'urn:mace:dir:attribute-def:eduOrgSuperiorURI',
|
'eduOrgSuperiorURI': DEF+'eduOrgSuperiorURI',
|
||||||
'eduOrgWhitePagesURI': 'urn:mace:dir:attribute-def:eduOrgWhitePagesURI',
|
'eduOrgWhitePagesURI': DEF+'eduOrgWhitePagesURI',
|
||||||
'eduPersonAffiliation': 'urn:mace:dir:attribute-def:eduPersonAffiliation',
|
'eduPersonAffiliation': DEF+'eduPersonAffiliation',
|
||||||
'eduPersonEntitlement': 'urn:mace:dir:attribute-def:eduPersonEntitlement',
|
'eduPersonEntitlement': DEF+'eduPersonEntitlement',
|
||||||
'eduPersonNickname': 'urn:mace:dir:attribute-def:eduPersonNickname',
|
'eduPersonNickname': DEF+'eduPersonNickname',
|
||||||
'eduPersonOrgDN': 'urn:mace:dir:attribute-def:eduPersonOrgDN',
|
'eduPersonOrgDN': DEF+'eduPersonOrgDN',
|
||||||
'eduPersonOrgUnitDN': 'urn:mace:dir:attribute-def:eduPersonOrgUnitDN',
|
'eduPersonOrgUnitDN': DEF+'eduPersonOrgUnitDN',
|
||||||
'eduPersonPrimaryAffiliation': 'urn:mace:dir:attribute-def:eduPersonPrimaryAffiliation',
|
'eduPersonPrimaryAffiliation': DEF+'eduPersonPrimaryAffiliation',
|
||||||
'eduPersonPrimaryOrgUnitDN': 'urn:mace:dir:attribute-def:eduPersonPrimaryOrgUnitDN',
|
'eduPersonPrimaryOrgUnitDN': DEF+'eduPersonPrimaryOrgUnitDN',
|
||||||
'eduPersonPrincipalName': 'urn:mace:dir:attribute-def:eduPersonPrincipalName',
|
'eduPersonPrincipalName': DEF+'eduPersonPrincipalName',
|
||||||
'eduPersonScopedAffiliation': 'urn:mace:dir:attribute-def:eduPersonScopedAffiliation',
|
'eduPersonScopedAffiliation': DEF+'eduPersonScopedAffiliation',
|
||||||
'eduPersonTargetedID': 'urn:mace:dir:attribute-def:eduPersonTargetedID',
|
'eduPersonTargetedID': DEF+'eduPersonTargetedID',
|
||||||
'email': 'urn:mace:dir:attribute-def:email',
|
'email': DEF+'email',
|
||||||
'emailAddress': 'urn:mace:dir:attribute-def:emailAddress',
|
'emailAddress': DEF+'emailAddress',
|
||||||
'employeeNumber': 'urn:mace:dir:attribute-def:employeeNumber',
|
'employeeNumber': DEF+'employeeNumber',
|
||||||
'employeeType': 'urn:mace:dir:attribute-def:employeeType',
|
'employeeType': DEF+'employeeType',
|
||||||
'enhancedSearchGuide': 'urn:mace:dir:attribute-def:enhancedSearchGuide',
|
'enhancedSearchGuide': DEF+'enhancedSearchGuide',
|
||||||
'facsimileTelephoneNumber': 'urn:mace:dir:attribute-def:facsimileTelephoneNumber',
|
'facsimileTelephoneNumber': DEF+'facsimileTelephoneNumber',
|
||||||
'favouriteDrink': 'urn:mace:dir:attribute-def:favouriteDrink',
|
'favouriteDrink': DEF+'favouriteDrink',
|
||||||
'fax': 'urn:mace:dir:attribute-def:fax',
|
'fax': DEF+'fax',
|
||||||
'federationFeideSchemaVersion': 'urn:mace:dir:attribute-def:federationFeideSchemaVersion',
|
'federationFeideSchemaVersion': DEF+'federationFeideSchemaVersion',
|
||||||
'friendlyCountryName': 'urn:mace:dir:attribute-def:friendlyCountryName',
|
'friendlyCountryName': DEF+'friendlyCountryName',
|
||||||
'generationQualifier': 'urn:mace:dir:attribute-def:generationQualifier',
|
'generationQualifier': DEF+'generationQualifier',
|
||||||
'givenName': 'urn:mace:dir:attribute-def:givenName',
|
'givenName': DEF+'givenName',
|
||||||
'gn': 'urn:mace:dir:attribute-def:gn',
|
'gn': DEF+'gn',
|
||||||
'homePhone': 'urn:mace:dir:attribute-def:homePhone',
|
'homePhone': DEF+'homePhone',
|
||||||
'homePostalAddress': 'urn:mace:dir:attribute-def:homePostalAddress',
|
'homePostalAddress': DEF+'homePostalAddress',
|
||||||
'homeTelephoneNumber': 'urn:mace:dir:attribute-def:homeTelephoneNumber',
|
'homeTelephoneNumber': DEF+'homeTelephoneNumber',
|
||||||
'host': 'urn:mace:dir:attribute-def:host',
|
'host': DEF+'host',
|
||||||
'houseIdentifier': 'urn:mace:dir:attribute-def:houseIdentifier',
|
'houseIdentifier': DEF+'houseIdentifier',
|
||||||
'info': 'urn:mace:dir:attribute-def:info',
|
'info': DEF+'info',
|
||||||
'initials': 'urn:mace:dir:attribute-def:initials',
|
'initials': DEF+'initials',
|
||||||
'internationaliSDNNumber': 'urn:mace:dir:attribute-def:internationaliSDNNumber',
|
'internationaliSDNNumber': DEF+'internationaliSDNNumber',
|
||||||
'janetMailbox': 'urn:mace:dir:attribute-def:janetMailbox',
|
'janetMailbox': DEF+'janetMailbox',
|
||||||
'jpegPhoto': 'urn:mace:dir:attribute-def:jpegPhoto',
|
'jpegPhoto': DEF+'jpegPhoto',
|
||||||
'knowledgeInformation': 'urn:mace:dir:attribute-def:knowledgeInformation',
|
'knowledgeInformation': DEF+'knowledgeInformation',
|
||||||
'l': 'urn:mace:dir:attribute-def:l',
|
'l': DEF+'l',
|
||||||
'labeledURI': 'urn:mace:dir:attribute-def:labeledURI',
|
'labeledURI': DEF+'labeledURI',
|
||||||
'localityName': 'urn:mace:dir:attribute-def:localityName',
|
'localityName': DEF+'localityName',
|
||||||
'mDRecord': 'urn:mace:dir:attribute-def:mDRecord',
|
'mDRecord': DEF+'mDRecord',
|
||||||
'mXRecord': 'urn:mace:dir:attribute-def:mXRecord',
|
'mXRecord': DEF+'mXRecord',
|
||||||
'mail': 'urn:mace:dir:attribute-def:mail',
|
'mail': DEF+'mail',
|
||||||
'mailPreferenceOption': 'urn:mace:dir:attribute-def:mailPreferenceOption',
|
'mailPreferenceOption': DEF+'mailPreferenceOption',
|
||||||
'manager': 'urn:mace:dir:attribute-def:manager',
|
'manager': DEF+'manager',
|
||||||
'member': 'urn:mace:dir:attribute-def:member',
|
'member': DEF+'member',
|
||||||
'mobile': 'urn:mace:dir:attribute-def:mobile',
|
'mobile': DEF+'mobile',
|
||||||
'mobileTelephoneNumber': 'urn:mace:dir:attribute-def:mobileTelephoneNumber',
|
'mobileTelephoneNumber': DEF+'mobileTelephoneNumber',
|
||||||
'nSRecord': 'urn:mace:dir:attribute-def:nSRecord',
|
'nSRecord': DEF+'nSRecord',
|
||||||
'name': 'urn:mace:dir:attribute-def:name',
|
'name': DEF+'name',
|
||||||
'norEduOrgAcronym': 'urn:mace:dir:attribute-def:norEduOrgAcronym',
|
'norEduOrgAcronym': DEF+'norEduOrgAcronym',
|
||||||
'norEduOrgNIN': 'urn:mace:dir:attribute-def:norEduOrgNIN',
|
'norEduOrgNIN': DEF+'norEduOrgNIN',
|
||||||
'norEduOrgSchemaVersion': 'urn:mace:dir:attribute-def:norEduOrgSchemaVersion',
|
'norEduOrgSchemaVersion': DEF+'norEduOrgSchemaVersion',
|
||||||
'norEduOrgUniqueIdentifier': 'urn:mace:dir:attribute-def:norEduOrgUniqueIdentifier',
|
'norEduOrgUniqueIdentifier': DEF+'norEduOrgUniqueIdentifier',
|
||||||
'norEduOrgUniqueNumber': 'urn:mace:dir:attribute-def:norEduOrgUniqueNumber',
|
'norEduOrgUniqueNumber': DEF+'norEduOrgUniqueNumber',
|
||||||
'norEduOrgUnitUniqueIdentifier': 'urn:mace:dir:attribute-def:norEduOrgUnitUniqueIdentifier',
|
'norEduOrgUnitUniqueIdentifier': DEF+'norEduOrgUnitUniqueIdentifier',
|
||||||
'norEduOrgUnitUniqueNumber': 'urn:mace:dir:attribute-def:norEduOrgUnitUniqueNumber',
|
'norEduOrgUnitUniqueNumber': DEF+'norEduOrgUnitUniqueNumber',
|
||||||
'norEduPersonBirthDate': 'urn:mace:dir:attribute-def:norEduPersonBirthDate',
|
'norEduPersonBirthDate': DEF+'norEduPersonBirthDate',
|
||||||
'norEduPersonLIN': 'urn:mace:dir:attribute-def:norEduPersonLIN',
|
'norEduPersonLIN': DEF+'norEduPersonLIN',
|
||||||
'norEduPersonNIN': 'urn:mace:dir:attribute-def:norEduPersonNIN',
|
'norEduPersonNIN': DEF+'norEduPersonNIN',
|
||||||
'o': 'urn:mace:dir:attribute-def:o',
|
'o': DEF+'o',
|
||||||
'objectClass': 'urn:mace:dir:attribute-def:objectClass',
|
'objectClass': DEF+'objectClass',
|
||||||
'organizationName': 'urn:mace:dir:attribute-def:organizationName',
|
'organizationName': DEF+'organizationName',
|
||||||
'organizationalStatus': 'urn:mace:dir:attribute-def:organizationalStatus',
|
'organizationalStatus': DEF+'organizationalStatus',
|
||||||
'organizationalUnitName': 'urn:mace:dir:attribute-def:organizationalUnitName',
|
'organizationalUnitName': DEF+'organizationalUnitName',
|
||||||
'otherMailbox': 'urn:mace:dir:attribute-def:otherMailbox',
|
'otherMailbox': DEF+'otherMailbox',
|
||||||
'ou': 'urn:mace:dir:attribute-def:ou',
|
'ou': DEF+'ou',
|
||||||
'owner': 'urn:mace:dir:attribute-def:owner',
|
'owner': DEF+'owner',
|
||||||
'pager': 'urn:mace:dir:attribute-def:pager',
|
'pager': DEF+'pager',
|
||||||
'pagerTelephoneNumber': 'urn:mace:dir:attribute-def:pagerTelephoneNumber',
|
'pagerTelephoneNumber': DEF+'pagerTelephoneNumber',
|
||||||
'personalSignature': 'urn:mace:dir:attribute-def:personalSignature',
|
'personalSignature': DEF+'personalSignature',
|
||||||
'personalTitle': 'urn:mace:dir:attribute-def:personalTitle',
|
'personalTitle': DEF+'personalTitle',
|
||||||
'photo': 'urn:mace:dir:attribute-def:photo',
|
'photo': DEF+'photo',
|
||||||
'physicalDeliveryOfficeName': 'urn:mace:dir:attribute-def:physicalDeliveryOfficeName',
|
'physicalDeliveryOfficeName': DEF+'physicalDeliveryOfficeName',
|
||||||
'pkcs9email': 'urn:mace:dir:attribute-def:pkcs9email',
|
'pkcs9email': DEF+'pkcs9email',
|
||||||
'postOfficeBox': 'urn:mace:dir:attribute-def:postOfficeBox',
|
'postOfficeBox': DEF+'postOfficeBox',
|
||||||
'postalAddress': 'urn:mace:dir:attribute-def:postalAddress',
|
'postalAddress': DEF+'postalAddress',
|
||||||
'postalCode': 'urn:mace:dir:attribute-def:postalCode',
|
'postalCode': DEF+'postalCode',
|
||||||
'preferredDeliveryMethod': 'urn:mace:dir:attribute-def:preferredDeliveryMethod',
|
'preferredDeliveryMethod': DEF+'preferredDeliveryMethod',
|
||||||
'preferredLanguage': 'urn:mace:dir:attribute-def:preferredLanguage',
|
'preferredLanguage': DEF+'preferredLanguage',
|
||||||
'presentationAddress': 'urn:mace:dir:attribute-def:presentationAddress',
|
'presentationAddress': DEF+'presentationAddress',
|
||||||
'protocolInformation': 'urn:mace:dir:attribute-def:protocolInformation',
|
'protocolInformation': DEF+'protocolInformation',
|
||||||
'pseudonym': 'urn:mace:dir:attribute-def:pseudonym',
|
'pseudonym': DEF+'pseudonym',
|
||||||
'registeredAddress': 'urn:mace:dir:attribute-def:registeredAddress',
|
'registeredAddress': DEF+'registeredAddress',
|
||||||
'rfc822Mailbox': 'urn:mace:dir:attribute-def:rfc822Mailbox',
|
'rfc822Mailbox': DEF+'rfc822Mailbox',
|
||||||
'roleOccupant': 'urn:mace:dir:attribute-def:roleOccupant',
|
'roleOccupant': DEF+'roleOccupant',
|
||||||
'roomNumber': 'urn:mace:dir:attribute-def:roomNumber',
|
'roomNumber': DEF+'roomNumber',
|
||||||
'sOARecord': 'urn:mace:dir:attribute-def:sOARecord',
|
'sOARecord': DEF+'sOARecord',
|
||||||
'searchGuide': 'urn:mace:dir:attribute-def:searchGuide',
|
'searchGuide': DEF+'searchGuide',
|
||||||
'secretary': 'urn:mace:dir:attribute-def:secretary',
|
'secretary': DEF+'secretary',
|
||||||
'seeAlso': 'urn:mace:dir:attribute-def:seeAlso',
|
'seeAlso': DEF+'seeAlso',
|
||||||
'serialNumber': 'urn:mace:dir:attribute-def:serialNumber',
|
'serialNumber': DEF+'serialNumber',
|
||||||
'singleLevelQuality': 'urn:mace:dir:attribute-def:singleLevelQuality',
|
'singleLevelQuality': DEF+'singleLevelQuality',
|
||||||
'sn': 'urn:mace:dir:attribute-def:sn',
|
'sn': DEF+'sn',
|
||||||
'st': 'urn:mace:dir:attribute-def:st',
|
'st': DEF+'st',
|
||||||
'stateOrProvinceName': 'urn:mace:dir:attribute-def:stateOrProvinceName',
|
'stateOrProvinceName': DEF+'stateOrProvinceName',
|
||||||
'street': 'urn:mace:dir:attribute-def:street',
|
'street': DEF+'street',
|
||||||
'streetAddress': 'urn:mace:dir:attribute-def:streetAddress',
|
'streetAddress': DEF+'streetAddress',
|
||||||
'subtreeMaximumQuality': 'urn:mace:dir:attribute-def:subtreeMaximumQuality',
|
'subtreeMaximumQuality': DEF+'subtreeMaximumQuality',
|
||||||
'subtreeMinimumQuality': 'urn:mace:dir:attribute-def:subtreeMinimumQuality',
|
'subtreeMinimumQuality': DEF+'subtreeMinimumQuality',
|
||||||
'supportedAlgorithms': 'urn:mace:dir:attribute-def:supportedAlgorithms',
|
'supportedAlgorithms': DEF+'supportedAlgorithms',
|
||||||
'supportedApplicationContext': 'urn:mace:dir:attribute-def:supportedApplicationContext',
|
'supportedApplicationContext': DEF+'supportedApplicationContext',
|
||||||
'surname': 'urn:mace:dir:attribute-def:surname',
|
'surname': DEF+'surname',
|
||||||
'telephoneNumber': 'urn:mace:dir:attribute-def:telephoneNumber',
|
'telephoneNumber': DEF+'telephoneNumber',
|
||||||
'teletexTerminalIdentifier': 'urn:mace:dir:attribute-def:teletexTerminalIdentifier',
|
'teletexTerminalIdentifier': DEF+'teletexTerminalIdentifier',
|
||||||
'telexNumber': 'urn:mace:dir:attribute-def:telexNumber',
|
'telexNumber': DEF+'telexNumber',
|
||||||
'textEncodedORAddress': 'urn:mace:dir:attribute-def:textEncodedORAddress',
|
'textEncodedORAddress': DEF+'textEncodedORAddress',
|
||||||
'title': 'urn:mace:dir:attribute-def:title',
|
'title': DEF+'title',
|
||||||
'uid': 'urn:mace:dir:attribute-def:uid',
|
'uid': DEF+'uid',
|
||||||
'uniqueIdentifier': 'urn:mace:dir:attribute-def:uniqueIdentifier',
|
'uniqueIdentifier': DEF+'uniqueIdentifier',
|
||||||
'uniqueMember': 'urn:mace:dir:attribute-def:uniqueMember',
|
'uniqueMember': DEF+'uniqueMember',
|
||||||
'userCertificate': 'urn:mace:dir:attribute-def:userCertificate',
|
'userCertificate': DEF+'userCertificate',
|
||||||
'userClass': 'urn:mace:dir:attribute-def:userClass',
|
'userClass': DEF+'userClass',
|
||||||
'userPKCS12': 'urn:mace:dir:attribute-def:userPKCS12',
|
'userPKCS12': DEF+'userPKCS12',
|
||||||
'userPassword': 'urn:mace:dir:attribute-def:userPassword',
|
'userPassword': DEF+'userPassword',
|
||||||
'userSMIMECertificate': 'urn:mace:dir:attribute-def:userSMIMECertificate',
|
'userSMIMECertificate': DEF+'userSMIMECertificate',
|
||||||
'userid': 'urn:mace:dir:attribute-def:userid',
|
'userid': DEF+'userid',
|
||||||
'x121Address': 'urn:mace:dir:attribute-def:x121Address',
|
'x121Address': DEF+'x121Address',
|
||||||
'x500UniqueIdentifier': 'urn:mace:dir:attribute-def:x500UniqueIdentifier',
|
'x500UniqueIdentifier': DEF+'x500UniqueIdentifier',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,104 +1,52 @@
|
|||||||
__author__ = 'rolandh'
|
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.'
|
||||||
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.'
|
||||||
X500ATTR_OID = "urn:oid:2.5.4."
|
NOREDUPERSON_OID = 'urn:oid:1.3.6.1.4.1.2428.90.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.'
|
||||||
NETSCAPE_LDAP = "urn:oid:2.16.840.1.113730.3.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.'
|
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.'
|
||||||
UMICH = "urn:oid:1.3.6.1.4.1.250.1.57."
|
X500ATTR_OID = 'urn:oid:2.5.4.'
|
||||||
SCHAC = "urn:oid:1.3.6.1.4.1.25178.1.2."
|
|
||||||
|
|
||||||
#urn:oid:1.3.6.1.4.1.1466.115.121.1.26
|
|
||||||
|
|
||||||
MAP = {
|
MAP = {
|
||||||
"identifier": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
|
'identifier': 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
|
||||||
"fro": {
|
'fro': {
|
||||||
|
EDUCOURSE_OID+'1': 'eduCourseOffering',
|
||||||
|
EDUCOURSE_OID+'2': 'eduCourseMember',
|
||||||
|
EDUPERSON_OID+'1': 'eduPersonAffiliation',
|
||||||
EDUPERSON_OID+'2': 'eduPersonNickname',
|
EDUPERSON_OID+'2': 'eduPersonNickname',
|
||||||
EDUPERSON_OID+'9': 'eduPersonScopedAffiliation',
|
EDUPERSON_OID+'3': 'eduPersonOrgDN',
|
||||||
EDUPERSON_OID+'11': 'eduPersonAssurance',
|
|
||||||
EDUPERSON_OID+'10': 'eduPersonTargetedID',
|
|
||||||
EDUPERSON_OID+'4': 'eduPersonOrgUnitDN',
|
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+'6': 'norEduOrgAcronym',
|
||||||
NOREDUPERSON_OID+'7': 'norEduOrgUniqueIdentifier',
|
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+'8': 'norEduOrgUnitUniqueIdentifier',
|
||||||
NOREDUPERSON_OID+'9': 'federationFeideSchemaVersion',
|
NOREDUPERSON_OID+'9': 'federationFeideSchemaVersion',
|
||||||
X500ATTR_OID+'53': 'deltaRevocationList',
|
NOREDUPERSON_OID+'10': 'norEduPersonLegalName',
|
||||||
X500ATTR_OID+'52': 'supportedAlgorithms',
|
NOREDUPERSON_OID+'11': 'norEduOrgSchemaVersion',
|
||||||
X500ATTR_OID+'51': 'houseIdentifier',
|
NOREDUPERSON_OID+'12': 'norEduOrgNIN',
|
||||||
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',
|
|
||||||
PKCS_9+'1': 'email',
|
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+'1': 'schacMotherTongue',
|
||||||
SCHAC+'2': 'schacGender',
|
SCHAC+'2': 'schacGender',
|
||||||
SCHAC+'3': 'schacDateOfBirth',
|
SCHAC+'3': 'schacDateOfBirth',
|
||||||
@@ -119,132 +67,177 @@ MAP = {
|
|||||||
SCHAC+'19': 'schacUserStatus',
|
SCHAC+'19': 'schacUserStatus',
|
||||||
SCHAC+'20': 'schacProjectMembership',
|
SCHAC+'20': 'schacProjectMembership',
|
||||||
SCHAC+'21': 'schacProjectSpecificRole',
|
SCHAC+'21': 'schacProjectSpecificRole',
|
||||||
},
|
SIS+'1': 'sisLegalGuardianFor',
|
||||||
"to": {
|
SIS+'2': 'sisSchoolGrade',
|
||||||
'cn': X500ATTR_OID+'3',
|
UCL_DIR_PILOT+'1': 'uid',
|
||||||
'commonName': X500ATTR_OID+'3',
|
UCL_DIR_PILOT+'3': 'mail',
|
||||||
'roleOccupant': X500ATTR_OID+'33',
|
UCL_DIR_PILOT+'25': 'dc',
|
||||||
'gn': X500ATTR_OID+'42',
|
UCL_DIR_PILOT+'37': 'associatedDomain',
|
||||||
'norEduPersonNIN': NOREDUPERSON_OID+'5',
|
UCL_DIR_PILOT+'43': 'co',
|
||||||
'title': X500ATTR_OID+'12',
|
UCL_DIR_PILOT+'60': 'jpegPhoto',
|
||||||
'facsimileTelephoneNumber': X500ATTR_OID+'23',
|
UMICH+'57': 'labeledURI',
|
||||||
'mail': UCL_DIR_PILOT+'3',
|
X500ATTR_OID+'2': 'knowledgeInformation',
|
||||||
'postOfficeBox': X500ATTR_OID+'18',
|
X500ATTR_OID+'3': 'cn',
|
||||||
'fax': X500ATTR_OID+'23',
|
X500ATTR_OID+'4': 'sn',
|
||||||
'telephoneNumber': X500ATTR_OID+'20',
|
X500ATTR_OID+'5': 'serialNumber',
|
||||||
'norEduPersonBirthDate': NOREDUPERSON_OID+'3',
|
X500ATTR_OID+'6': 'c',
|
||||||
'rfc822Mailbox': UCL_DIR_PILOT+'3',
|
X500ATTR_OID+'7': 'l',
|
||||||
'dc': UCL_DIR_PILOT+'25',
|
X500ATTR_OID+'8': 'st',
|
||||||
'countryName': X500ATTR_OID+'6',
|
X500ATTR_OID+'9': 'street',
|
||||||
'emailAddress': PKCS_9+'1',
|
X500ATTR_OID+'10': 'o',
|
||||||
'employeeNumber': NETSCAPE_LDAP+'3',
|
X500ATTR_OID+'11': 'ou',
|
||||||
'organizationName': X500ATTR_OID+'10',
|
X500ATTR_OID+'12': 'title',
|
||||||
'eduPersonAssurance': EDUPERSON_OID+'11',
|
X500ATTR_OID+'14': 'searchGuide',
|
||||||
'norEduOrgAcronym': NOREDUPERSON_OID+'6',
|
X500ATTR_OID+'15': 'businessCategory',
|
||||||
'registeredAddress': X500ATTR_OID+'26',
|
X500ATTR_OID+'16': 'postalAddress',
|
||||||
'physicalDeliveryOfficeName': X500ATTR_OID+'19',
|
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',
|
'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',
|
'authorityRevocationList': X500ATTR_OID+'38',
|
||||||
'displayName': NETSCAPE_LDAP+'241',
|
|
||||||
'businessCategory': X500ATTR_OID+'15',
|
'businessCategory': X500ATTR_OID+'15',
|
||||||
'serialNumber': X500ATTR_OID+'5',
|
'c': X500ATTR_OID+'6',
|
||||||
'norEduOrgUniqueIdentifier': NOREDUPERSON_OID+'7',
|
'cACertificate': X500ATTR_OID+'37',
|
||||||
'st': X500ATTR_OID+'8',
|
|
||||||
'carLicense': NETSCAPE_LDAP+'1',
|
'carLicense': NETSCAPE_LDAP+'1',
|
||||||
'presentationAddress': X500ATTR_OID+'29',
|
'certificateRevocationList': X500ATTR_OID+'39',
|
||||||
'sn': X500ATTR_OID+'4',
|
'cn': X500ATTR_OID+'3',
|
||||||
'domainComponent': UCL_DIR_PILOT+'25',
|
|
||||||
'labeledURI': UMICH+'57',
|
|
||||||
'uid': UCL_DIR_PILOT+'1',
|
|
||||||
'co': UCL_DIR_PILOT+'43',
|
'co': UCL_DIR_PILOT+'43',
|
||||||
'friendlyCountryName': UCL_DIR_PILOT+'43',
|
'crossCertificatePair': X500ATTR_OID+'40',
|
||||||
'schacMotherTongue':SCHAC+'1',
|
'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',
|
'schacGender': SCHAC+'2',
|
||||||
'schacDateOfBirth':SCHAC+'3',
|
|
||||||
'schacPlaceOfBirth': SCHAC+'4',
|
|
||||||
'schacCountryOfCitizenship':SCHAC+'5',
|
|
||||||
'schacSn1': SCHAC+'6',
|
|
||||||
'schacSn2': SCHAC+'7',
|
|
||||||
'schacPersonalTitle':SCHAC+'8',
|
|
||||||
'schacHomeOrganization': SCHAC+'9',
|
'schacHomeOrganization': SCHAC+'9',
|
||||||
'schacHomeOrganizationType': SCHAC+'10',
|
'schacHomeOrganizationType': SCHAC+'10',
|
||||||
'schacCountryOfResidence': SCHAC+'11',
|
'schacMotherTongue': SCHAC+'1',
|
||||||
'schacUserPresenceID': SCHAC+'12',
|
|
||||||
'schacPersonalPosition': SCHAC+'13',
|
'schacPersonalPosition': SCHAC+'13',
|
||||||
|
'schacPersonalTitle': SCHAC+'8',
|
||||||
'schacPersonalUniqueCode': SCHAC+'14',
|
'schacPersonalUniqueCode': SCHAC+'14',
|
||||||
'schacPersonalUniqueID': SCHAC+'15',
|
'schacPersonalUniqueID': SCHAC+'15',
|
||||||
'schacExpiryDate': SCHAC+'17',
|
'schacPlaceOfBirth': SCHAC+'4',
|
||||||
'schacUserPrivateAttribute': SCHAC+'18',
|
|
||||||
'schacUserStatus': SCHAC+'19',
|
|
||||||
'schacProjectMembership': SCHAC+'20',
|
'schacProjectMembership': SCHAC+'20',
|
||||||
'schacProjectSpecificRole': SCHAC+'21',
|
'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',
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,73 +1,82 @@
|
|||||||
EDUPERSON_OID = "urn:oid:1.3.6.1.4.1.5923.1.1.1."
|
EDUPERSON_OID = 'urn:oid:1.3.6.1.4.1.5923.1.1.1.'
|
||||||
X500ATTR = "urn:oid:2.5.4."
|
NETSCAPE_LDAP = 'urn:oid:2.16.840.1.113730.3.1.'
|
||||||
NOREDUPERSON_OID = "urn:oid:1.3.6.1.4.1.2428.90.1."
|
NOREDUPERSON_OID = 'urn:oid:1.3.6.1.4.1.2428.90.1.'
|
||||||
NETSCAPE_LDAP = "urn:oid:2.16.840.1.113730.3.1."
|
PKCS_9 = 'urn:oid:1.2.840.113549.1.9.'
|
||||||
UCL_DIR_PILOT = "urn:oid:0.9.2342.19200300.100.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.'
|
||||||
UMICH = "urn:oid:1.3.6.1.4.1.250.1.57."
|
X500ATTR = 'urn:oid:2.5.4.'
|
||||||
|
|
||||||
|
|
||||||
MAP = {
|
MAP = {
|
||||||
"identifier": "urn:mace:shibboleth:1.0:attributeNamespace:uri",
|
"identifier": "urn:mace:shibboleth:1.0:attributeNamespace:uri",
|
||||||
"fro": {
|
'fro': {
|
||||||
|
EDUPERSON_OID+'1': 'eduPersonAffiliation',
|
||||||
EDUPERSON_OID+'2': 'eduPersonNickname',
|
EDUPERSON_OID+'2': 'eduPersonNickname',
|
||||||
EDUPERSON_OID+'9': 'eduPersonScopedAffiliation',
|
EDUPERSON_OID+'3': 'eduPersonOrgDN',
|
||||||
EDUPERSON_OID+'11': 'eduPersonAssurance',
|
|
||||||
EDUPERSON_OID+'10': 'eduPersonTargetedID',
|
|
||||||
EDUPERSON_OID+'4': 'eduPersonOrgUnitDN',
|
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+'6': 'norEduOrgAcronym',
|
||||||
NOREDUPERSON_OID+'7': 'norEduOrgUniqueIdentifier',
|
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+'8': 'norEduOrgUnitUniqueIdentifier',
|
||||||
NOREDUPERSON_OID+'9': 'federationFeideSchemaVersion',
|
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',
|
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+'3': 'mail',
|
||||||
UCL_DIR_PILOT+'25': 'dc',
|
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+'40': 'crossCertificatePair',
|
||||||
X500ATTR+'42': 'givenName',
|
X500ATTR+'42': 'givenName',
|
||||||
X500ATTR+'43': 'initials',
|
X500ATTR+'43': 'initials',
|
||||||
@@ -76,115 +85,107 @@ MAP = {
|
|||||||
X500ATTR+'46': 'dnQualifier',
|
X500ATTR+'46': 'dnQualifier',
|
||||||
X500ATTR+'47': 'enhancedSearchGuide',
|
X500ATTR+'47': 'enhancedSearchGuide',
|
||||||
X500ATTR+'48': 'protocolInformation',
|
X500ATTR+'48': 'protocolInformation',
|
||||||
|
X500ATTR+'50': 'uniqueMember',
|
||||||
|
X500ATTR+'51': 'houseIdentifier',
|
||||||
|
X500ATTR+'52': 'supportedAlgorithms',
|
||||||
|
X500ATTR+'53': 'deltaRevocationList',
|
||||||
X500ATTR+'54': 'dmdName',
|
X500ATTR+'54': 'dmdName',
|
||||||
NETSCAPE_LDAP+'4': 'employeeType',
|
X500ATTR+'65': 'pseudonym',
|
||||||
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',
|
|
||||||
},
|
},
|
||||||
"to":{
|
'to': {
|
||||||
'roleOccupant': X500ATTR+'33',
|
'associatedDomain': UCL_DIR_PILOT+'37',
|
||||||
'gn': X500ATTR+'42',
|
'authorityRevocationList': X500ATTR+'38',
|
||||||
'norEduPersonNIN': NOREDUPERSON_OID+'5',
|
'businessCategory': X500ATTR+'15',
|
||||||
'title': X500ATTR+'12',
|
'c': X500ATTR+'6',
|
||||||
'facsimileTelephoneNumber': X500ATTR+'23',
|
'cACertificate': X500ATTR+'37',
|
||||||
'mail': UCL_DIR_PILOT+'3',
|
'carLicense': NETSCAPE_LDAP+'1',
|
||||||
'postOfficeBox': X500ATTR+'18',
|
'certificateRevocationList': X500ATTR+'39',
|
||||||
'fax': X500ATTR+'23',
|
|
||||||
'telephoneNumber': X500ATTR+'20',
|
|
||||||
'norEduPersonBirthDate': NOREDUPERSON_OID+'3',
|
|
||||||
'rfc822Mailbox': UCL_DIR_PILOT+'3',
|
|
||||||
'dc': UCL_DIR_PILOT+'25',
|
|
||||||
'countryName': X500ATTR+'6',
|
'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',
|
'emailAddress': PKCS_9+'1',
|
||||||
'employeeNumber': NETSCAPE_LDAP+'3',
|
'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',
|
'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',
|
'member': X500ATTR+'31',
|
||||||
'streetAddress': X500ATTR+'9',
|
'norEduOrgAcronym': NOREDUPERSON_OID+'6',
|
||||||
'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',
|
|
||||||
'norEduOrgUniqueIdentifier': NOREDUPERSON_OID+'7',
|
'norEduOrgUniqueIdentifier': NOREDUPERSON_OID+'7',
|
||||||
'st': X500ATTR+'8',
|
'norEduOrgUniqueNumber': NOREDUPERSON_OID+'1',
|
||||||
'carLicense': NETSCAPE_LDAP+'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',
|
'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',
|
'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',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -269,7 +269,8 @@ class Config(object):
|
|||||||
acs = ac_factory()
|
acs = ac_factory()
|
||||||
|
|
||||||
if not acs:
|
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)
|
_acs = self.getattr("attribute_converters", typ)
|
||||||
if _acs:
|
if _acs:
|
||||||
|
|||||||
@@ -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 class_instances_from_soap_enveloped_saml_thingies
|
||||||
from saml2.soap import open_soap_envelope
|
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 saml
|
||||||
from saml2 import response
|
from saml2 import response as saml_response
|
||||||
from saml2 import BINDING_URI
|
from saml2 import BINDING_URI
|
||||||
from saml2 import BINDING_HTTP_ARTIFACT
|
from saml2 import BINDING_HTTP_ARTIFACT
|
||||||
from saml2 import BINDING_PAOS
|
from saml2 import BINDING_PAOS
|
||||||
from saml2 import request
|
from saml2 import request as saml_request
|
||||||
from saml2 import soap
|
from saml2 import soap
|
||||||
from saml2 import element_to_extension_element
|
from saml2 import element_to_extension_element
|
||||||
from saml2 import extension_elements_to_elements
|
from saml2 import extension_elements_to_elements
|
||||||
@@ -296,7 +298,16 @@ class Entity(HTTPBase):
|
|||||||
|
|
||||||
return info
|
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)
|
#logger.debug("unravel '%s'" % txt)
|
||||||
if binding not in [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST,
|
if binding not in [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST,
|
||||||
BINDING_SOAP, BINDING_URI, BINDING_HTTP_ARTIFACT,
|
BINDING_SOAP, BINDING_URI, BINDING_HTTP_ARTIFACT,
|
||||||
@@ -309,7 +320,8 @@ class Entity(HTTPBase):
|
|||||||
elif binding == BINDING_HTTP_POST:
|
elif binding == BINDING_HTTP_POST:
|
||||||
xmlstr = base64.b64decode(txt)
|
xmlstr = base64.b64decode(txt)
|
||||||
elif binding == BINDING_SOAP:
|
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)
|
xmlstr = func(txt)
|
||||||
elif binding == BINDING_HTTP_ARTIFACT:
|
elif binding == BINDING_HTTP_ARTIFACT:
|
||||||
xmlstr = base64.b64decode(txt)
|
xmlstr = base64.b64decode(txt)
|
||||||
@@ -320,7 +332,8 @@ class Entity(HTTPBase):
|
|||||||
|
|
||||||
return xmlstr
|
return xmlstr
|
||||||
|
|
||||||
def parse_soap_message(self, text):
|
@staticmethod
|
||||||
|
def parse_soap_message(text):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
:param text: The SOAP message
|
:param text: The SOAP message
|
||||||
@@ -330,7 +343,8 @@ class Entity(HTTPBase):
|
|||||||
ecp,
|
ecp,
|
||||||
samlp])
|
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
|
Picks out the parts of the SOAP message, body and headers apart
|
||||||
:param text: The SOAP message
|
:param text: The SOAP message
|
||||||
@@ -438,7 +452,8 @@ class Entity(HTTPBase):
|
|||||||
msg.extension_elements = extensions
|
msg.extension_elements = extensions
|
||||||
|
|
||||||
def _response(self, in_response_to, consumer_url=None, status=None,
|
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.
|
""" Create a Response.
|
||||||
|
|
||||||
:param in_response_to: The session identifier of the request
|
:param in_response_to: The session identifier of the request
|
||||||
@@ -471,10 +486,13 @@ class Entity(HTTPBase):
|
|||||||
if encrypt_assertion:
|
if encrypt_assertion:
|
||||||
sign_class = [(class_name(response), response.id)]
|
sign_class = [(class_name(response), response.id)]
|
||||||
if sign:
|
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)
|
cbxs = CryptoBackendXmlSec1(self.config.xmlsec_binary)
|
||||||
_, cert_file = make_temp("%s" % encrypt_cert, decode=False)
|
_, 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:
|
if sign:
|
||||||
return signed_instance_factory(response, self.sec, sign_class)
|
return signed_instance_factory(response, self.sec, sign_class)
|
||||||
else:
|
else:
|
||||||
@@ -520,13 +538,14 @@ class Entity(HTTPBase):
|
|||||||
|
|
||||||
# ------------------------------------------------------------------------
|
# ------------------------------------------------------------------------
|
||||||
|
|
||||||
def srv2typ(self, service):
|
@staticmethod
|
||||||
|
def srv2typ(service):
|
||||||
for typ in ["aa", "pdp", "aq"]:
|
for typ in ["aa", "pdp", "aq"]:
|
||||||
if service in ENDPOINTS[typ]:
|
if service in ENDPOINTS[typ]:
|
||||||
if typ == "aa":
|
if typ == "aa":
|
||||||
return "attribute_authority"
|
return "attribute_authority"
|
||||||
elif typ == "aq":
|
elif typ == "aq":
|
||||||
return "authn_authority"
|
return "authn_authority"
|
||||||
else:
|
else:
|
||||||
return typ
|
return typ
|
||||||
|
|
||||||
@@ -570,12 +589,14 @@ class Entity(HTTPBase):
|
|||||||
origdoc = xmlstr
|
origdoc = xmlstr
|
||||||
xmlstr = self.unravel(xmlstr, binding, request_cls.msgtype)
|
xmlstr = self.unravel(xmlstr, binding, request_cls.msgtype)
|
||||||
must = self.config.getattr("want_authn_requests_signed", "idp")
|
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:
|
if only_valid_cert is None:
|
||||||
only_valid_cert = False
|
only_valid_cert = False
|
||||||
if only_valid_cert:
|
if only_valid_cert:
|
||||||
must = True
|
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")
|
_log_debug("Loaded request")
|
||||||
|
|
||||||
@@ -674,14 +695,14 @@ class Entity(HTTPBase):
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def create_artifact_resolve(self, artifact, destination, sid, consent=None,
|
def create_artifact_resolve(self, artifact, destination, sessid,
|
||||||
extensions=None, sign=False):
|
consent=None, extensions=None, sign=False):
|
||||||
"""
|
"""
|
||||||
Create a ArtifactResolve request
|
Create a ArtifactResolve request
|
||||||
|
|
||||||
:param artifact:
|
:param artifact:
|
||||||
:param destination:
|
:param destination:
|
||||||
:param sid: session id
|
:param sessid: session id
|
||||||
:param consent:
|
:param consent:
|
||||||
:param extensions:
|
:param extensions:
|
||||||
:param sign:
|
:param sign:
|
||||||
@@ -690,7 +711,7 @@ class Entity(HTTPBase):
|
|||||||
|
|
||||||
artifact = Artifact(text=artifact)
|
artifact = Artifact(text=artifact)
|
||||||
|
|
||||||
return self._message(ArtifactResolve, destination, sid,
|
return self._message(ArtifactResolve, destination, sessid,
|
||||||
consent, extensions, sign, artifact=artifact)
|
consent, extensions, sign, artifact=artifact)
|
||||||
|
|
||||||
def create_artifact_response(self, request, artifact, bindings=None,
|
def create_artifact_response(self, request, artifact, bindings=None,
|
||||||
@@ -763,7 +784,7 @@ class Entity(HTTPBase):
|
|||||||
was not.
|
was not.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self._parse_request(xmlstr, request.ManageNameIDRequest,
|
return self._parse_request(xmlstr, saml_request.ManageNameIDRequest,
|
||||||
"manage_name_id_service", binding)
|
"manage_name_id_service", binding)
|
||||||
|
|
||||||
def create_manage_name_id_response(self, request, bindings=None,
|
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,
|
def parse_manage_name_id_request_response(self, string,
|
||||||
binding=BINDING_SOAP):
|
binding=BINDING_SOAP):
|
||||||
return self._parse_response(string, response.ManageNameIDResponse,
|
return self._parse_response(string, saml_response.ManageNameIDResponse,
|
||||||
"manage_name_id_service", binding)
|
"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
|
""" Deal with a Response
|
||||||
|
|
||||||
:param xmlstr: The response as a xml string
|
:param xmlstr: The response as a xml string
|
||||||
:param response_cls: What type of response it is
|
:param response_cls: What type of response it is
|
||||||
:param binding: What type of binding this message came through.
|
: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
|
:param kwargs: Extra key word arguments
|
||||||
:return: None if the reply doesn't contain a valid SAML Response,
|
:return: None if the reply doesn't contain a valid SAML Response,
|
||||||
otherwise the response.
|
otherwise the response.
|
||||||
@@ -800,20 +825,20 @@ class Entity(HTTPBase):
|
|||||||
response = None
|
response = None
|
||||||
|
|
||||||
if self.config.accepted_time_diff:
|
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 "asynchop" not in kwargs:
|
||||||
if binding in [BINDING_SOAP, BINDING_PAOS]:
|
if binding in [BINDING_SOAP, BINDING_PAOS]:
|
||||||
kwargs["asynchop"] = False
|
asynchop = False
|
||||||
else:
|
else:
|
||||||
kwargs["asynchop"] = True
|
asynchop = True
|
||||||
|
|
||||||
if xmlstr:
|
if xmlstr:
|
||||||
if "return_addrs" not in kwargs:
|
if "return_addrs" not in kwargs:
|
||||||
if binding in [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST]:
|
if binding in [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST]:
|
||||||
try:
|
try:
|
||||||
# expected return address
|
# expected return address
|
||||||
kwargs["return_addrs"] = self.config.endpoint(
|
return_addrs = self.config.endpoint(
|
||||||
service, binding=binding)
|
service, binding=binding)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.info("Not supposed to handle this!")
|
logger.info("Not supposed to handle this!")
|
||||||
@@ -827,12 +852,6 @@ class Entity(HTTPBase):
|
|||||||
|
|
||||||
xmlstr = self.unravel(xmlstr, binding, response_cls.msgtype)
|
xmlstr = self.unravel(xmlstr, binding, response_cls.msgtype)
|
||||||
origxml = xmlstr
|
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
|
if not xmlstr: # Not a valid reponse
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -851,16 +870,14 @@ class Entity(HTTPBase):
|
|||||||
|
|
||||||
logger.debug("XMLSTR: %s" % xmlstr)
|
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:
|
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:
|
if not response:
|
||||||
return None
|
return None
|
||||||
@@ -887,7 +904,7 @@ class Entity(HTTPBase):
|
|||||||
was not.
|
was not.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self._parse_request(xmlstr, request.LogoutRequest,
|
return self._parse_request(xmlstr, saml_request.LogoutRequest,
|
||||||
"single_logout_service", binding)
|
"single_logout_service", binding)
|
||||||
|
|
||||||
def use_artifact(self, message, endpoint_index=0):
|
def use_artifact(self, message, endpoint_index=0):
|
||||||
@@ -964,7 +981,7 @@ class Entity(HTTPBase):
|
|||||||
kwargs = {"entity_id": self.config.entityid,
|
kwargs = {"entity_id": self.config.entityid,
|
||||||
"attribute_converters": self.config.attribute_converters}
|
"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,
|
"artifact_resolve", BINDING_SOAP,
|
||||||
**kwargs)
|
**kwargs)
|
||||||
# should just be one
|
# should just be one
|
||||||
|
|||||||
@@ -775,37 +775,58 @@ class MetadataStore(object):
|
|||||||
return [m["text"] for m in ad["affiliate_member"]]
|
return [m["text"] for m in ad["affiliate_member"]]
|
||||||
|
|
||||||
def entity_categories(self, entity_id):
|
def entity_categories(self, entity_id):
|
||||||
ent = self.__getitem__(entity_id)
|
"""
|
||||||
res = []
|
Get a list of entity categories for an entity id.
|
||||||
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"]])
|
|
||||||
|
|
||||||
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):
|
def supported_entity_categories(self, entity_id):
|
||||||
ent = self.__getitem__(entity_id)
|
"""
|
||||||
res = []
|
Get a list of entity category support for an entity id.
|
||||||
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"]])
|
|
||||||
|
|
||||||
|
: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
|
return res
|
||||||
|
|
||||||
def bindings(self, entity_id, typ, service):
|
def bindings(self, entity_id, typ, service):
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ import xmldsig as ds
|
|||||||
import xmlenc as xenc
|
import xmlenc as xenc
|
||||||
|
|
||||||
from saml2 import samlp
|
from saml2 import samlp
|
||||||
|
from saml2 import class_name
|
||||||
from saml2 import saml
|
from saml2 import saml
|
||||||
from saml2 import extension_element_to_element
|
|
||||||
from saml2 import extension_elements_to_elements
|
from saml2 import extension_elements_to_elements
|
||||||
from saml2 import SAMLError
|
from saml2 import SAMLError
|
||||||
from saml2 import time_util
|
from saml2 import time_util
|
||||||
@@ -387,7 +387,7 @@ class StatusResponse(object):
|
|||||||
|
|
||||||
if self.asynchop:
|
if self.asynchop:
|
||||||
if self.response.destination and \
|
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,
|
logger.error("%s not in %s" % (self.response.destination,
|
||||||
self.return_addrs))
|
self.return_addrs))
|
||||||
return None
|
return None
|
||||||
@@ -399,7 +399,7 @@ class StatusResponse(object):
|
|||||||
def loads(self, xmldata, decode=True, origxml=None):
|
def loads(self, xmldata, decode=True, origxml=None):
|
||||||
return self._loads(xmldata, decode, origxml)
|
return self._loads(xmldata, decode, origxml)
|
||||||
|
|
||||||
def verify(self):
|
def verify(self, key_file=""):
|
||||||
try:
|
try:
|
||||||
return self._verify()
|
return self._verify()
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
@@ -473,6 +473,7 @@ class AuthnResponse(StatusResponse):
|
|||||||
self.came_from = ""
|
self.came_from = ""
|
||||||
self.ava = None
|
self.ava = None
|
||||||
self.assertion = None
|
self.assertion = None
|
||||||
|
self.assertions = []
|
||||||
self.session_not_on_or_after = 0
|
self.session_not_on_or_after = 0
|
||||||
self.allow_unsolicited = allow_unsolicited
|
self.allow_unsolicited = allow_unsolicited
|
||||||
self.require_signature = want_assertions_signed
|
self.require_signature = want_assertions_signed
|
||||||
@@ -739,8 +740,13 @@ class AuthnResponse(StatusResponse):
|
|||||||
return self.name_id
|
return self.name_id
|
||||||
|
|
||||||
def _assertion(self, assertion):
|
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 context: %s" % (self.context,))
|
||||||
logger.debug("assertion keys: %s" % (assertion.keyswv()))
|
logger.debug("assertion keys: %s" % (assertion.keyswv()))
|
||||||
logger.debug("outstanding_queries: %s" % (self.outstanding_queries,))
|
logger.debug("outstanding_queries: %s" % (self.outstanding_queries,))
|
||||||
@@ -773,54 +779,61 @@ class AuthnResponse(StatusResponse):
|
|||||||
logger.exception("get subject")
|
logger.exception("get subject")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def _encrypted_assertion(self, xmlstr):
|
def decrypt_assertions(self, encrypted_assertions, key_file=""):
|
||||||
if xmlstr.encrypted_data:
|
res = []
|
||||||
assertion_str = self.sec.decrypt(xmlstr.encrypted_data.to_string())
|
for encrypted_assertion in encrypted_assertions:
|
||||||
if not assertion_str:
|
if encrypted_assertion.extension_elements:
|
||||||
raise DecryptionFailed()
|
assertions = extension_elements_to_elements(
|
||||||
assertion = saml.assertion_from_string(assertion_str)
|
encrypted_assertion.extension_elements, [saml, samlp])
|
||||||
else:
|
for assertion in assertions:
|
||||||
decrypt_xml = self.sec.decrypt(xmlstr)
|
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")
|
def parse_assertion(self, key_file=""):
|
||||||
|
|
||||||
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):
|
|
||||||
if self.context == "AuthnQuery":
|
if self.context == "AuthnQuery":
|
||||||
# can contain one or more assertions
|
# can contain one or more assertions
|
||||||
pass
|
pass
|
||||||
else: # This is a saml2int limitation
|
else: # This is a saml2int limitation
|
||||||
try:
|
try:
|
||||||
assert len(self.response.assertion) == 1 or \
|
assert len(self.response.assertion) == 1 or \
|
||||||
len(self.response.encrypted_assertion) == 1
|
len(self.response.encrypted_assertion) == 1
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
raise Exception("No assertion part")
|
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:
|
if self.response.assertion:
|
||||||
logger.debug("***Unencrypted response***")
|
logger.debug("***Unencrypted assertion***")
|
||||||
for assertion in self.response.assertion:
|
for assertion in self.response.assertion:
|
||||||
if not self._assertion(assertion):
|
if not self._assertion(assertion):
|
||||||
return False
|
return False
|
||||||
return True
|
else:
|
||||||
else:
|
self.assertions.append(assertion)
|
||||||
logger.debug("***Encrypted response***")
|
self.assertion = self.assertions[0]
|
||||||
for assertion in self.response.encrypted_assertion:
|
|
||||||
if not self._encrypted_assertion(assertion):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def verify(self):
|
return True
|
||||||
|
|
||||||
|
def verify(self, key_file=""):
|
||||||
""" Verify that the assertion is syntactically correct and
|
""" 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:
|
try:
|
||||||
self._verify()
|
self._verify()
|
||||||
@@ -830,7 +843,7 @@ class AuthnResponse(StatusResponse):
|
|||||||
if not isinstance(self.response, samlp.Response):
|
if not isinstance(self.response, samlp.Response):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
if self.parse_assertion():
|
if self.parse_assertion(key_file):
|
||||||
return self
|
return self
|
||||||
else:
|
else:
|
||||||
logger.error("Could not parse the assertion")
|
logger.error("Could not parse the assertion")
|
||||||
@@ -1056,7 +1069,7 @@ class AssertionIDResponse(object):
|
|||||||
|
|
||||||
return self._postamble()
|
return self._postamble()
|
||||||
|
|
||||||
def verify(self):
|
def verify(self, key_file=""):
|
||||||
try:
|
try:
|
||||||
valid_instance(self.response)
|
valid_instance(self.response)
|
||||||
except NotValid, exc:
|
except NotValid, exc:
|
||||||
|
|||||||
@@ -246,6 +246,8 @@ class AttributeValueBase(SamlBase):
|
|||||||
# clear type
|
# clear type
|
||||||
#self.clear_type()
|
#self.clear_type()
|
||||||
self.set_text(tree.text)
|
self.set_text(tree.text)
|
||||||
|
if XSI_NIL in self.extension_attributes:
|
||||||
|
del self.extension_attributes[XSI_NIL]
|
||||||
try:
|
try:
|
||||||
typ = self.extension_attributes[XSI_TYPE]
|
typ = self.extension_attributes[XSI_TYPE]
|
||||||
_verify_value_type(typ, getattr(self, "text"))
|
_verify_value_type(typ, getattr(self, "text"))
|
||||||
|
|||||||
@@ -35,12 +35,13 @@ from Crypto.PublicKey import RSA
|
|||||||
from saml2.cert import OpenSSLWrapper
|
from saml2.cert import OpenSSLWrapper
|
||||||
from saml2.extension import pefim
|
from saml2.extension import pefim
|
||||||
from saml2.saml import EncryptedAssertion
|
from saml2.saml import EncryptedAssertion
|
||||||
from saml2.samlp import Response, response_from_string
|
from saml2.samlp import Response
|
||||||
|
|
||||||
import xmldsig as ds
|
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 class_name
|
||||||
from saml2 import saml
|
from saml2 import saml
|
||||||
from saml2 import ExtensionElement
|
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"
|
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"
|
TRIPLE_DES_CBC = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"
|
||||||
XMLTAG = "<?xml version='1.0'?>"
|
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):
|
class SigverError(SAMLError):
|
||||||
@@ -125,8 +127,12 @@ def rm_xmltag(statement):
|
|||||||
statement = statement[len(XMLTAG):]
|
statement = statement[len(XMLTAG):]
|
||||||
if statement[0] == '\n':
|
if statement[0] == '\n':
|
||||||
statement = statement[1:]
|
statement = statement[1:]
|
||||||
elif statement.startswith(PREFIX):
|
elif statement.startswith(PREFIX1):
|
||||||
statement = statement[len(PREFIX):]
|
statement = statement[len(PREFIX1):]
|
||||||
|
if statement[0] == '\n':
|
||||||
|
statement = statement[1:]
|
||||||
|
elif statement.startswith(PREFIX2):
|
||||||
|
statement = statement[len(PREFIX2):]
|
||||||
if statement[0] == '\n':
|
if statement[0] == '\n':
|
||||||
statement = statement[1:]
|
statement = statement[1:]
|
||||||
|
|
||||||
@@ -693,7 +699,7 @@ class CryptoBackend():
|
|||||||
def encrypt(self, text, recv_key, template, key_type):
|
def encrypt(self, text, recv_key, template, key_type):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def encrypt_assertion(self, statement, recv_key, key_type):
|
def encrypt_assertion(self, statement, recv_key, key_type, xpath=""):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def decrypt(self, enctext, key_file):
|
def decrypt(self, enctext, key_file):
|
||||||
@@ -733,12 +739,25 @@ class CryptoBackendXmlSec1(CryptoBackend):
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
return ""
|
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))
|
logger.debug("Encryption input len: %d" % len(text))
|
||||||
_, fil = make_temp("%s" % text, decode=False)
|
_, fil = make_temp("%s" % text, decode=False)
|
||||||
|
|
||||||
com_list = [self.xmlsec, "--encrypt", "--pubkey-cert-pem", recv_key,
|
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],
|
(_stdout, _stderr, output) = self._run_xmlsec(com_list, [template],
|
||||||
exception=DecryptError,
|
exception=DecryptError,
|
||||||
@@ -767,7 +786,8 @@ class CryptoBackendXmlSec1(CryptoBackend):
|
|||||||
"--session-key", key_type, "--xml-data", fil,
|
"--session-key", key_type, "--xml-data", fil,
|
||||||
"--node-xpath", ASSERT_XPATH]
|
"--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)
|
os.unlink(fil)
|
||||||
if not output:
|
if not output:
|
||||||
@@ -1206,7 +1226,7 @@ class SecurityContext(object):
|
|||||||
|
|
||||||
:param text: Text to encrypt
|
:param text: Text to encrypt
|
||||||
:param recv_key: A file containing the receivers public key
|
: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
|
:param key_type: The type of session key to use
|
||||||
:result: An encrypted XML text
|
:result: An encrypted XML text
|
||||||
"""
|
"""
|
||||||
@@ -1262,8 +1282,7 @@ class SecurityContext(object):
|
|||||||
return self.crypto.validate_signature(signedtext, cert_file=cert_file,
|
return self.crypto.validate_signature(signedtext, cert_file=cert_file,
|
||||||
cert_type=cert_type,
|
cert_type=cert_type,
|
||||||
node_name=node_name,
|
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,
|
def _check_signature(self, decoded_xml, item, node_name=NODE_NAME,
|
||||||
origdoc=None, id_attr="", must=False,
|
origdoc=None, id_attr="", must=False,
|
||||||
@@ -1735,7 +1754,10 @@ def pre_encrypt_assertion(response):
|
|||||||
assertion = response.assertion
|
assertion = response.assertion
|
||||||
response.assertion = None
|
response.assertion = None
|
||||||
response.encrypted_assertion = EncryptedAssertion()
|
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
|
# txt = "%s" % response
|
||||||
# _ass = "%s" % assertion
|
# _ass = "%s" % assertion
|
||||||
# _ass = rm_xmltag(_ass)
|
# _ass = rm_xmltag(_ass)
|
||||||
|
|||||||
@@ -81,7 +81,8 @@ def parse_duration(duration):
|
|||||||
assert duration[index] == "P"
|
assert duration[index] == "P"
|
||||||
index += 1
|
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:
|
for code, typ in D_FORMAT:
|
||||||
#print duration[index:], code
|
#print duration[index:], code
|
||||||
@@ -94,25 +95,36 @@ def parse_duration(duration):
|
|||||||
raise Exception("Not allowed to end with 'T'")
|
raise Exception("Not allowed to end with 'T'")
|
||||||
else:
|
else:
|
||||||
raise Exception("Missing T")
|
raise Exception("Missing T")
|
||||||
|
elif duration[index] == "T":
|
||||||
|
continue
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
mod = duration[index:].index(code)
|
mod = duration[index:].index(code)
|
||||||
|
_val = duration[index:index + mod]
|
||||||
try:
|
try:
|
||||||
dic[typ] = int(duration[index:index + mod])
|
dic[typ] = int(_val)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
if code == "S":
|
# smallest value used may also have a decimal fraction
|
||||||
|
if mod + index + 1 == dlen:
|
||||||
try:
|
try:
|
||||||
dic[typ] = float(duration[index:index + mod])
|
dic[typ] = float(_val)
|
||||||
except ValueError:
|
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:
|
else:
|
||||||
raise Exception(
|
raise ValueError(
|
||||||
"Fractions not allow on anything byt seconds")
|
"Fraction not allowed on other than smallest value")
|
||||||
index = mod + index + 1
|
index = mod + index + 1
|
||||||
except ValueError:
|
except ValueError:
|
||||||
dic[typ] = 0
|
dic[typ] = 0
|
||||||
|
|
||||||
if index == len(duration):
|
if index == dlen:
|
||||||
break
|
break
|
||||||
|
|
||||||
return sign, dic
|
return sign, dic
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ from pathutils import full_path
|
|||||||
from pathutils import xmlsec_path
|
from pathutils import xmlsec_path
|
||||||
|
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"entityid" : "urn:mace:example.com:saml:roland:sp",
|
"entityid": "urn:mace:example.com:saml:roland:sp",
|
||||||
"name" : "urn:mace:example.com:saml:roland:sp",
|
"name": "urn:mace:example.com:saml:roland:sp",
|
||||||
"description": "My own SP",
|
"description": "My own SP",
|
||||||
"service": {
|
"service": {
|
||||||
"sp": {
|
"sp": {
|
||||||
@@ -25,14 +25,14 @@ CONFIG = {
|
|||||||
"local": [full_path("idp.xml"), full_path("vo_metadata.xml")],
|
"local": [full_path("idp.xml"), full_path("vo_metadata.xml")],
|
||||||
},
|
},
|
||||||
"virtual_organization": {
|
"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",
|
"nameid_format": "urn:oid:1.3.6.1.4.1.1466.115.121.1.15-NameID",
|
||||||
"common_identifier": "umuselin",
|
"common_identifier": "umuselin",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"subject_data": "subject_data.db",
|
"subject_data": "subject_data.db",
|
||||||
"accepted_time_diff": 60,
|
"accepted_time_diff": 60,
|
||||||
"attribute_map_dir" : full_path("attributemaps"),
|
"attribute_map_dir": full_path("attributemaps"),
|
||||||
"valid_for": 6,
|
"valid_for": 6,
|
||||||
"organization": {
|
"organization": {
|
||||||
"name": ("AB Exempel", "se"),
|
"name": ("AB Exempel", "se"),
|
||||||
@@ -40,12 +40,13 @@ CONFIG = {
|
|||||||
"url": "http://www.example.org",
|
"url": "http://www.example.org",
|
||||||
},
|
},
|
||||||
"contact_person": [{
|
"contact_person": [{
|
||||||
"given_name": "Roland",
|
"given_name": "Roland",
|
||||||
"sur_name": "Hedberg",
|
"sur_name": "Hedberg",
|
||||||
"telephone_number": "+46 70 100 0000",
|
"telephone_number": "+46 70 100 0000",
|
||||||
"email_address": ["tech@eample.com", "tech@example.org"],
|
"email_address": ["tech@eample.com",
|
||||||
"contact_type": "technical"
|
"tech@example.org"],
|
||||||
},
|
"contact_type": "technical"
|
||||||
|
},
|
||||||
],
|
],
|
||||||
"logger": {
|
"logger": {
|
||||||
"rotating": {
|
"rotating": {
|
||||||
|
|||||||
@@ -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 str_to_time, instant, valid, in_a_while
|
||||||
from saml2.time_util import before, after, not_before, not_on_or_after
|
from saml2.time_util import before, after, not_before, not_on_or_after
|
||||||
|
|
||||||
|
|
||||||
def test_f_quotient():
|
def test_f_quotient():
|
||||||
assert f_quotient(-1,3) == -1
|
assert f_quotient(-1, 3) == -1
|
||||||
assert f_quotient(0,3) == 0
|
assert f_quotient(0, 3) == 0
|
||||||
assert f_quotient(1,3) == 0
|
assert f_quotient(1, 3) == 0
|
||||||
assert f_quotient(2,3) == 0
|
assert f_quotient(2, 3) == 0
|
||||||
assert f_quotient(3,3) == 1
|
assert f_quotient(3, 3) == 1
|
||||||
assert f_quotient(3.123,3) == 1
|
assert f_quotient(3.123, 3) == 1
|
||||||
|
|
||||||
|
|
||||||
def test_modulo():
|
def test_modulo():
|
||||||
assert modulo(-1,3) == 2
|
assert modulo(-1, 3) == 2
|
||||||
assert modulo(0,3) == 0
|
assert modulo(0, 3) == 0
|
||||||
assert modulo(1,3) == 1
|
assert modulo(1, 3) == 1
|
||||||
assert modulo(2,3) == 2
|
assert modulo(2, 3) == 2
|
||||||
assert modulo(3,3) == 0
|
assert modulo(3, 3) == 0
|
||||||
x = 3.123
|
x = 3.123
|
||||||
assert modulo(3.123,3) == x - 3
|
assert modulo(3.123, 3) == x - 3
|
||||||
|
|
||||||
|
|
||||||
def test_f_quotient_2():
|
def test_f_quotient_2():
|
||||||
assert f_quotient(0, 1, 13) == -1
|
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(i, 1, 13) == 0
|
||||||
assert f_quotient(13, 1, 13) == 1
|
assert f_quotient(13, 1, 13) == 1
|
||||||
assert f_quotient(13.123, 1, 13) == 1
|
assert f_quotient(13.123, 1, 13) == 1
|
||||||
|
|
||||||
|
|
||||||
def test_modulo_2():
|
def test_modulo_2():
|
||||||
assert modulo(0, 1, 13) == 12
|
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(i, 1, 13) == i
|
||||||
assert modulo(13, 1, 13) == 1
|
assert modulo(13, 1, 13) == 1
|
||||||
#x = 0.123
|
#x = 0.123
|
||||||
#assert modulo(13+x, 1, 13) == 1+x
|
#assert modulo(13+x, 1, 13) == 1+x
|
||||||
|
|
||||||
|
|
||||||
def test_parse_duration():
|
def test_parse_duration():
|
||||||
(sign, d) = parse_duration("P1Y3M5DT7H10M3.3S")
|
(sign, d) = parse_duration("P1Y3M5DT7H10M3.3S")
|
||||||
assert sign == "+"
|
assert sign == "+"
|
||||||
@@ -49,6 +54,45 @@ def test_parse_duration():
|
|||||||
assert d['tm_year'] == 1
|
assert d['tm_year'] == 1
|
||||||
assert d['tm_min'] == 10
|
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():
|
def test_add_duration_1():
|
||||||
#2000-01-12T12:13:14Z P1Y3M5DT7H10M3S 2001-04-17T19:23:17Z
|
#2000-01-12T12:13:14Z P1Y3M5DT7H10M3S 2001-04-17T19:23:17Z
|
||||||
t = add_duration(str_to_time("2000-01-12T12:13:14Z"), "P1Y3M5DT7H10M3S")
|
t = add_duration(str_to_time("2000-01-12T12:13:14Z"), "P1Y3M5DT7H10M3S")
|
||||||
@@ -59,9 +103,10 @@ def test_add_duration_1():
|
|||||||
assert t.tm_min == 23
|
assert t.tm_min == 23
|
||||||
assert t.tm_sec == 17
|
assert t.tm_sec == 17
|
||||||
|
|
||||||
|
|
||||||
def test_add_duration_2():
|
def test_add_duration_2():
|
||||||
#2000-01-12 PT33H 2000-01-13
|
#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_year == 2000
|
||||||
assert t.tm_mon == 1
|
assert t.tm_mon == 1
|
||||||
assert t.tm_mday == 14
|
assert t.tm_mday == 14
|
||||||
@@ -69,6 +114,7 @@ def test_add_duration_2():
|
|||||||
assert t.tm_min == 0
|
assert t.tm_min == 0
|
||||||
assert t.tm_sec == 0
|
assert t.tm_sec == 0
|
||||||
|
|
||||||
|
|
||||||
def test_str_to_time():
|
def test_str_to_time():
|
||||||
t = calendar.timegm(str_to_time("2000-01-12T00:00:00Z"))
|
t = calendar.timegm(str_to_time("2000-01-12T00:00:00Z"))
|
||||||
#TODO: Find all instances of time.mktime(.....)
|
#TODO: Find all instances of time.mktime(.....)
|
||||||
@@ -78,22 +124,25 @@ def test_str_to_time():
|
|||||||
# do this as an external method in the
|
# do this as an external method in the
|
||||||
assert t == 947635200
|
assert t == 947635200
|
||||||
|
|
||||||
|
|
||||||
def test_instant():
|
def test_instant():
|
||||||
inst = str_to_time(instant())
|
inst = str_to_time(instant())
|
||||||
now = time.gmtime()
|
now = time.gmtime()
|
||||||
|
|
||||||
assert now >= inst
|
assert now >= inst
|
||||||
|
|
||||||
|
|
||||||
def test_valid():
|
def test_valid():
|
||||||
assert valid("2000-01-12T00:00:00Z") == False
|
assert valid("2000-01-12T00:00:00Z") == False
|
||||||
current_year = datetime.datetime.today().year
|
current_year = datetime.datetime.today().year
|
||||||
assert valid("%d-01-12T00:00:00Z" % (current_year + 1)) == True
|
assert valid("%d-01-12T00:00:00Z" % (current_year + 1)) == True
|
||||||
this_instance = instant()
|
this_instance = instant()
|
||||||
time.sleep(1)
|
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)
|
soon = in_a_while(seconds=10)
|
||||||
assert valid(soon) == True
|
assert valid(soon) == True
|
||||||
|
|
||||||
|
|
||||||
def test_timeout():
|
def test_timeout():
|
||||||
soon = in_a_while(seconds=1)
|
soon = in_a_while(seconds=1)
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
@@ -122,3 +171,7 @@ def test_not_on_or_after():
|
|||||||
current_year = datetime.datetime.today().year
|
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)) == True
|
||||||
assert not_on_or_after("%d-01-01T00:00:00Z" % (current_year - 1)) == False
|
assert not_on_or_after("%d-01-01T00:00:00Z" % (current_year - 1)) == False
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_parse_duration_n()
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ def test_audience():
|
|||||||
assert aud_restr.audience.text == "urn:foo:bar"
|
assert aud_restr.audience.text == "urn:foo:bar"
|
||||||
|
|
||||||
def test_conditions():
|
def test_conditions():
|
||||||
conditions = utils.factory( saml.Conditions,
|
conditions = utils.factory(saml.Conditions,
|
||||||
not_before="2009-10-30T07:58:10.852Z",
|
not_before="2009-10-30T07:58:10.852Z",
|
||||||
not_on_or_after="2009-10-30T08:03:10.852Z",
|
not_on_or_after="2009-10-30T08:03:10.852Z",
|
||||||
audience_restriction=[utils.factory(saml.AudienceRestriction,
|
audience_restriction=[utils.factory(saml.AudienceRestriction,
|
||||||
|
|||||||
@@ -18,9 +18,11 @@ from saml2.validate import valid_anytype
|
|||||||
|
|
||||||
from py.test import raises
|
from py.test import raises
|
||||||
|
|
||||||
def _eq(l1,l2):
|
|
||||||
|
def _eq(l1, l2):
|
||||||
return set(l1) == set(l2)
|
return set(l1) == set(l2)
|
||||||
|
|
||||||
|
|
||||||
def test_duration():
|
def test_duration():
|
||||||
assert valid_duration("P1Y2M3DT10H30M")
|
assert valid_duration("P1Y2M3DT10H30M")
|
||||||
assert valid_duration("P1Y2M3DT10H30M1.567S")
|
assert valid_duration("P1Y2M3DT10H30M1.567S")
|
||||||
@@ -31,41 +33,45 @@ def test_duration():
|
|||||||
assert valid_duration("P0Y1347M")
|
assert valid_duration("P0Y1347M")
|
||||||
assert valid_duration("P0Y1347M0D")
|
assert valid_duration("P0Y1347M0D")
|
||||||
assert valid_duration("-P1347M")
|
assert valid_duration("-P1347M")
|
||||||
|
assert valid_duration("P1Y2MT2.5H")
|
||||||
|
|
||||||
raises( NotValid, 'valid_duration("P-1347M")')
|
raises(NotValid, 'valid_duration("P-1347M")')
|
||||||
raises( NotValid, ' valid_duration("P1Y2MT")')
|
raises(NotValid, ' valid_duration("P1Y2MT")')
|
||||||
raises( NotValid, ' valid_duration("P1Y2MT2.5H")')
|
raises(NotValid, ' valid_duration("P1Y2MT2xH")')
|
||||||
raises( NotValid, ' valid_duration("P1Y2MT2xH")')
|
|
||||||
|
|
||||||
|
|
||||||
def test_unsigned_short():
|
def test_unsigned_short():
|
||||||
assert valid_unsigned_short("1234")
|
assert valid_unsigned_short("1234")
|
||||||
|
|
||||||
raises( NotValid, ' valid_unsigned_short("-1234")')
|
raises(NotValid, ' valid_unsigned_short("-1234")')
|
||||||
raises( NotValid, ' valid_unsigned_short("1234567890")')
|
raises(NotValid, ' valid_unsigned_short("1234567890")')
|
||||||
|
|
||||||
|
|
||||||
def test_valid_non_negative_integer():
|
def test_valid_non_negative_integer():
|
||||||
assert valid_non_negative_integer("1234567890")
|
assert valid_non_negative_integer("1234567890")
|
||||||
|
|
||||||
raises( NotValid, 'valid_non_negative_integer("-123")')
|
raises(NotValid, 'valid_non_negative_integer("-123")')
|
||||||
raises( NotValid, 'valid_non_negative_integer("123.56")')
|
raises(NotValid, 'valid_non_negative_integer("123.56")')
|
||||||
assert valid_non_negative_integer("12345678901234567890")
|
assert valid_non_negative_integer("12345678901234567890")
|
||||||
|
|
||||||
|
|
||||||
def test_valid_string():
|
def test_valid_string():
|
||||||
assert valid_string(u'example')
|
assert valid_string(u'example')
|
||||||
|
|
||||||
raises( NotValid, 'valid_string("02656c6c6f".decode("hex"))')
|
raises(NotValid, 'valid_string("02656c6c6f".decode("hex"))')
|
||||||
|
|
||||||
|
|
||||||
def test_valid_anyuri():
|
def test_valid_anyuri():
|
||||||
assert valid_any_uri("urn:oasis:names:tc:SAML:2.0:attrname-format:uri")
|
assert valid_any_uri("urn:oasis:names:tc:SAML:2.0:attrname-format:uri")
|
||||||
|
|
||||||
|
|
||||||
def test_valid_instance():
|
def test_valid_instance():
|
||||||
attr_statem = saml.AttributeStatement()
|
attr_statem = saml.AttributeStatement()
|
||||||
text = ["value of test attribute",
|
text = ["value of test attribute",
|
||||||
"value1 of test attribute",
|
"value1 of test attribute",
|
||||||
"value2 of test attribute",
|
"value2 of test attribute",
|
||||||
"value1 of test attribute2",
|
"value1 of test attribute2",
|
||||||
"value2 of test attribute2",]
|
"value2 of test attribute2", ]
|
||||||
|
|
||||||
attr_statem.attribute.append(saml.Attribute())
|
attr_statem.attribute.append(saml.Attribute())
|
||||||
attr_statem.attribute.append(saml.Attribute())
|
attr_statem.attribute.append(saml.Attribute())
|
||||||
@@ -94,7 +100,8 @@ def test_valid_instance():
|
|||||||
response.status = samlp.Status()
|
response.status = samlp.Status()
|
||||||
response.assertion.append(saml.Assertion())
|
response.assertion.append(saml.Assertion())
|
||||||
|
|
||||||
raises( MustValueError, 'valid_instance(response)')
|
raises(MustValueError, 'valid_instance(response)')
|
||||||
|
|
||||||
|
|
||||||
def test_valid_anytype():
|
def test_valid_anytype():
|
||||||
assert valid_anytype("130.239.16.3")
|
assert valid_anytype("130.239.16.3")
|
||||||
@@ -104,4 +111,3 @@ def test_valid_anytype():
|
|||||||
assert valid_anytype("P1Y2M3DT10H30M")
|
assert valid_anytype("P1Y2M3DT10H30M")
|
||||||
assert valid_anytype("urn:oasis:names:tc:SAML:2.0:attrname-format:uri")
|
assert valid_anytype("urn:oasis:names:tc:SAML:2.0:attrname-format:uri")
|
||||||
|
|
||||||
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# coding=utf-8
|
||||||
from saml2.authn_context import pword
|
from saml2.authn_context import pword
|
||||||
from saml2.mdie import to_dict
|
from saml2.mdie import to_dict
|
||||||
from saml2 import md, assertion
|
from saml2 import md, assertion
|
||||||
@@ -75,7 +76,6 @@ def test_filter_on_attributes_1():
|
|||||||
assert ava.keys() == ["serialNumber"]
|
assert ava.keys() == ["serialNumber"]
|
||||||
assert ava["serialNumber"] == ["12345"]
|
assert ava["serialNumber"] == ["12345"]
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
def test_lifetime_1():
|
def test_lifetime_1():
|
||||||
@@ -172,6 +172,8 @@ def test_ava_filter_2():
|
|||||||
"surName": "Jeter",
|
"surName": "Jeter",
|
||||||
"mail": "derek@example.com"}
|
"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',
|
raises(MissingValue, policy.filter, ava, 'urn:mace:umu.se:saml:roland:sp',
|
||||||
None, [mail], [gn, sn])
|
None, [mail], [gn, sn])
|
||||||
|
|
||||||
@@ -183,6 +185,44 @@ def test_ava_filter_2():
|
|||||||
None, [gn, sn, mail])
|
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):
|
def test_filter_attribute_value_assertions_0(AVA):
|
||||||
p = Policy({
|
p = Policy({
|
||||||
"default": {
|
"default": {
|
||||||
@@ -255,6 +295,7 @@ def test_filter_attribute_value_assertions_2(AVA):
|
|||||||
assert _eq(ava.keys(), ["givenName"])
|
assert _eq(ava.keys(), ["givenName"])
|
||||||
assert ava["givenName"] == ["Roland"]
|
assert ava["givenName"] == ["Roland"]
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
@@ -290,7 +331,9 @@ def test_assertion_1(AVA):
|
|||||||
|
|
||||||
def test_assertion_2():
|
def test_assertion_2():
|
||||||
AVA = {'mail': u'roland.hedberg@adm.umu.se',
|
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',
|
'displayName': u'Roland Hedberg',
|
||||||
'uid': 'http://roland.hedberg.myopenid.com/'}
|
'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)")
|
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 _eq(ava.keys(), ['givenName', 'sn'])
|
||||||
assert ava == {'givenName': ['Roland'], 'sn': ['Hedberg']}
|
assert ava == {'givenName': ['Roland'], 'sn': ['Hedberg']}
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
@@ -706,7 +751,7 @@ ACD = pword.AuthenticationContextDeclaration(authn_method=authn_method)
|
|||||||
|
|
||||||
|
|
||||||
def test_assertion_with_noop_attribute_conv():
|
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)
|
ast = Assertion(ava)
|
||||||
policy = Policy({
|
policy = Policy({
|
||||||
"default": {
|
"default": {
|
||||||
@@ -719,7 +764,7 @@ def test_assertion_with_noop_attribute_conv():
|
|||||||
issuer = Issuer(text="entityid", format=NAMEID_FORMAT_ENTITY)
|
issuer = Issuer(text="entityid", format=NAMEID_FORMAT_ENTITY)
|
||||||
msg = ast.construct("sp_entity_id", "in_response_to", "consumer_url",
|
msg = ast.construct("sp_entity_id", "in_response_to", "consumer_url",
|
||||||
name_id, [AttributeConverterNOOP(NAME_FORMAT_URI)],
|
name_id, [AttributeConverterNOOP(NAME_FORMAT_URI)],
|
||||||
policy, issuer=issuer, authn_decl=ACD ,
|
policy, issuer=issuer, authn_decl=ACD,
|
||||||
authn_auth="authn_authn")
|
authn_auth="authn_authn")
|
||||||
|
|
||||||
print msg
|
print msg
|
||||||
@@ -767,7 +812,7 @@ def test_assertion_with_zero_attributes():
|
|||||||
issuer = Issuer(text="entityid", format=NAMEID_FORMAT_ENTITY)
|
issuer = Issuer(text="entityid", format=NAMEID_FORMAT_ENTITY)
|
||||||
msg = ast.construct("sp_entity_id", "in_response_to", "consumer_url",
|
msg = ast.construct("sp_entity_id", "in_response_to", "consumer_url",
|
||||||
name_id, [AttributeConverterNOOP(NAME_FORMAT_URI)],
|
name_id, [AttributeConverterNOOP(NAME_FORMAT_URI)],
|
||||||
policy, issuer=issuer, authn_decl=ACD ,
|
policy, issuer=issuer, authn_decl=ACD,
|
||||||
authn_auth="authn_authn")
|
authn_auth="authn_authn")
|
||||||
|
|
||||||
print msg
|
print msg
|
||||||
@@ -797,4 +842,4 @@ def test_assertion_with_authn_instant():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
test_assertion_2()
|
test_ava_filter_dont_fail()
|
||||||
|
|||||||
@@ -122,4 +122,3 @@ class TestClass:
|
|||||||
assert inactive == ["bcde"]
|
assert inactive == ["bcde"]
|
||||||
assert ava == {}
|
assert ava == {}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
from saml2.sigver import pre_encryption_part, make_temp
|
||||||
from saml2.mdstore import MetadataStore
|
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.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 class_name
|
||||||
from saml2 import time_util
|
from saml2 import time_util
|
||||||
from saml2 import saml, samlp
|
from saml2 import saml, samlp
|
||||||
@@ -25,9 +26,10 @@ PUB_KEY = full_path("test.pem")
|
|||||||
PRIV_KEY = full_path("test.key")
|
PRIV_KEY = full_path("test.key")
|
||||||
|
|
||||||
|
|
||||||
def _eq(l1,l2):
|
def _eq(l1, l2):
|
||||||
return set(l1) == set(l2)
|
return set(l1) == set(l2)
|
||||||
|
|
||||||
|
|
||||||
CERT1 = """MIICsDCCAhmgAwIBAgIJAJrzqSSwmDY9MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
|
CERT1 = """MIICsDCCAhmgAwIBAgIJAJrzqSSwmDY9MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
|
||||||
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
|
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
|
||||||
aWRnaXRzIFB0eSBMdGQwHhcNMDkxMDA2MTk0OTQxWhcNMDkxMTA1MTk0OTQxWjBF
|
aWRnaXRzIFB0eSBMdGQwHhcNMDkxMDA2MTk0OTQxWhcNMDkxMTA1MTk0OTQxWjBF
|
||||||
@@ -104,7 +106,6 @@ class FakeConfig():
|
|||||||
|
|
||||||
|
|
||||||
class TestSecurity():
|
class TestSecurity():
|
||||||
|
|
||||||
def setup_class(self):
|
def setup_class(self):
|
||||||
# This would be one way to initialize the security context :
|
# This would be one way to initialize the security context :
|
||||||
#
|
#
|
||||||
@@ -126,8 +127,8 @@ class TestSecurity():
|
|||||||
issue_instant="2009-10-30T13:20:28Z",
|
issue_instant="2009-10-30T13:20:28Z",
|
||||||
signature=sigver.pre_signature_part("11111", self.sec.my_cert, 1),
|
signature=sigver.pre_signature_part("11111", self.sec.my_cert, 1),
|
||||||
attribute_statement=do_attribute_statement({
|
attribute_statement=do_attribute_statement({
|
||||||
("", "", "surName"): ("Foo", ""),
|
("", "", "surName"): ("Foo", ""),
|
||||||
("", "", "givenName"): ("Bar", ""),
|
("", "", "givenName"): ("Bar", ""),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -144,7 +145,7 @@ class TestSecurity():
|
|||||||
|
|
||||||
def test_non_verify_2(self):
|
def test_non_verify_2(self):
|
||||||
xml_response = open(FALSE_SIGNED).read()
|
xml_response = open(FALSE_SIGNED).read()
|
||||||
raises(sigver.SignatureError,self.sec.correctly_signed_response,
|
raises(sigver.SignatureError, self.sec.correctly_signed_response,
|
||||||
xml_response)
|
xml_response)
|
||||||
|
|
||||||
def test_sign_assertion(self):
|
def test_sign_assertion(self):
|
||||||
@@ -155,7 +156,7 @@ class TestSecurity():
|
|||||||
sass = saml.assertion_from_string(sign_ass)
|
sass = saml.assertion_from_string(sign_ass)
|
||||||
#print sass
|
#print sass
|
||||||
assert _eq(sass.keyswv(), ['attribute_statement', 'issue_instant',
|
assert _eq(sass.keyswv(), ['attribute_statement', 'issue_instant',
|
||||||
'version', 'signature', 'id'])
|
'version', 'signature', 'id'])
|
||||||
assert sass.version == "2.0"
|
assert sass.version == "2.0"
|
||||||
assert sass.id == "11111"
|
assert sass.id == "11111"
|
||||||
assert time_util.str_to_time(sass.issue_instant)
|
assert time_util.str_to_time(sass.issue_instant)
|
||||||
@@ -227,12 +228,14 @@ class TestSecurity():
|
|||||||
|
|
||||||
def test_sign_response(self):
|
def test_sign_response(self):
|
||||||
response = factory(samlp.Response,
|
response = factory(samlp.Response,
|
||||||
assertion=self._assertion,
|
assertion=self._assertion,
|
||||||
id="22222",
|
id="22222",
|
||||||
signature=sigver.pre_signature_part("22222", self.sec.my_cert))
|
signature=sigver.pre_signature_part("22222",
|
||||||
|
self.sec
|
||||||
|
.my_cert))
|
||||||
|
|
||||||
to_sign = [(class_name(self._assertion), self._assertion.id),
|
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)
|
s_response = sigver.signed_instance_factory(response, self.sec, to_sign)
|
||||||
|
|
||||||
assert s_response is not None
|
assert s_response is not None
|
||||||
@@ -252,23 +255,27 @@ class TestSecurity():
|
|||||||
assert item.id == "22222"
|
assert item.id == "22222"
|
||||||
|
|
||||||
def test_sign_response_2(self):
|
def test_sign_response_2(self):
|
||||||
assertion2 = factory( saml.Assertion,
|
assertion2 = factory(saml.Assertion,
|
||||||
version= "2.0",
|
version="2.0",
|
||||||
id= "11122",
|
id="11122",
|
||||||
issue_instant= "2009-10-30T13:20:28Z",
|
issue_instant="2009-10-30T13:20:28Z",
|
||||||
signature= sigver.pre_signature_part("11122", self.sec.my_cert),
|
signature=sigver.pre_signature_part("11122",
|
||||||
attribute_statement=do_attribute_statement({
|
self.sec
|
||||||
("","","surName"): ("Fox",""),
|
.my_cert),
|
||||||
("","","givenName") :("Bear",""),
|
attribute_statement=do_attribute_statement({
|
||||||
})
|
("", "", "surName"): ("Fox", ""),
|
||||||
)
|
("", "", "givenName"): ("Bear", ""),
|
||||||
|
})
|
||||||
|
)
|
||||||
response = factory(samlp.Response,
|
response = factory(samlp.Response,
|
||||||
assertion=assertion2,
|
assertion=assertion2,
|
||||||
id="22233",
|
id="22233",
|
||||||
signature=sigver.pre_signature_part("22233", self.sec.my_cert))
|
signature=sigver.pre_signature_part("22233",
|
||||||
|
self.sec
|
||||||
|
.my_cert))
|
||||||
|
|
||||||
to_sign = [(class_name(assertion2), assertion2.id),
|
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)
|
s_response = sigver.signed_instance_factory(response, self.sec, to_sign)
|
||||||
|
|
||||||
@@ -277,7 +284,7 @@ class TestSecurity():
|
|||||||
|
|
||||||
sass = response2.assertion[0]
|
sass = response2.assertion[0]
|
||||||
assert _eq(sass.keyswv(), ['attribute_statement', 'issue_instant',
|
assert _eq(sass.keyswv(), ['attribute_statement', 'issue_instant',
|
||||||
'version', 'signature', 'id'])
|
'version', 'signature', 'id'])
|
||||||
assert sass.version == "2.0"
|
assert sass.version == "2.0"
|
||||||
assert sass.id == "11122"
|
assert sass.id == "11122"
|
||||||
|
|
||||||
@@ -288,30 +295,34 @@ class TestSecurity():
|
|||||||
|
|
||||||
def test_sign_verify(self):
|
def test_sign_verify(self):
|
||||||
response = factory(samlp.Response,
|
response = factory(samlp.Response,
|
||||||
assertion=self._assertion,
|
assertion=self._assertion,
|
||||||
id="22233",
|
id="22233",
|
||||||
signature=sigver.pre_signature_part("22233", self.sec.my_cert))
|
signature=sigver.pre_signature_part("22233",
|
||||||
|
self.sec
|
||||||
|
.my_cert))
|
||||||
|
|
||||||
to_sign = [(class_name(self._assertion), self._assertion.id),
|
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)
|
s_response = sigver.signed_instance_factory(response, self.sec, to_sign)
|
||||||
|
|
||||||
print s_response
|
print s_response
|
||||||
res = self.sec.verify_signature("%s" % s_response,
|
res = self.sec.verify_signature("%s" % s_response,
|
||||||
node_name=class_name(samlp.Response()))
|
node_name=class_name(samlp.Response()))
|
||||||
|
|
||||||
print res
|
print res
|
||||||
assert res
|
assert res
|
||||||
|
|
||||||
def test_sign_verify_with_cert_from_instance(self):
|
def test_sign_verify_with_cert_from_instance(self):
|
||||||
response = factory(samlp.Response,
|
response = factory(samlp.Response,
|
||||||
assertion=self._assertion,
|
assertion=self._assertion,
|
||||||
id="22222",
|
id="22222",
|
||||||
signature=sigver.pre_signature_part("22222", self.sec.my_cert))
|
signature=sigver.pre_signature_part("22222",
|
||||||
|
self.sec
|
||||||
|
.my_cert))
|
||||||
|
|
||||||
to_sign = [(class_name(self._assertion), self._assertion.id),
|
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)
|
s_response = sigver.signed_instance_factory(response, self.sec, to_sign)
|
||||||
|
|
||||||
@@ -319,11 +330,10 @@ class TestSecurity():
|
|||||||
|
|
||||||
ci = "".join(sigver.cert_from_instance(response2)[0].split())
|
ci = "".join(sigver.cert_from_instance(response2)[0].split())
|
||||||
|
|
||||||
|
|
||||||
assert ci == self.sec.my_cert
|
assert ci == self.sec.my_cert
|
||||||
|
|
||||||
res = self.sec.verify_signature("%s" % s_response,
|
res = self.sec.verify_signature("%s" % s_response,
|
||||||
node_name=class_name(samlp.Response()))
|
node_name=class_name(samlp.Response()))
|
||||||
|
|
||||||
assert res
|
assert res
|
||||||
|
|
||||||
@@ -332,19 +342,22 @@ class TestSecurity():
|
|||||||
assert res == response2
|
assert res == response2
|
||||||
|
|
||||||
def test_sign_verify_assertion_with_cert_from_instance(self):
|
def test_sign_verify_assertion_with_cert_from_instance(self):
|
||||||
assertion = factory( saml.Assertion,
|
assertion = factory(saml.Assertion,
|
||||||
version= "2.0",
|
version="2.0",
|
||||||
id= "11100",
|
id="11100",
|
||||||
issue_instant= "2009-10-30T13:20:28Z",
|
issue_instant="2009-10-30T13:20:28Z",
|
||||||
signature= sigver.pre_signature_part("11100", self.sec.my_cert),
|
signature=sigver.pre_signature_part("11100",
|
||||||
attribute_statement=do_attribute_statement({
|
self.sec
|
||||||
("","","surName"): ("Fox",""),
|
.my_cert),
|
||||||
("","","givenName") :("Bear",""),
|
attribute_statement=do_attribute_statement({
|
||||||
})
|
("", "", "surName"): ("Fox", ""),
|
||||||
)
|
("", "", "givenName"): ("Bear", ""),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
to_sign = [(class_name(assertion), assertion.id)]
|
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
|
print s_assertion
|
||||||
ass = assertion_from_string(s_assertion)
|
ass = assertion_from_string(s_assertion)
|
||||||
ci = "".join(sigver.cert_from_instance(ass)[0].split())
|
ci = "".join(sigver.cert_from_instance(ass)[0].split())
|
||||||
@@ -359,21 +372,24 @@ class TestSecurity():
|
|||||||
assert res
|
assert res
|
||||||
|
|
||||||
def test_exception_sign_verify_with_cert_from_instance(self):
|
def test_exception_sign_verify_with_cert_from_instance(self):
|
||||||
assertion = factory( saml.Assertion,
|
assertion = factory(saml.Assertion,
|
||||||
version= "2.0",
|
version="2.0",
|
||||||
id= "11100",
|
id="11100",
|
||||||
issue_instant= "2009-10-30T13:20:28Z",
|
issue_instant="2009-10-30T13:20:28Z",
|
||||||
#signature= sigver.pre_signature_part("11100", self.sec.my_cert),
|
#signature= sigver.pre_signature_part("11100",
|
||||||
attribute_statement=do_attribute_statement({
|
# self.sec.my_cert),
|
||||||
("","","surName"): ("Foo",""),
|
attribute_statement=do_attribute_statement({
|
||||||
("","","givenName") :("Bar",""),
|
("", "", "surName"): ("Foo", ""),
|
||||||
})
|
("", "", "givenName"): ("Bar", ""),
|
||||||
)
|
})
|
||||||
|
)
|
||||||
|
|
||||||
response = factory(samlp.Response,
|
response = factory(samlp.Response,
|
||||||
assertion=assertion,
|
assertion=assertion,
|
||||||
id="22222",
|
id="22222",
|
||||||
signature=sigver.pre_signature_part("22222", self.sec.my_cert))
|
signature=sigver.pre_signature_part("22222",
|
||||||
|
self.sec
|
||||||
|
.my_cert))
|
||||||
|
|
||||||
to_sign = [(class_name(response), response.id)]
|
to_sign = [(class_name(response), response.id)]
|
||||||
|
|
||||||
@@ -383,7 +399,8 @@ class TestSecurity():
|
|||||||
# Change something that should make everything fail
|
# Change something that should make everything fail
|
||||||
response2.id = "23456"
|
response2.id = "23456"
|
||||||
raises(sigver.SignatureError, self.sec._check_signature,
|
raises(sigver.SignatureError, self.sec._check_signature,
|
||||||
s_response, response2, class_name(response2))
|
s_response, response2, class_name(response2))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TestSecurityMetadata():
|
class TestSecurityMetadata():
|
||||||
@@ -397,36 +414,71 @@ class TestSecurityMetadata():
|
|||||||
conf.only_use_keys_in_metadata = False
|
conf.only_use_keys_in_metadata = False
|
||||||
self.sec = sigver.security_context(conf)
|
self.sec = sigver.security_context(conf)
|
||||||
|
|
||||||
self._assertion = factory( saml.Assertion,
|
assertion = factory(
|
||||||
version="2.0",
|
saml.Assertion, version="2.0", id="11111",
|
||||||
id="11111",
|
issue_instant="2009-10-30T13:20:28Z",
|
||||||
issue_instant="2009-10-30T13:20:28Z",
|
signature=sigver.pre_signature_part("11111", self.sec.my_cert, 1),
|
||||||
signature=sigver.pre_signature_part("11111", self.sec.my_cert, 1),
|
attribute_statement=do_attribute_statement(
|
||||||
attribute_statement=do_attribute_statement({
|
{("", "", "surName"): ("Foo", ""),
|
||||||
("","","surName"): ("Foo",""),
|
("", "", "givenName"): ("Bar", ""), })
|
||||||
("","","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__":
|
if __name__ == "__main__":
|
||||||
t = TestSecurity()
|
#t = TestSecurity()
|
||||||
t.setup_class()
|
#t.setup_class()
|
||||||
|
#t.test_sign_then_encrypt_assertion()
|
||||||
|
test_xbox()
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from saml2 import saml
|
|
||||||
from saml2 import config
|
from saml2 import config
|
||||||
from saml2.authn_context import INTERNETPROTOCOLPASSWORD
|
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 response_factory
|
||||||
from saml2.response import StatusResponse
|
from saml2.response import StatusResponse
|
||||||
from saml2.response import AuthnResponse
|
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
|
from pytest import raises
|
||||||
|
|
||||||
@@ -26,7 +26,6 @@ IDENTITY = {"eduPersonAffiliation": ["staff", "member"],
|
|||||||
"mail": ["foo@gmail.com"],
|
"mail": ["foo@gmail.com"],
|
||||||
"title": ["shortstop"]}
|
"title": ["shortstop"]}
|
||||||
|
|
||||||
|
|
||||||
AUTHN = {
|
AUTHN = {
|
||||||
"class_ref": INTERNETPROTOCOLPASSWORD,
|
"class_ref": INTERNETPROTOCOLPASSWORD,
|
||||||
"authn_auth": "http://www.example.com/login"
|
"authn_auth": "http://www.example.com/login"
|
||||||
@@ -39,29 +38,28 @@ class TestResponse:
|
|||||||
name_id = server.ident.transient_nameid(
|
name_id = server.ident.transient_nameid(
|
||||||
"urn:mace:example.com:saml:roland:sp", "id12")
|
"urn:mace:example.com:saml:roland:sp", "id12")
|
||||||
|
|
||||||
self._resp_ = server.create_authn_response(IDENTITY,
|
self._resp_ = server.create_authn_response(
|
||||||
"id12", # in_response_to
|
IDENTITY,
|
||||||
"http://lingon.catalogix.se:8087/",
|
"id12", # in_response_to
|
||||||
|
"http://lingon.catalogix.se:8087/",
|
||||||
# consumer_url
|
# consumer_url
|
||||||
"urn:mace:example"
|
"urn:mace:example.com:saml:roland:sp",
|
||||||
".com:saml:roland:sp",
|
# sp_entity_id
|
||||||
# sp_entity_id
|
name_id=name_id)
|
||||||
name_id=name_id)
|
|
||||||
|
|
||||||
self._sign_resp_ = server.create_authn_response(
|
self._sign_resp_ = server.create_authn_response(
|
||||||
IDENTITY,
|
IDENTITY,
|
||||||
"id12", # in_response_to
|
"id12", # in_response_to
|
||||||
"http://lingon.catalogix.se:8087/", # consumer_url
|
"http://lingon.catalogix.se:8087/", # consumer_url
|
||||||
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||||
name_id=name_id,
|
name_id=name_id,
|
||||||
sign_assertion=True)
|
sign_assertion=True)
|
||||||
|
|
||||||
self._resp_authn = server.create_authn_response(
|
self._resp_authn = server.create_authn_response(
|
||||||
IDENTITY,
|
IDENTITY,
|
||||||
"id12", # in_response_to
|
"id12", # in_response_to
|
||||||
"http://lingon.catalogix.se:8087/", # consumer_url
|
"http://lingon.catalogix.se:8087/", # consumer_url
|
||||||
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||||
name_id=name_id,
|
name_id=name_id,
|
||||||
authn=AUTHN)
|
authn=AUTHN)
|
||||||
|
|
||||||
@@ -72,7 +70,8 @@ class TestResponse:
|
|||||||
def test_1(self):
|
def test_1(self):
|
||||||
xml_response = ("%s" % (self._resp_,))
|
xml_response = ("%s" % (self._resp_,))
|
||||||
resp = response_factory(xml_response, self.conf,
|
resp = response_factory(xml_response, self.conf,
|
||||||
return_addrs=["http://lingon.catalogix.se:8087/"],
|
return_addrs=[
|
||||||
|
"http://lingon.catalogix.se:8087/"],
|
||||||
outstanding_queries={
|
outstanding_queries={
|
||||||
"id12": "http://localhost:8088/sso"},
|
"id12": "http://localhost:8088/sso"},
|
||||||
timeslack=10000, decode=False)
|
timeslack=10000, decode=False)
|
||||||
@@ -83,7 +82,8 @@ class TestResponse:
|
|||||||
def test_2(self):
|
def test_2(self):
|
||||||
xml_response = self._sign_resp_
|
xml_response = self._sign_resp_
|
||||||
resp = response_factory(xml_response, self.conf,
|
resp = response_factory(xml_response, self.conf,
|
||||||
return_addrs=["http://lingon.catalogix.se:8087/"],
|
return_addrs=[
|
||||||
|
"http://lingon.catalogix.se:8087/"],
|
||||||
outstanding_queries={
|
outstanding_queries={
|
||||||
"id12": "http://localhost:8088/sso"},
|
"id12": "http://localhost:8088/sso"},
|
||||||
timeslack=10000, decode=False)
|
timeslack=10000, decode=False)
|
||||||
@@ -92,15 +92,6 @@ class TestResponse:
|
|||||||
assert isinstance(resp, AuthnResponse)
|
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__":
|
if __name__ == "__main__":
|
||||||
t = TestResponse()
|
t = TestResponse()
|
||||||
t.setup_class()
|
t.setup_class()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from saml2.sigver import pre_encryption_part, ASSERT_XPATH, EncryptError
|
|||||||
from saml2.sigver import CryptoBackendXmlSec1
|
from saml2.sigver import CryptoBackendXmlSec1
|
||||||
from saml2.sigver import pre_encrypt_assertion
|
from saml2.sigver import pre_encrypt_assertion
|
||||||
from pathutils import xmlsec_path
|
from pathutils import xmlsec_path
|
||||||
|
from pathutils import full_path
|
||||||
|
|
||||||
__author__ = 'roland'
|
__author__ = 'roland'
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ def test_enc1():
|
|||||||
# data_file.close()
|
# data_file.close()
|
||||||
|
|
||||||
key_type = "des-192"
|
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,
|
"--session-key", key_type, "--xml-data", data,
|
||||||
"--node-xpath", ASSERT_XPATH]
|
"--node-xpath", ASSERT_XPATH]
|
||||||
|
|
||||||
@@ -89,7 +90,7 @@ def test_enc2():
|
|||||||
IDENTITY, "id12", "http://lingon.catalogix.se:8087/",
|
IDENTITY, "id12", "http://lingon.catalogix.se:8087/",
|
||||||
"urn:mace:example.com:saml:roland:sp", name_id=name_id)
|
"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())
|
pre_encryption_part())
|
||||||
|
|
||||||
print enc_resp
|
print enc_resp
|
||||||
|
|||||||
@@ -2,18 +2,25 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import base64
|
import base64
|
||||||
from urlparse import parse_qs
|
from urlparse import parse_qs
|
||||||
|
from saml2.sigver import pre_encryption_part
|
||||||
from saml2.assertion import Policy
|
from saml2.assertion import Policy
|
||||||
from saml2.authn_context import INTERNETPROTOCOLPASSWORD
|
from saml2.authn_context import INTERNETPROTOCOLPASSWORD
|
||||||
from saml2.saml import NameID, NAMEID_FORMAT_TRANSIENT
|
from saml2.saml import NameID, NAMEID_FORMAT_TRANSIENT
|
||||||
from saml2.samlp import response_from_string
|
from saml2.samlp import response_from_string
|
||||||
|
|
||||||
from saml2.server import Server
|
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 s_utils
|
||||||
from saml2 import sigver
|
from saml2 import sigver
|
||||||
from saml2 import time_util
|
from saml2 import time_util
|
||||||
from saml2.s_utils import OtherError
|
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.soap import make_soap_enveloped_saml_thingy
|
||||||
from saml2 import BINDING_HTTP_POST
|
from saml2 import BINDING_HTTP_POST
|
||||||
from saml2 import BINDING_HTTP_REDIRECT
|
from saml2 import BINDING_HTTP_REDIRECT
|
||||||
@@ -182,7 +189,8 @@ class TestServer1():
|
|||||||
name_id_policy = resp_args["name_id_policy"]
|
name_id_policy = resp_args["name_id_policy"]
|
||||||
assert _eq(name_id_policy.keyswv(), ["format", "allow_create"])
|
assert _eq(name_id_policy.keyswv(), ["format", "allow_create"])
|
||||||
assert name_id_policy.format == saml.NAMEID_FORMAT_TRANSIENT
|
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):
|
def test_sso_response_with_identity(self):
|
||||||
name_id = self.server.ident.transient_nameid(
|
name_id = self.server.ident.transient_nameid(
|
||||||
@@ -195,8 +203,8 @@ class TestServer1():
|
|||||||
"mail": "derek.jeter@nyy.mlb.com",
|
"mail": "derek.jeter@nyy.mlb.com",
|
||||||
"title": "The man"
|
"title": "The man"
|
||||||
},
|
},
|
||||||
"id12", # in_response_to
|
"id12", # in_response_to
|
||||||
"http://localhost:8087/", # destination
|
"http://localhost:8087/", # destination
|
||||||
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||||
name_id=name_id,
|
name_id=name_id,
|
||||||
authn=AUTHN
|
authn=AUTHN
|
||||||
@@ -227,7 +235,8 @@ class TestServer1():
|
|||||||
break
|
break
|
||||||
assert len(attr.attribute_value) == 1
|
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 == "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]
|
value = attr.attribute_value[0]
|
||||||
assert value.text.strip() == "Short stop"
|
assert value.text.strip() == "Short stop"
|
||||||
assert value.get_type() == "xs:string"
|
assert value.get_type() == "xs:string"
|
||||||
@@ -242,13 +251,13 @@ class TestServer1():
|
|||||||
def test_sso_response_without_identity(self):
|
def test_sso_response_without_identity(self):
|
||||||
resp = self.server.create_authn_response(
|
resp = self.server.create_authn_response(
|
||||||
{},
|
{},
|
||||||
"id12", # in_response_to
|
"id12", # in_response_to
|
||||||
"http://localhost:8087/", # consumer_url
|
"http://localhost:8087/", # consumer_url
|
||||||
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||||
userid="USER1",
|
userid="USER1",
|
||||||
authn=AUTHN,
|
authn=AUTHN,
|
||||||
release_policy=Policy(),
|
release_policy=Policy(),
|
||||||
best_effort=True
|
best_effort=True
|
||||||
)
|
)
|
||||||
|
|
||||||
print resp.keyswv()
|
print resp.keyswv()
|
||||||
@@ -268,12 +277,12 @@ class TestServer1():
|
|||||||
|
|
||||||
resp = self.server.create_authn_response(
|
resp = self.server.create_authn_response(
|
||||||
{},
|
{},
|
||||||
"id12", # in_response_to
|
"id12", # in_response_to
|
||||||
"http://localhost:8087/", # consumer_url
|
"http://localhost:8087/", # consumer_url
|
||||||
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||||
userid="USER1",
|
userid="USER1",
|
||||||
authn=_authn,
|
authn=_authn,
|
||||||
best_effort=True
|
best_effort=True
|
||||||
)
|
)
|
||||||
|
|
||||||
print resp.keyswv()
|
print resp.keyswv()
|
||||||
@@ -297,9 +306,9 @@ class TestServer1():
|
|||||||
print resp.status
|
print resp.status
|
||||||
assert resp.status.status_code.value == samlp.STATUS_RESPONDER
|
assert resp.status.status_code.value == samlp.STATUS_RESPONDER
|
||||||
assert resp.status.status_code.status_code.value == \
|
assert resp.status.status_code.status_code.value == \
|
||||||
samlp.STATUS_REQUEST_UNSUPPORTED
|
samlp.STATUS_REQUEST_UNSUPPORTED
|
||||||
assert resp.status.status_message.text == \
|
assert resp.status.status_message.text == \
|
||||||
"eduPersonAffiliation missing"
|
"eduPersonAffiliation missing"
|
||||||
assert resp.issuer.text == "urn:mace:example.com:saml:roland:idp"
|
assert resp.issuer.text == "urn:mace:example.com:saml:roland:idp"
|
||||||
assert not resp.assertion
|
assert not resp.assertion
|
||||||
|
|
||||||
@@ -346,8 +355,8 @@ class TestServer1():
|
|||||||
|
|
||||||
signed_resp = self.server.create_authn_response(
|
signed_resp = self.server.create_authn_response(
|
||||||
ava,
|
ava,
|
||||||
"id12", # in_response_to
|
"id12", # in_response_to
|
||||||
"http://lingon.catalogix.se:8087/", # consumer_url
|
"http://lingon.catalogix.se:8087/", # consumer_url
|
||||||
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||||
name_id=name_id,
|
name_id=name_id,
|
||||||
sign_assertion=True
|
sign_assertion=True
|
||||||
@@ -480,7 +489,6 @@ def _logout_request(conf_file):
|
|||||||
|
|
||||||
|
|
||||||
class TestServerLogout():
|
class TestServerLogout():
|
||||||
|
|
||||||
def test_1(self):
|
def test_1(self):
|
||||||
server = Server("idp_slo_redirect_conf")
|
server = Server("idp_slo_redirect_conf")
|
||||||
req_id, request = _logout_request("sp_slo_redirect_conf")
|
req_id, request = _logout_request("sp_slo_redirect_conf")
|
||||||
@@ -502,4 +510,3 @@ class TestServerLogout():
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
ts = TestServer1()
|
ts = TestServer1()
|
||||||
ts.setup_class()
|
ts.setup_class()
|
||||||
ts.test_sso_response_specific_instant()
|
|
||||||
@@ -4,21 +4,32 @@
|
|||||||
import base64
|
import base64
|
||||||
import urllib
|
import urllib
|
||||||
import urlparse
|
import urlparse
|
||||||
from saml2.authn_context import INTERNETPROTOCOLPASSWORD
|
from saml2 import BINDING_HTTP_POST
|
||||||
from saml2.response import LogoutResponse
|
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.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.config import SPConfig
|
||||||
|
from saml2.response import LogoutResponse
|
||||||
from saml2.saml import NAMEID_FORMAT_PERSISTENT
|
from saml2.saml import NAMEID_FORMAT_PERSISTENT
|
||||||
from saml2.saml import NAMEID_FORMAT_TRANSIENT
|
from saml2.saml import NAMEID_FORMAT_TRANSIENT
|
||||||
from saml2.saml import NameID
|
from saml2.saml import NameID
|
||||||
from saml2.server import Server
|
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 saml2.time_util import in_a_while
|
||||||
|
|
||||||
from py.test import raises
|
from fakeIDP import FakeIDP
|
||||||
from fakeIDP import FakeIDP, unpack_form
|
from fakeIDP import unpack_form
|
||||||
|
|
||||||
|
|
||||||
AUTHN = {
|
AUTHN = {
|
||||||
@@ -341,8 +352,123 @@ class TestClient:
|
|||||||
print my_name
|
print my_name
|
||||||
assert my_name == "urn:mace:example.com:saml:roland:sp"
|
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"
|
IDP = "urn:mace:example.com:saml:roland:idp"
|
||||||
|
|
||||||
|
|
||||||
@@ -448,4 +574,4 @@ class TestClientWithDummy():
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
tc = TestClient()
|
tc = TestClient()
|
||||||
tc.setup_class()
|
tc.setup_class()
|
||||||
tc.test_sign_auth_request_0()
|
tc.test_sign_then_encrypt_assertion2()
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ class TestSP():
|
|||||||
'sn': ['Jeter'],
|
'sn': ['Jeter'],
|
||||||
'title': ['The man']}
|
'title': ['The man']}
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
_sp = TestSP()
|
_sp = TestSP()
|
||||||
_sp.setup_class()
|
_sp.setup_class()
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from saml2.server import Server
|
|||||||
|
|
||||||
__author__ = 'rolandh'
|
__author__ = 'rolandh'
|
||||||
|
|
||||||
|
|
||||||
def test_basic():
|
def test_basic():
|
||||||
sp = Saml2Client(config_file="servera_conf")
|
sp = Saml2Client(config_file="servera_conf")
|
||||||
idp = Server(config_file="idp_all_conf")
|
idp = Server(config_file="idp_all_conf")
|
||||||
@@ -18,7 +19,7 @@ def test_basic():
|
|||||||
newid = NewID(text="Barfoo")
|
newid = NewID(text="Barfoo")
|
||||||
|
|
||||||
mid, mreq = sp.create_manage_name_id_request(destination, name_id=nameid,
|
mid, mreq = sp.create_manage_name_id_request(destination, name_id=nameid,
|
||||||
new_id=newid)
|
new_id=newid)
|
||||||
|
|
||||||
print mreq
|
print mreq
|
||||||
rargs = sp.apply_binding(binding, "%s" % mreq, destination, "")
|
rargs = sp.apply_binding(binding, "%s" % mreq, destination, "")
|
||||||
@@ -31,6 +32,7 @@ def test_basic():
|
|||||||
|
|
||||||
assert mid == _req.message.id
|
assert mid == _req.message.id
|
||||||
|
|
||||||
|
|
||||||
def test_flow():
|
def test_flow():
|
||||||
sp = Saml2Client(config_file="servera_conf")
|
sp = Saml2Client(config_file="servera_conf")
|
||||||
idp = Server(config_file="idp_all_conf")
|
idp = Server(config_file="idp_all_conf")
|
||||||
@@ -42,7 +44,7 @@ def test_flow():
|
|||||||
newid = NewID(text="Barfoo")
|
newid = NewID(text="Barfoo")
|
||||||
|
|
||||||
mid, midq = sp.create_manage_name_id_request(destination, name_id=nameid,
|
mid, midq = sp.create_manage_name_id_request(destination, name_id=nameid,
|
||||||
new_id=newid)
|
new_id=newid)
|
||||||
|
|
||||||
print midq
|
print midq
|
||||||
rargs = sp.apply_binding(binding, "%s" % midq, destination, "")
|
rargs = sp.apply_binding(binding, "%s" % midq, destination, "")
|
||||||
@@ -67,8 +69,13 @@ def test_flow():
|
|||||||
|
|
||||||
# ---------- @SP ---------------
|
# ---------- @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
|
print _response.response
|
||||||
|
|
||||||
assert _response.response.id == mnir.id
|
assert _response.response.id == mnir.id
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_flow()
|
||||||
@@ -8,7 +8,6 @@ from saml2.cert import OpenSSLWrapper
|
|||||||
|
|
||||||
|
|
||||||
class TestGenerateCertificates(unittest.TestCase):
|
class TestGenerateCertificates(unittest.TestCase):
|
||||||
|
|
||||||
def test_validate_with_root_cert(self):
|
def test_validate_with_root_cert(self):
|
||||||
|
|
||||||
cert_info_ca = {
|
cert_info_ca = {
|
||||||
@@ -33,23 +32,35 @@ class TestGenerateCertificates(unittest.TestCase):
|
|||||||
|
|
||||||
ca_cert, ca_key = osw.create_certificate(cert_info_ca, request=False,
|
ca_cert, ca_key = osw.create_certificate(cert_info_ca, request=False,
|
||||||
write_to_file=True,
|
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_cert_str = osw.read_str_from_file(ca_cert)
|
||||||
ca_key_str = osw.read_str_from_file(ca_key)
|
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)
|
valid, mess = osw.verify(ca_cert_str, cert_str)
|
||||||
self.assertTrue(valid)
|
self.assertTrue(valid)
|
||||||
|
|
||||||
false_ca_cert, false_ca_key = osw.create_certificate(cert_info_ca, request=False, write_to_file=False)
|
false_ca_cert, false_ca_key = osw.create_certificate(cert_info_ca,
|
||||||
false_req_cert_str_1, false_req_key_str_1 = osw.create_certificate(cert_info_ca, request=True)
|
request=False,
|
||||||
false_cert_str_1 = osw.create_cert_signed_certificate(false_ca_cert, false_ca_key, false_req_cert_str_1)
|
write_to_file=False)
|
||||||
false_req_cert_str_2, false_req_key_str_2 = osw.create_certificate(cert_info, request=True)
|
false_req_cert_str_1, false_req_key_str_1 = osw.create_certificate(
|
||||||
false_cert_str_2 = osw.create_cert_signed_certificate(false_ca_cert, false_ca_key, false_req_cert_str_2)
|
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)
|
valid, mess = osw.verify(false_ca_cert, cert_str)
|
||||||
self.assertFalse(valid)
|
self.assertFalse(valid)
|
||||||
@@ -106,20 +117,28 @@ class TestGenerateCertificates(unittest.TestCase):
|
|||||||
|
|
||||||
osw = OpenSSLWrapper()
|
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)
|
req_cert_str, intermediate_1_key_str = osw.create_certificate(
|
||||||
intermediate_cert_1_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str, req_cert_str)
|
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)
|
req_cert_str, intermediate_2_key_str = osw.create_certificate(
|
||||||
intermediate_cert_2_str = osw.create_cert_signed_certificate(intermediate_cert_1_str, intermediate_1_key_str,
|
cert_intermediate_2_info, request=True)
|
||||||
req_cert_str)
|
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)
|
req_cert_str, client_key_str = osw.create_certificate(
|
||||||
client_cert_str = osw.create_cert_signed_certificate(intermediate_cert_2_str, intermediate_2_key_str,
|
cert_client_cert_info, request=True)
|
||||||
req_cert_str)
|
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)
|
valid, mess = osw.verify_chain(cert_chain, client_cert_str)
|
||||||
self.assertTrue(valid)
|
self.assertTrue(valid)
|
||||||
@@ -145,21 +164,23 @@ class TestGenerateCertificates(unittest.TestCase):
|
|||||||
"organization_unit": "asdfg"
|
"organization_unit": "asdfg"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
osw = OpenSSLWrapper()
|
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(
|
||||||
cipher_passphrase=
|
cert_info_ca, request=False,
|
||||||
{"cipher": "blowfish", "passphrase": "qwerty"})
|
cipher_passphrase={"cipher": "blowfish", "passphrase": "qwerty"})
|
||||||
|
|
||||||
req_cert_str, req_key_str = osw.create_certificate(cert_info, request=True)
|
req_cert_str, req_key_str = osw.create_certificate(cert_info,
|
||||||
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str, req_cert_str,
|
request=True)
|
||||||
passphrase="qwerty")
|
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str,
|
||||||
|
req_cert_str,
|
||||||
|
passphrase="qwerty")
|
||||||
|
|
||||||
valid = False
|
valid = False
|
||||||
try:
|
try:
|
||||||
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str, req_cert_str,
|
cert_str = osw.create_cert_signed_certificate(
|
||||||
passphrase="qwertyqwerty")
|
ca_cert_str, ca_key_str, req_cert_str,
|
||||||
|
passphrase="qwertyqwerty")
|
||||||
except Exception:
|
except Exception:
|
||||||
valid = True
|
valid = True
|
||||||
|
|
||||||
@@ -185,39 +206,59 @@ class TestGenerateCertificates(unittest.TestCase):
|
|||||||
"organization_unit": "asdfg"
|
"organization_unit": "asdfg"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
osw = OpenSSLWrapper()
|
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)
|
req_cert_str, req_key_str = osw.create_certificate(cert_info,
|
||||||
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str, req_cert_str)
|
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)
|
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)
|
ca_cert_str, ca_key_str = osw.create_certificate(cert_info_ca,
|
||||||
req_cert_str, req_key_str = osw.create_certificate(cert_info, request=True)
|
request=False,
|
||||||
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str, req_cert_str)
|
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)
|
valid, mess = osw.verify(ca_cert_str, cert_str)
|
||||||
self.assertFalse(valid)
|
self.assertFalse(valid)
|
||||||
|
|
||||||
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,
|
||||||
req_cert_str, req_key_str = osw.create_certificate(cert_info, request=True)
|
request=False)
|
||||||
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str, req_cert_str, valid_from=1000,
|
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_to=100000)
|
||||||
valid, mess = osw.verify(ca_cert_str, cert_str)
|
valid, mess = osw.verify(ca_cert_str, cert_str)
|
||||||
self.assertFalse(valid)
|
self.assertFalse(valid)
|
||||||
|
|
||||||
ca_cert_str, ca_key_str = osw.create_certificate(cert_info_ca, request=False, valid_from=0, valid_to=1)
|
ca_cert_str, ca_key_str = osw.create_certificate(cert_info_ca,
|
||||||
req_cert_str, req_key_str = osw.create_certificate(cert_info, request=True)
|
request=False,
|
||||||
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str, req_cert_str)
|
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)
|
time.sleep(2)
|
||||||
valid, mess = osw.verify(ca_cert_str, cert_str)
|
valid, mess = osw.verify(ca_cert_str, cert_str)
|
||||||
self.assertFalse(valid)
|
self.assertFalse(valid)
|
||||||
|
|
||||||
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,
|
||||||
req_cert_str, req_key_str = osw.create_certificate(cert_info, request=True)
|
request=False)
|
||||||
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str, req_cert_str, 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,
|
||||||
|
valid_from=0, valid_to=1)
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
valid, mess = osw.verify(ca_cert_str, cert_str)
|
valid, mess = osw.verify(ca_cert_str, cert_str)
|
||||||
self.assertFalse(valid)
|
self.assertFalse(valid)
|
||||||
@@ -9,6 +9,7 @@ from saml2.extension.pefim import SPCertEnc
|
|||||||
from saml2.samlp import Extensions
|
from saml2.samlp import Extensions
|
||||||
from saml2.samlp import authn_request_from_string
|
from saml2.samlp import authn_request_from_string
|
||||||
from saml2.sigver import read_cert_from_file
|
from saml2.sigver import read_cert_from_file
|
||||||
|
from pathutils import full_path
|
||||||
|
|
||||||
__author__ = 'roland'
|
__author__ = 'roland'
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ conf.load_file("server_conf")
|
|||||||
client = Saml2Client(conf)
|
client = Saml2Client(conf)
|
||||||
|
|
||||||
# place a certificate in an authn request
|
# 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(
|
spcertenc = SPCertEnc(
|
||||||
x509_data=ds.X509Data(
|
x509_data=ds.X509Data(
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ def base_init(imports):
|
|||||||
line.append("%s%s=%s," % (indent4, _name, _name))
|
line.append("%s%s=%s," % (indent4, _name, _name))
|
||||||
line.append("%s)" % indent4)
|
line.append("%s)" % indent4)
|
||||||
else:
|
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():
|
for sup, elems in imports.items():
|
||||||
line.append("%s%s.__init__(self, " % (INDENT+INDENT, sup))
|
line.append("%s%s.__init__(self, " % (INDENT+INDENT, sup))
|
||||||
lattr = elems[:]
|
lattr = elems[:]
|
||||||
@@ -2187,5 +2187,3 @@ def main(argv):
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main(sys.argv[1:])
|
main(sys.argv[1:])
|
||||||
|
|
||||||
|
|
||||||
140
tools/sync_attrmaps.py
Executable file
140
tools/sync_attrmaps.py
Executable 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()
|
||||||
Reference in New Issue
Block a user