PKG-INFO 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. Metadata-Version: 2.1
  2. Name: djangosaml2
  3. Version: 0.18.0
  4. Summary: pysaml2 integration for Django
  5. Home-page: https://github.com/knaperek/djangosaml2
  6. Author: Yaco Sistemas and independent contributors
  7. Author-email: lgs@yaco.es
  8. Maintainer: Jozef Knaperek
  9. License: Apache 2.0
  10. Download-URL: https://pypi.org/project/djangosaml2/
  11. Description: ===========
  12. djangosaml2
  13. ===========
  14. .. image:: https://travis-ci.org/knaperek/djangosaml2.svg?branch=master
  15. :target: https://travis-ci.org/knaperek/djangosaml2
  16. :align: left
  17. djangosaml2 is a Django application that integrates the PySAML2 library
  18. into your project. This mean that you can protect your Django based project
  19. with a service provider based on PySAML. This way it will talk SAML2 with
  20. your Identity Provider allowing you to use this authentication mechanism.
  21. This document will guide you through a few simple steps to accomplish
  22. such goal.
  23. .. contents::
  24. Installation
  25. ============
  26. PySAML2 uses xmlsec1_ binary to sign SAML assertions so you need to install
  27. it either through your operating system package or by compiling the source
  28. code. It doesn't matter where the final executable is installed because
  29. you will need to set the full path to it in the configuration stage.
  30. .. _xmlsec1: http://www.aleksey.com/xmlsec/
  31. Now you can install the djangosaml2 package using easy_install or pip. This
  32. will also install PySAML2 and its dependencies automatically.
  33. Configuration
  34. =============
  35. There are three things you need to setup to make djangosaml2 work in your
  36. Django project:
  37. 1. **settings.py** as you may already know, it is the main Django
  38. configuration file.
  39. 2. **urls.py** is the file where you will include djangosaml2 urls.
  40. 3. **pysaml2** specific files such as an attribute map directory and a
  41. certificate.
  42. Changes in the settings.py file
  43. -------------------------------
  44. The first thing you need to do is add ``djangosaml2`` to the list of
  45. installed apps::
  46. INSTALLED_APPS = (
  47. 'django.contrib.auth',
  48. 'django.contrib.contenttypes',
  49. 'django.contrib.sessions',
  50. 'django.contrib.sites',
  51. 'django.contrib.messages',
  52. 'django.contrib.admin',
  53. 'djangosaml2', # new application
  54. )
  55. Actually this is not really required since djangosaml2 does not include
  56. any data model. The only reason we include it is to be able to run
  57. djangosaml2 test suite from our project, something you should always
  58. do to make sure it is compatible with your Django version and environment.
  59. .. note::
  60. When you finish the configuration you can run the djangosaml2 test suite as
  61. you run any other Django application test suite. Just type ``python manage.py
  62. test djangosaml2``.
  63. Python 2 users need to ``pip install djangosaml2[test]`` in order to run the
  64. tests.
  65. Then you have to add the ``djangosaml2.backends.Saml2Backend``
  66. authentication backend to the list of authentications backends.
  67. By default only the ModelBackend included in Django is configured.
  68. A typical configuration would look like this::
  69. AUTHENTICATION_BACKENDS = (
  70. 'django.contrib.auth.backends.ModelBackend',
  71. 'djangosaml2.backends.Saml2Backend',
  72. )
  73. .. note::
  74. Before djangosaml2 0.5.0 this authentication backend was
  75. automatically added by djangosaml2. This turned out to be
  76. a bad idea since some applications want to use their own
  77. custom policies for authorization and the authentication
  78. backend is a good place to define that. Starting from
  79. djangosaml2 0.5.0 it is now possible to define such
  80. backends.
  81. Finally we have to tell Django what the new login url we want to use is::
  82. LOGIN_URL = '/saml2/login/'
  83. SESSION_EXPIRE_AT_BROWSER_CLOSE = True
  84. Here we are telling Django that any view that requires an authenticated
  85. user should redirect the user browser to that url if the user has not
  86. been authenticated before. We are also telling that when the user closes
  87. his browser, the session should be terminated. This is useful in SAML2
  88. federations where the logout protocol is not always available.
  89. .. note::
  90. The login url starts with ``/saml2/`` as an example but you can change that
  91. if you want. Check the section about changes in the ``urls.py``
  92. file for more information.
  93. If you want to allow several authentication mechanisms in your project
  94. you should set the LOGIN_URL option to another view and put a link in such
  95. view to the ``/saml2/login/`` view.
  96. Preferred Logout binding
  97. ------------------------
  98. Use the following setting to choose your preferred binding for SP initiated logout requests::
  99. SAML_LOGOUT_REQUEST_PREFERRED_BINDING
  100. For example::
  101. import saml2
  102. SAML_LOGOUT_REQUEST_PREFERRED_BINDING = saml2.BINDING_HTTP_POST
  103. Changes in the urls.py file
  104. ---------------------------
  105. The next thing you need to do is to include ``djangosaml2.urls`` module in your
  106. main ``urls.py`` module::
  107. urlpatterns = patterns(
  108. '',
  109. # lots of url definitions here
  110. (r'^saml2/', include('djangosaml2.urls')),
  111. # more url definitions
  112. )
  113. As you can see we are including ``djangosaml2.urls`` under the *saml2*
  114. prefix. Feel free to use your own prefix but be consistent with what
  115. you have put in the ``settings.py`` file in the LOGIN_URL parameter.
  116. PySAML2 specific files and configuration
  117. ----------------------------------------
  118. Once you have finished configuring your Django project you have to
  119. start configuring PySAML. If you use just that library you have to
  120. put your configuration options in a file and initialize PySAML2 with
  121. the path to that file.
  122. In djangosaml2 you just put the same information in the Django
  123. settings.py file under the SAML_CONFIG option.
  124. We will see a typical configuration for protecting a Django project::
  125. from os import path
  126. import saml2
  127. import saml2.saml
  128. BASEDIR = path.dirname(path.abspath(__file__))
  129. SAML_CONFIG = {
  130. # full path to the xmlsec1 binary programm
  131. 'xmlsec_binary': '/usr/bin/xmlsec1',
  132. # your entity id, usually your subdomain plus the url to the metadata view
  133. 'entityid': 'http://localhost:8000/saml2/metadata/',
  134. # directory with attribute mapping
  135. 'attribute_map_dir': path.join(BASEDIR, 'attribute-maps'),
  136. # this block states what services we provide
  137. 'service': {
  138. # we are just a lonely SP
  139. 'sp' : {
  140. 'name': 'Federated Django sample SP',
  141. 'name_id_format': saml2.saml.NAMEID_FORMAT_PERSISTENT,
  142. 'endpoints': {
  143. # url and binding to the assetion consumer service view
  144. # do not change the binding or service name
  145. 'assertion_consumer_service': [
  146. ('http://localhost:8000/saml2/acs/',
  147. saml2.BINDING_HTTP_POST),
  148. ],
  149. # url and binding to the single logout service view
  150. # do not change the binding or service name
  151. 'single_logout_service': [
  152. ('http://localhost:8000/saml2/ls/',
  153. saml2.BINDING_HTTP_REDIRECT),
  154. ('http://localhost:8000/saml2/ls/post',
  155. saml2.BINDING_HTTP_POST),
  156. ],
  157. },
  158. # Mandates that the identity provider MUST authenticate the
  159. # presenter directly rather than rely on a previous security context.
  160. 'force_authn': False,
  161. # Enable AllowCreate in NameIDPolicy.
  162. 'name_id_format_allow_create': False,
  163. # attributes that this project need to identify a user
  164. 'required_attributes': ['uid'],
  165. # attributes that may be useful to have but not required
  166. 'optional_attributes': ['eduPersonAffiliation'],
  167. # in this section the list of IdPs we talk to are defined
  168. 'idp': {
  169. # we do not need a WAYF service since there is
  170. # only an IdP defined here. This IdP should be
  171. # present in our metadata
  172. # the keys of this dictionary are entity ids
  173. 'https://localhost/simplesaml/saml2/idp/metadata.php': {
  174. 'single_sign_on_service': {
  175. saml2.BINDING_HTTP_REDIRECT: 'https://localhost/simplesaml/saml2/idp/SSOService.php',
  176. },
  177. 'single_logout_service': {
  178. saml2.BINDING_HTTP_REDIRECT: 'https://localhost/simplesaml/saml2/idp/SingleLogoutService.php',
  179. },
  180. },
  181. },
  182. },
  183. },
  184. # where the remote metadata is stored
  185. 'metadata': {
  186. 'local': [path.join(BASEDIR, 'remote_metadata.xml')],
  187. },
  188. # set to 1 to output debugging information
  189. 'debug': 1,
  190. # Signing
  191. 'key_file': path.join(BASEDIR, 'mycert.key'), # private part
  192. 'cert_file': path.join(BASEDIR, 'mycert.pem'), # public part
  193. # Encryption
  194. 'encryption_keypairs': [{
  195. 'key_file': path.join(BASEDIR, 'my_encryption_key.key'), # private part
  196. 'cert_file': path.join(BASEDIR, 'my_encryption_cert.pem'), # public part
  197. }],
  198. # own metadata settings
  199. 'contact_person': [
  200. {'given_name': 'Lorenzo',
  201. 'sur_name': 'Gil',
  202. 'company': 'Yaco Sistemas',
  203. 'email_address': 'lgs@yaco.es',
  204. 'contact_type': 'technical'},
  205. {'given_name': 'Angel',
  206. 'sur_name': 'Fernandez',
  207. 'company': 'Yaco Sistemas',
  208. 'email_address': 'angel@yaco.es',
  209. 'contact_type': 'administrative'},
  210. ],
  211. # you can set multilanguage information here
  212. 'organization': {
  213. 'name': [('Yaco Sistemas', 'es'), ('Yaco Systems', 'en')],
  214. 'display_name': [('Yaco', 'es'), ('Yaco', 'en')],
  215. 'url': [('http://www.yaco.es', 'es'), ('http://www.yaco.com', 'en')],
  216. },
  217. 'valid_for': 24, # how long is our metadata valid
  218. }
  219. .. note::
  220. Please check the `PySAML2 documentation`_ for more information about
  221. these and other configuration options.
  222. .. _`PySAML2 documentation`: http://pysaml2.readthedocs.io/en/latest/
  223. There are several external files and directories you have to create according
  224. to this configuration.
  225. The xmlsec1 binary was mentioned in the installation section. Here, in the
  226. configuration part you just need to put the full path to xmlsec1 so PySAML2
  227. can call it as it needs.
  228. The ``attribute_map_dir`` points to a directory with attribute mappings that
  229. are used to translate user attribute names from several standards. It's usually
  230. safe to just copy the default PySAML2 attribute maps that you can find in the
  231. ``tests/attributemaps`` directory of the source distribution.
  232. The ``metadata`` option is a dictionary where you can define several types of
  233. metadata for remote entities. Usually the easiest type is the ``local`` where
  234. you just put the name of a local XML file with the contents of the remote
  235. entities metadata. This XML file should be in the SAML2 metadata format.
  236. The ``key_file`` and ``cert_file`` options reference the two parts of a
  237. standard x509 certificate. You need it to sign your metadata. For assertion
  238. encryption/decryption support please configure another set of ``key_file`` and
  239. ``cert_file``, but as inner attributes of ``encryption_keypairs`` option.
  240. .. note::
  241. Check your openssl documentation to generate a test certificate but don't
  242. forget to order a real one when you go into production.
  243. Custom and dynamic configuration loading
  244. ........................................
  245. By default, djangosaml2 reads the pysaml2 configuration options from the
  246. SAML_CONFIG setting but sometimes you want to read this information from
  247. another place, like a file or a database. Sometimes you even want this
  248. configuration to be different depending on the request.
  249. Starting from djangosaml2 0.5.0 you can define your own configuration
  250. loader which is a callable that accepts a request parameter and returns
  251. a saml2.config.SPConfig object. In order to do so you set the following
  252. setting::
  253. SAML_CONFIG_LOADER = 'python.path.to.your.callable'
  254. User attributes
  255. ---------------
  256. In the SAML 2.0 authentication process the Identity Provider (IdP) will
  257. send a security assertion to the Service Provider (SP) upon a successful
  258. authentication. This assertion contains attributes about the user that
  259. was authenticated. It depends on the IdP configuration what exact
  260. attributes are sent to each SP it can talk to.
  261. When such assertion is received on the Django side it is used to find a Django
  262. user and create a session for it. By default djangosaml2 will do a query on the
  263. User model with the USERNAME_FIELD_ attribute but you can change it to any
  264. other attribute of the User model. For example, you can do this lookup using
  265. the 'email' attribute. In order to do so you should set the following setting::
  266. SAML_DJANGO_USER_MAIN_ATTRIBUTE = 'email'
  267. .. _USERNAME_FIELD: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#django.contrib.auth.models.CustomUser.USERNAME_FIELD
  268. Please, use an unique attribute when setting this option. Otherwise
  269. the authentication process may fail because djangosaml2 will not know
  270. which Django user it should pick.
  271. If your main attribute is something inherently case-insensitive (such as
  272. an email address), you may set::
  273. SAML_DJANGO_USER_MAIN_ATTRIBUTE_LOOKUP = '__iexact'
  274. (This is simply appended to the main attribute name to form a Django
  275. query. Your main attribute must be unique even given this lookup.)
  276. Another option is to use the SAML2 name id as the username by setting::
  277. SAML_USE_NAME_ID_AS_USERNAME = True
  278. You can configure djangosaml2 to create such user if it is not already in
  279. the Django database or maybe you don't want to allow users that are not
  280. in your database already. For this purpose there is another option you
  281. can set in the settings.py file::
  282. SAML_CREATE_UNKNOWN_USER = True
  283. This setting is True by default.
  284. ACS_DEFAULT_REDIRECT_URL = reverse_lazy('some_url_name')
  285. This setting lets you specify a URL for redirection after a successful
  286. authentication. Particularly useful when you only plan to use
  287. IdP initiated login and the IdP does not have a configured RelayState
  288. parameter. The default is ``/``.
  289. The other thing you will probably want to configure is the mapping of
  290. SAML2 user attributes to Django user attributes. By default only the
  291. User.username attribute is mapped but you can add more attributes or
  292. change that one. In order to do so you need to change the
  293. SAML_ATTRIBUTE_MAPPING option in your settings.py::
  294. SAML_ATTRIBUTE_MAPPING = {
  295. 'uid': ('username', ),
  296. 'mail': ('email', ),
  297. 'cn': ('first_name', ),
  298. 'sn': ('last_name', ),
  299. }
  300. where the keys of this dictionary are SAML user attributes and the values
  301. are Django User attributes.
  302. If you are using Django user profile objects to store extra attributes
  303. about your user you can add those attributes to the SAML_ATTRIBUTE_MAPPING
  304. dictionary. For each (key, value) pair, djangosaml2 will try to store the
  305. attribute in the User model if there is a matching field in that model.
  306. Otherwise it will try to do the same with your profile custom model. For
  307. multi-valued attributes only the first value is assigned to the destination field.
  308. Alternatively, custom processing of attributes can be achieved by setting the
  309. value(s) in the SAML_ATTRIBUTE_MAPPING, to name(s) of method(s) defined on a
  310. custom django User object. In this case, each method is called by djangosaml2,
  311. passing the full list of attribute values extracted from the <saml:AttributeValue>
  312. elements of the <saml:Attribute>. Among other uses, this is a useful way to process
  313. multi-valued attributes such as lists of user group names.
  314. For example:
  315. Saml assertion snippet::
  316. <saml:Attribute Name="groups" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
  317. <saml:AttributeValue>group1</saml:AttributeValue>
  318. <saml:AttributeValue>group2</saml:AttributeValue>
  319. <saml:AttributeValue>group3</saml:AttributeValue>
  320. </saml:Attribute>
  321. Custom User object::
  322. from django.contrib.auth.models import AbstractUser
  323. class User(AbstractUser):
  324. def process_groups(self, groups):
  325. // process list of group names in argument 'groups'
  326. pass;
  327. settings.py::
  328. SAML_ATTRIBUTE_MAPPING = {
  329. 'groups': ('process_groups', ),
  330. }
  331. Learn more about Django profile models at:
  332. https://docs.djangoproject.com/en/dev/topics/auth/customizing/#substituting-a-custom-user-model
  333. Sometimes you need to use special logic to update the user object
  334. depending on the SAML2 attributes and the mapping described above
  335. is simply not enough. For these cases djangosaml2 provides a Django
  336. signal that you can listen to. In order to do so you can add the
  337. following code to your app::
  338. from djangosaml2.signals import pre_user_save
  339. def custom_update_user(sender=User, instance, attributes, user_modified, **kargs)
  340. ...
  341. return True # I modified the user object
  342. Your handler will receive the user object, the list of SAML attributes
  343. and a flag telling you if the user is already modified and need
  344. to be saved after your handler is executed. If your handler
  345. modifies the user object it should return True. Otherwise it should
  346. return False. This way djangosaml2 will know if it should save
  347. the user object so you don't need to do it and no more calls to
  348. the save method are issued.
  349. IdP setup
  350. =========
  351. Congratulations, you have finished configuring the SP side of the federation.
  352. Now you need to send the entity id and the metadata of this new SP to the
  353. IdP administrators so they can add it to their list of trusted services.
  354. You can get this information starting your Django development server and
  355. going to the http://localhost:8000/saml2/metadata url. If you have included
  356. the djangosaml2 urls under a different url prefix you need to correct this
  357. url.
  358. SimpleSAMLphp issues
  359. --------------------
  360. As of SimpleSAMLphp 1.8.2 there is a problem if you specify attributes in
  361. the SP configuration. When the SimpleSAMLphp metadata parser converts the
  362. XML into its custom php format it puts the following option::
  363. 'attributes.NameFormat' => 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri'
  364. But it need to be replaced by this one::
  365. 'AttributeNameFormat' => 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri'
  366. Otherwise the Assertions sent from the IdP to the SP will have a wrong
  367. Attribute Name Format and pysaml2 will be confused.
  368. Furthermore if you have a AttributeLimit filter in your SimpleSAMLphp
  369. configuration you will need to enable another attribute filter just
  370. before to make sure that the AttributeLimit does not remove the attributes
  371. from the authentication source. The filter you need to add is an AttributeMap
  372. filter like this::
  373. 10 => array(
  374. 'class' => 'core:AttributeMap', 'name2oid'
  375. ),
  376. Testing
  377. =======
  378. One way to check if everything is working as expected is to enable the
  379. following url::
  380. urlpatterns = patterns(
  381. '',
  382. # lots of url definitions here
  383. (r'^saml2/', include('djangosaml2.urls')),
  384. (r'^test/', 'djangosaml2.views.echo_attributes'),
  385. # more url definitions
  386. )
  387. Now if you go to the /test/ url you will see your SAML attributes and also
  388. a link to do a global logout.
  389. You can also run the unit tests with the following command::
  390. python tests/run_tests.py
  391. If you have `tox`_ installed you can simply call tox inside the root directory
  392. and it will run the tests in multiple versions of Python.
  393. .. _`tox`: http://pypi.python.org/pypi/tox
  394. FAQ
  395. ===
  396. **Why can't SAML be implemented as an Django Authentication Backend?**
  397. well SAML authentication is not that simple as a set of credentials you can
  398. put on a login form and get a response back. Actually the user password is
  399. not given to the service provider at all. This is by design. You have to
  400. delegate the task of authentication to the IdP and then get an asynchronous
  401. response from it.
  402. Given said that, djangosaml2 does use a Django Authentication Backend to
  403. transform the SAML assertion about the user into a Django user object.
  404. **Why not put everything in a Django middleware class and make our lifes
  405. easier?**
  406. Yes, that was an option I did evaluate but at the end the current design
  407. won. In my opinion putting this logic into a middleware has the advantage
  408. of making it easier to configure but has a couple of disadvantages: first,
  409. the middleware would need to check if the request path is one of the
  410. SAML endpoints for every request. Second, it would be too magical and in
  411. case of a problem, much harder to debug.
  412. **Why not call this package django-saml as many other Django applications?**
  413. Following that pattern then I should import the application with
  414. import saml but unfortunately that module name is already used in pysaml2.
  415. Keywords: django,pysaml2,sso,saml2,federated authentication,authentication
  416. Platform: UNKNOWN
  417. Classifier: Development Status :: 4 - Beta
  418. Classifier: Environment :: Web Environment
  419. Classifier: Framework :: Django
  420. Classifier: Framework :: Django :: 1.8
  421. Classifier: Framework :: Django :: 1.9
  422. Classifier: Framework :: Django :: 1.10
  423. Classifier: Framework :: Django :: 1.11
  424. Classifier: Framework :: Django :: 2.0
  425. Classifier: Framework :: Django :: 2.1
  426. Classifier: Framework :: Django :: 2.2
  427. Classifier: Framework :: Django :: 3.0
  428. Classifier: Intended Audience :: Developers
  429. Classifier: License :: OSI Approved :: Apache Software License
  430. Classifier: Operating System :: OS Independent
  431. Classifier: Programming Language :: Python
  432. Classifier: Programming Language :: Python :: 2
  433. Classifier: Programming Language :: Python :: 2.7
  434. Classifier: Programming Language :: Python :: 3
  435. Classifier: Programming Language :: Python :: 3.5
  436. Classifier: Programming Language :: Python :: 3.6
  437. Classifier: Programming Language :: Python :: 3.7
  438. Classifier: Topic :: Internet :: WWW/HTTP
  439. Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
  440. Classifier: Topic :: Security
  441. Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
  442. Provides-Extra: test