First bits of documentation using Sphinx.
|
@ -0,0 +1,153 @@
|
|||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Authentic2.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Authentic2.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/Authentic2"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Authentic2"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
|
@ -0,0 +1,295 @@
|
|||
.. _README:
|
||||
|
||||
======================================
|
||||
Authentic2 - Versatile Identity Server
|
||||
======================================
|
||||
|
||||
Authentic2 is a versatile identity provider aiming to address a broad
|
||||
range of needs, from simple to complex setups; it has support for many
|
||||
protocols and can bridge between them.
|
||||
|
||||
Authentic2 is under the GNU AGPL version 3 licence.
|
||||
|
||||
It has support for SAMLv2 thanks to Lasso, a free (GNU GPL)
|
||||
implementation of the Liberty Alliance specifications.
|
||||
|
||||
Features
|
||||
========
|
||||
|
||||
Authentic can authenticate users against:
|
||||
- an LDAP directory,
|
||||
- a SAML 2.0 identity provider,
|
||||
- an OpenID identity provider,
|
||||
- with an X509 certificate.
|
||||
|
||||
Authentic can provide authentication to web applications using the following
|
||||
protocols:
|
||||
- OpenID,
|
||||
- SAML 2.0,
|
||||
- CAS 1.0 & CAS 2.0.
|
||||
|
||||
Authentic can proxy authentication between any two different protocols it
|
||||
support.
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
You must install the following packages to use Authentic
|
||||
|
||||
- Python Lasso binding 2.3.5:
|
||||
|
||||
From sources: http://lasso.entrouvert.org/download
|
||||
Debian based distribution: apt-get install python-lasso
|
||||
|
||||
- Django 1.3:
|
||||
|
||||
From sources: http://www.djangoproject.com/download/1.3/tarball/
|
||||
|
||||
- Django-registration 0.8-alpha-1:
|
||||
|
||||
From sources: http://bitbucket.org/ubernostrum/django-registration/downloads
|
||||
Debian based distribution: apt-get install python-django-registration
|
||||
|
||||
- Django-authopenid 0.9.6:
|
||||
|
||||
From sources: http://bitbucket.org/benoitc/django-authopenid/downloads
|
||||
|
||||
- Django-south 0.7.3:
|
||||
|
||||
From sources:: http://south.aeracode.org/docs/installation.html
|
||||
|
||||
- Django-profiles 0.2:
|
||||
|
||||
From sources:: http://pypi.python.org/pypi/django-profiles
|
||||
|
||||
You install all the django libraries quickly using pip::
|
||||
|
||||
pip install django django-profiles django-registration \
|
||||
django-debug-toolbar django-authopenid south
|
||||
|
||||
or easy_install::
|
||||
|
||||
easy_install django django-profiles django-registration \
|
||||
django-debug-toolbar django-authopenid south
|
||||
|
||||
Quick Start
|
||||
-----------
|
||||
|
||||
Then launch the following commands::
|
||||
|
||||
python manage.py syncdb --migrate
|
||||
python manage.py runserver
|
||||
|
||||
You should see the following output::
|
||||
|
||||
Validating models...
|
||||
0 errors found
|
||||
|
||||
Django version 1.2, using settings 'authentic.settings'
|
||||
Development server is running at http://127.0.0.1:8000/
|
||||
Quit the server with CONTROL-C.
|
||||
|
||||
You can access the running application on http://127.0.0.1:8000/
|
||||
|
||||
|
||||
Specifying a different database
|
||||
-------------------------------
|
||||
|
||||
This is done by modifying the DATABASES dictionary in your local_settings.py file
|
||||
(create it in Authentic project directory); for example::
|
||||
|
||||
DATABASES['default'] = {
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
'NAME': 'authentic',
|
||||
'USER': 'admindb',
|
||||
'PASSWORD': 'foobar',
|
||||
'HOST': 'db.example.com',
|
||||
'PORT': '', # empty string means default value
|
||||
}
|
||||
|
||||
You should refer to the Django documentation on databases settings at
|
||||
http://docs.djangoproject.com/en/dev/ref/settings/#databases for all
|
||||
the details.
|
||||
|
||||
How to upgrade to a new version of authentic ?
|
||||
----------------------------------------------
|
||||
|
||||
Authentic store all its data in a relational database as specified in its
|
||||
settings.py or local_settings.py file. So in order to upgrade to a new version
|
||||
of authentic you have to update your database schema using the
|
||||
migration command — you will need to have installed the dependency django-south,
|
||||
see the beginning of this README file.::
|
||||
|
||||
python ./manage.py migrate
|
||||
|
||||
Then you will need to create new tables if there are.::
|
||||
|
||||
python ./manage.py syncdb
|
||||
|
||||
Using Authentic with an LDAP directory
|
||||
======================================
|
||||
|
||||
Authentic use the module django_auth_ldap to synchronize the Django user tables
|
||||
with an LDAP. For complex use case, we will refer you to the django_auth_ldap
|
||||
documentation, see http://packages.python.org/django-auth-ldap/.
|
||||
|
||||
How to authenticate users against an LDAP server with anonymous binding ?
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
1. Install the django_auth_ldap module for Django::
|
||||
|
||||
pip install django_auth_ldap
|
||||
|
||||
|
||||
2. Configure your local_settings.py file for authenticating against LDAP.
|
||||
|
||||
The next lines must be added::
|
||||
|
||||
AUTHENTICATION_BACKENDS += ( 'django_auth_ldap.backend.LDAPBackend', )
|
||||
|
||||
import ldap
|
||||
from django_auth_ldap.config import LDAPSearch
|
||||
|
||||
# Here put the LDAP URL of your server
|
||||
AUTH_LDAP_SERVER_URI = 'ldap://ldap.example.com'
|
||||
# Let the bind DN and bind password blank for anonymous binding
|
||||
AUTH_LDAP_BIND_DN = ""
|
||||
AUTH_LDAP_BIND_PASSWORD = ""
|
||||
# Lookup user under the branch o=base and by mathcing their uid against the
|
||||
# received login name
|
||||
AUTH_LDAP_USER_SEARCH = LDAPSearch("o=base",
|
||||
ldap.SCOPE_SUBTREE, "(uid=%(user)s)")
|
||||
|
||||
How to allow members of an LDAP group to manage Authentic ?
|
||||
-----------------------------------------------------------
|
||||
|
||||
1. First you must know the objectClass of groups in your LDAP schema, this FAQ
|
||||
will show you the configuration for two usual classes: groupOfNames and
|
||||
groupOfUniqueNames.
|
||||
|
||||
2. Find the relevant groupname. We will say it is: cn=admin,o=mycompany
|
||||
|
||||
3. Add the following lines::
|
||||
|
||||
from django_auth_ldap.config import GroupOfNamesType
|
||||
AUTH_LDAP_GROUP_TYPE = GroupOfNamesType()
|
||||
AUTH_LDAP_GROUP_SEARCH = LDAPSearch("o=mycompany",
|
||||
ldap.SCOPE_SUBTREE, "(objectClass=groupOfNames)")
|
||||
AUTH_LDAP_USER_FLAGS_BY_GROUP = {
|
||||
"is_staff": "cn=admin,o=mycompany"
|
||||
}
|
||||
|
||||
For an objectClass of groupOfUniqueNames you would change the string
|
||||
GroupOfNamesType to GroupOfUniqueNamesType and grouOfNames to
|
||||
groupOfUniqueNames. For more complex cases see the django_auth_ldap
|
||||
documentation.
|
||||
|
||||
SAML 2.0
|
||||
========
|
||||
|
||||
How to I authenticate against Authentic2 with a SAMLv2 service provider ?
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
1. Grab the SAML2 IdP metadata:
|
||||
|
||||
http[s]://your.domain.com/idp/saml2/metadata
|
||||
|
||||
2. And configure your service provider with it.
|
||||
|
||||
Go to the providers admin panel on:
|
||||
|
||||
http[s]://your.domain.com/admin/saml/libertyprovider/add/
|
||||
|
||||
There create a new provider using the service provider metadata and enable it
|
||||
as a service provider, you can customize some behaviours like the preferred
|
||||
assertion consumer or encryption for the NameID or the Assertion element.
|
||||
|
||||
CAS
|
||||
===
|
||||
|
||||
How to use Authentic2 as a CAS 1.0 or CAS 2.0 identity provider ?
|
||||
-----------------------------------------------------------------
|
||||
|
||||
1. Activate CAS IdP support in settings.py::
|
||||
|
||||
IDP_CAS = True
|
||||
|
||||
2. Then create the database table to hold CAS service tickets::
|
||||
|
||||
python authentic2/manage.py syncdb --migrate
|
||||
|
||||
3. Also configure authentic2 to authenticate against your LDAP directory (see
|
||||
above) if your want your user attributes to be accessible from your service,
|
||||
if it is not necessary you can use the normal relational database storage
|
||||
for you users.
|
||||
|
||||
4. Finally configure your service to point to the CAS endpoint at:
|
||||
|
||||
http[s]://your.domain.com/idp/cas/
|
||||
|
||||
5. If needed configure your service to resolve authenticated user with your
|
||||
LDAP directory (if user attributes are needed for your service)
|
||||
|
||||
|
||||
PAM authentication
|
||||
==================
|
||||
|
||||
This module is copied from https://bitbucket.org/wnielson/django-pam/ by Weston
|
||||
Nielson and the pam ctype module by Chris Atlee http://atlee.ca/software/pam/.
|
||||
|
||||
Add 'authentic2.vendor.dpam.backends.PAMBackend' to your
|
||||
``settings.py``::
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
...
|
||||
'authentic2.vendor.dpam.backends.PAMBackend',
|
||||
...
|
||||
)
|
||||
|
||||
Now you can login via the system-login credentials. If the user is
|
||||
successfully authenticated but has never logged-in before, a new ``User``
|
||||
object is created. By default this new ``User`` has both ``is_staff`` and
|
||||
``is_superuser`` set to ``False``. You can change this behavior by adding
|
||||
``PAM_IS_STAFF=True`` and ``PAM_IS_SUPERUSER`` in your ``settings.py`` file.
|
||||
|
||||
The default PAM service used is ``login`` but you can change it by setting the
|
||||
``PAM_SERVICE`` variable in your ``settings.py`` file.
|
||||
|
||||
Attribute Management in Authentic2
|
||||
==================================
|
||||
|
||||
See `attribute management <attribute_management.html>`_ page.
|
||||
|
||||
Roadmap
|
||||
=======
|
||||
|
||||
- All (or nearly) settings will be configurable from the /admin panels
|
||||
- Login page will remember user choices for authentication and authenticate
|
||||
the user passively using hidden iframes
|
||||
- After a logout no passive login will be done
|
||||
- CAS IdP will allow to whitelist service URL and proxy granting ticket URLs,
|
||||
and to refuse request from unkown URLs. It will also allow to use patterns
|
||||
as URLs.
|
||||
- Extended CAS 2.0, with SAML attribute inside the CAS 2.0 validated ticket.
|
||||
- A virtual LDAP directory based on the OpenLDAP socket backend would remove
|
||||
the need for a real LDAP directory to pass user attributes to CAS relying
|
||||
parties.
|
||||
- WS-Trust token service endpoint
|
||||
- Email forwarder, so that relying parties never get the real user email.
|
||||
- Support slo in the CAS logout endpoint
|
||||
|
||||
Copyright
|
||||
---------
|
||||
|
||||
Authentic is copyrighted by Entr'ouvert and is licensed through the GNU General
|
||||
Public Licence, version 2 or later. A copy of the whole license text is
|
||||
available in the COPYING file.
|
||||
|
||||
The OpenID IdP originates in the project django_openid_provider by Roman
|
||||
Barczy¿ski, which is under the Apache 2.0 licence. This imply that you must
|
||||
distribute authentic2 under the AGPL3 licence when distributing this part of the
|
||||
project which is the only AGPL licence version compatible with the Apache 2.0
|
||||
licence.
|
|
@ -0,0 +1,4 @@
|
|||
# Sphinx build info version 1
|
||||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
||||
config: 9a63a4e352955e4e1c10b0bedfb3135a
|
||||
tags: fbb0d17656682115ca4d033fb2f83ba1
|
|
@ -0,0 +1,421 @@
|
|||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Authentic2 - Versatile Identity Server — Authentic2 1.9.2 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '',
|
||||
VERSION: '1.9.2',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="top" title="Authentic2 1.9.2 documentation" href="index.html" />
|
||||
<link rel="prev" title="Welcome to Authentic2’s documentation!" href="index.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="index.html" title="Welcome to Authentic2’s documentation!"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
<div class="section" id="authentic2-versatile-identity-server">
|
||||
<span id="readme"></span><h1>Authentic2 - Versatile Identity Server<a class="headerlink" href="#authentic2-versatile-identity-server" title="Permalink to this headline">¶</a></h1>
|
||||
<p>Authentic2 is a versatile identity provider aiming to address a broad
|
||||
range of needs, from simple to complex setups; it has support for many
|
||||
protocols and can bridge between them.</p>
|
||||
<p>Authentic2 is under the GNU AGPL version 3 licence.</p>
|
||||
<p>It has support for SAMLv2 thanks to Lasso, a free (GNU GPL)
|
||||
implementation of the Liberty Alliance specifications.</p>
|
||||
<div class="section" id="features">
|
||||
<h2>Features<a class="headerlink" href="#features" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Authentic can authenticate users against:
|
||||
- an LDAP directory,
|
||||
- a SAML 2.0 identity provider,
|
||||
- an OpenID identity provider,
|
||||
- with an X509 certificate.</p>
|
||||
<p>Authentic can provide authentication to web applications using the following
|
||||
protocols:
|
||||
- OpenID,
|
||||
- SAML 2.0,
|
||||
- CAS 1.0 & CAS 2.0.</p>
|
||||
<p>Authentic can proxy authentication between any two different protocols it
|
||||
support.</p>
|
||||
</div>
|
||||
<div class="section" id="installation">
|
||||
<h2>Installation<a class="headerlink" href="#installation" title="Permalink to this headline">¶</a></h2>
|
||||
<div class="section" id="dependencies">
|
||||
<h3>Dependencies<a class="headerlink" href="#dependencies" title="Permalink to this headline">¶</a></h3>
|
||||
<p>You must install the following packages to use Authentic</p>
|
||||
<ul>
|
||||
<li><p class="first">Python Lasso binding 2.3.5:</p>
|
||||
<blockquote>
|
||||
<div><p>From sources: <a class="reference external" href="http://lasso.entrouvert.org/download">http://lasso.entrouvert.org/download</a>
|
||||
Debian based distribution: apt-get install python-lasso</p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><p class="first">Django 1.3:</p>
|
||||
<blockquote>
|
||||
<div><p>From sources: <a class="reference external" href="http://www.djangoproject.com/download/1.3/tarball/">http://www.djangoproject.com/download/1.3/tarball/</a></p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><p class="first">Django-registration 0.8-alpha-1:</p>
|
||||
<blockquote>
|
||||
<div><p>From sources: <a class="reference external" href="http://bitbucket.org/ubernostrum/django-registration/downloads">http://bitbucket.org/ubernostrum/django-registration/downloads</a>
|
||||
Debian based distribution: apt-get install python-django-registration</p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><p class="first">Django-authopenid 0.9.6:</p>
|
||||
<blockquote>
|
||||
<div><p>From sources: <a class="reference external" href="http://bitbucket.org/benoitc/django-authopenid/downloads">http://bitbucket.org/benoitc/django-authopenid/downloads</a></p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><p class="first">Django-south 0.7.3:</p>
|
||||
<blockquote>
|
||||
<div><p>From sources:: <a class="reference external" href="http://south.aeracode.org/docs/installation.html">http://south.aeracode.org/docs/installation.html</a></p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><p class="first">Django-profiles 0.2:</p>
|
||||
<blockquote>
|
||||
<div><p>From sources:: <a class="reference external" href="http://pypi.python.org/pypi/django-profiles">http://pypi.python.org/pypi/django-profiles</a></p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
</ul>
|
||||
<p>You install all the django libraries quickly using pip:</p>
|
||||
<div class="highlight-python"><pre>pip install django django-profiles django-registration \
|
||||
django-debug-toolbar django-authopenid south</pre>
|
||||
</div>
|
||||
<p>or easy_install:</p>
|
||||
<div class="highlight-python"><pre>easy_install django django-profiles django-registration \
|
||||
django-debug-toolbar django-authopenid south</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="quick-start">
|
||||
<h3>Quick Start<a class="headerlink" href="#quick-start" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Then launch the following commands:</p>
|
||||
<div class="highlight-python"><pre>python manage.py syncdb --migrate
|
||||
python manage.py runserver</pre>
|
||||
</div>
|
||||
<p>You should see the following output:</p>
|
||||
<div class="highlight-python"><pre>Validating models...
|
||||
0 errors found
|
||||
|
||||
Django version 1.2, using settings 'authentic.settings'
|
||||
Development server is running at http://127.0.0.1:8000/
|
||||
Quit the server with CONTROL-C.
|
||||
|
||||
You can access the running application on http://127.0.0.1:8000/</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="specifying-a-different-database">
|
||||
<h3>Specifying a different database<a class="headerlink" href="#specifying-a-different-database" title="Permalink to this headline">¶</a></h3>
|
||||
<p>This is done by modifying the DATABASES dictionary in your local_settings.py file
|
||||
(create it in Authentic project directory); for example:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre><span class="n">DATABASES</span><span class="p">[</span><span class="s">'default'</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s">'ENGINE'</span><span class="p">:</span> <span class="s">'django.db.backends.postgresql'</span><span class="p">,</span>
|
||||
<span class="s">'NAME'</span><span class="p">:</span> <span class="s">'authentic'</span><span class="p">,</span>
|
||||
<span class="s">'USER'</span><span class="p">:</span> <span class="s">'admindb'</span><span class="p">,</span>
|
||||
<span class="s">'PASSWORD'</span><span class="p">:</span> <span class="s">'foobar'</span><span class="p">,</span>
|
||||
<span class="s">'HOST'</span><span class="p">:</span> <span class="s">'db.example.com'</span><span class="p">,</span>
|
||||
<span class="s">'PORT'</span><span class="p">:</span> <span class="s">''</span><span class="p">,</span> <span class="c"># empty string means default value</span>
|
||||
<span class="p">}</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You should refer to the Django documentation on databases settings at
|
||||
<a class="reference external" href="http://docs.djangoproject.com/en/dev/ref/settings/#databases">http://docs.djangoproject.com/en/dev/ref/settings/#databases</a> for all
|
||||
the details.</p>
|
||||
</div>
|
||||
<div class="section" id="how-to-upgrade-to-a-new-version-of-authentic">
|
||||
<h3>How to upgrade to a new version of authentic ?<a class="headerlink" href="#how-to-upgrade-to-a-new-version-of-authentic" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Authentic store all its data in a relational database as specified in its
|
||||
settings.py or local_settings.py file. So in order to upgrade to a new version
|
||||
of authentic you have to update your database schema using the
|
||||
migration command — you will need to have installed the dependency django-south,
|
||||
see the beginning of this README file.:</p>
|
||||
<div class="highlight-python"><pre>python ./manage.py migrate</pre>
|
||||
</div>
|
||||
<p>Then you will need to create new tables if there are.:</p>
|
||||
<div class="highlight-python"><pre>python ./manage.py syncdb</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="using-authentic-with-an-ldap-directory">
|
||||
<h2>Using Authentic with an LDAP directory<a class="headerlink" href="#using-authentic-with-an-ldap-directory" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Authentic use the module django_auth_ldap to synchronize the Django user tables
|
||||
with an LDAP. For complex use case, we will refer you to the django_auth_ldap
|
||||
documentation, see <a class="reference external" href="http://packages.python.org/django-auth-ldap/">http://packages.python.org/django-auth-ldap/</a>.</p>
|
||||
<div class="section" id="how-to-authenticate-users-against-an-ldap-server-with-anonymous-binding">
|
||||
<h3>How to authenticate users against an LDAP server with anonymous binding ?<a class="headerlink" href="#how-to-authenticate-users-against-an-ldap-server-with-anonymous-binding" title="Permalink to this headline">¶</a></h3>
|
||||
<ol class="arabic">
|
||||
<li><p class="first">Install the django_auth_ldap module for Django:</p>
|
||||
<div class="highlight-python"><pre>pip install django_auth_ldap</pre>
|
||||
</div>
|
||||
</li>
|
||||
<li><p class="first">Configure your local_settings.py file for authenticating against LDAP.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>The next lines must be added:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre><span class="n">AUTHENTICATION_BACKENDS</span> <span class="o">+=</span> <span class="p">(</span> <span class="s">'django_auth_ldap.backend.LDAPBackend'</span><span class="p">,</span> <span class="p">)</span>
|
||||
|
||||
<span class="kn">import</span> <span class="nn">ldap</span>
|
||||
<span class="kn">from</span> <span class="nn">django_auth_ldap.config</span> <span class="kn">import</span> <span class="n">LDAPSearch</span>
|
||||
|
||||
<span class="c"># Here put the LDAP URL of your server</span>
|
||||
<span class="n">AUTH_LDAP_SERVER_URI</span> <span class="o">=</span> <span class="s">'ldap://ldap.example.com'</span>
|
||||
<span class="c"># Let the bind DN and bind password blank for anonymous binding</span>
|
||||
<span class="n">AUTH_LDAP_BIND_DN</span> <span class="o">=</span> <span class="s">""</span>
|
||||
<span class="n">AUTH_LDAP_BIND_PASSWORD</span> <span class="o">=</span> <span class="s">""</span>
|
||||
<span class="c"># Lookup user under the branch o=base and by mathcing their uid against the</span>
|
||||
<span class="c"># received login name</span>
|
||||
<span class="n">AUTH_LDAP_USER_SEARCH</span> <span class="o">=</span> <span class="n">LDAPSearch</span><span class="p">(</span><span class="s">"o=base"</span><span class="p">,</span>
|
||||
<span class="n">ldap</span><span class="o">.</span><span class="n">SCOPE_SUBTREE</span><span class="p">,</span> <span class="s">"(uid=</span><span class="si">%(user)s</span><span class="s">)"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="how-to-allow-members-of-an-ldap-group-to-manage-authentic">
|
||||
<h3>How to allow members of an LDAP group to manage Authentic ?<a class="headerlink" href="#how-to-allow-members-of-an-ldap-group-to-manage-authentic" title="Permalink to this headline">¶</a></h3>
|
||||
<ol class="arabic">
|
||||
<li><p class="first">First you must know the objectClass of groups in your LDAP schema, this FAQ
|
||||
will show you the configuration for two usual classes: groupOfNames and
|
||||
groupOfUniqueNames.</p>
|
||||
</li>
|
||||
<li><p class="first">Find the relevant groupname. We will say it is: cn=admin,o=mycompany</p>
|
||||
</li>
|
||||
<li><p class="first">Add the following lines:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">django_auth_ldap.config</span> <span class="kn">import</span> <span class="n">GroupOfNamesType</span>
|
||||
<span class="n">AUTH_LDAP_GROUP_TYPE</span> <span class="o">=</span> <span class="n">GroupOfNamesType</span><span class="p">()</span>
|
||||
<span class="n">AUTH_LDAP_GROUP_SEARCH</span> <span class="o">=</span> <span class="n">LDAPSearch</span><span class="p">(</span><span class="s">"o=mycompany"</span><span class="p">,</span>
|
||||
<span class="n">ldap</span><span class="o">.</span><span class="n">SCOPE_SUBTREE</span><span class="p">,</span> <span class="s">"(objectClass=groupOfNames)"</span><span class="p">)</span>
|
||||
<span class="n">AUTH_LDAP_USER_FLAGS_BY_GROUP</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s">"is_staff"</span><span class="p">:</span> <span class="s">"cn=admin,o=mycompany"</span>
|
||||
<span class="p">}</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
<p>For an objectClass of groupOfUniqueNames you would change the string
|
||||
GroupOfNamesType to GroupOfUniqueNamesType and grouOfNames to
|
||||
groupOfUniqueNames. For more complex cases see the django_auth_ldap
|
||||
documentation.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="saml-2-0">
|
||||
<h2>SAML 2.0<a class="headerlink" href="#saml-2-0" title="Permalink to this headline">¶</a></h2>
|
||||
<div class="section" id="how-to-i-authenticate-against-authentic2-with-a-samlv2-service-provider">
|
||||
<h3>How to I authenticate against Authentic2 with a SAMLv2 service provider ?<a class="headerlink" href="#how-to-i-authenticate-against-authentic2-with-a-samlv2-service-provider" title="Permalink to this headline">¶</a></h3>
|
||||
<ol class="arabic">
|
||||
<li><p class="first">Grab the SAML2 IdP metadata:</p>
|
||||
<blockquote>
|
||||
<div><p>http[s]://your.domain.com/idp/saml2/metadata</p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><p class="first">And configure your service provider with it.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>Go to the providers admin panel on:</p>
|
||||
<blockquote>
|
||||
<div>http[s]://your.domain.com/admin/saml/libertyprovider/add/</div></blockquote>
|
||||
<p>There create a new provider using the service provider metadata and enable it
|
||||
as a service provider, you can customize some behaviours like the preferred
|
||||
assertion consumer or encryption for the NameID or the Assertion element.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="cas">
|
||||
<h2>CAS<a class="headerlink" href="#cas" title="Permalink to this headline">¶</a></h2>
|
||||
<div class="section" id="how-to-use-authentic2-as-a-cas-1-0-or-cas-2-0-identity-provider">
|
||||
<h3>How to use Authentic2 as a CAS 1.0 or CAS 2.0 identity provider ?<a class="headerlink" href="#how-to-use-authentic2-as-a-cas-1-0-or-cas-2-0-identity-provider" title="Permalink to this headline">¶</a></h3>
|
||||
<ol class="arabic">
|
||||
<li><p class="first">Activate CAS IdP support in settings.py:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre><span class="n">IDP_CAS</span> <span class="o">=</span> <span class="bp">True</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</li>
|
||||
<li><p class="first">Then create the database table to hold CAS service tickets:</p>
|
||||
<div class="highlight-python"><pre>python authentic2/manage.py syncdb --migrate</pre>
|
||||
</div>
|
||||
</li>
|
||||
<li><p class="first">Also configure authentic2 to authenticate against your LDAP directory (see
|
||||
above) if your want your user attributes to be accessible from your service,
|
||||
if it is not necessary you can use the normal relational database storage
|
||||
for you users.</p>
|
||||
</li>
|
||||
<li><p class="first">Finally configure your service to point to the CAS endpoint at:</p>
|
||||
<blockquote>
|
||||
<div><p>http[s]://your.domain.com/idp/cas/</p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><p class="first">If needed configure your service to resolve authenticated user with your
|
||||
LDAP directory (if user attributes are needed for your service)</p>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="pam-authentication">
|
||||
<h2>PAM authentication<a class="headerlink" href="#pam-authentication" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This module is copied from <a class="reference external" href="https://bitbucket.org/wnielson/django-pam/">https://bitbucket.org/wnielson/django-pam/</a> by Weston
|
||||
Nielson and the pam ctype module by Chris Atlee <a class="reference external" href="http://atlee.ca/software/pam/">http://atlee.ca/software/pam/</a>.</p>
|
||||
<p>Add ‘authentic2.vendor.dpam.backends.PAMBackend’ to your
|
||||
<tt class="docutils literal"><span class="pre">settings.py</span></tt>:</p>
|
||||
<div class="highlight-python"><pre>AUTHENTICATION_BACKENDS = (
|
||||
...
|
||||
'authentic2.vendor.dpam.backends.PAMBackend',
|
||||
...
|
||||
)</pre>
|
||||
</div>
|
||||
<p>Now you can login via the system-login credentials. If the user is
|
||||
successfully authenticated but has never logged-in before, a new <tt class="docutils literal"><span class="pre">User</span></tt>
|
||||
object is created. By default this new <tt class="docutils literal"><span class="pre">User</span></tt> has both <tt class="docutils literal"><span class="pre">is_staff</span></tt> and
|
||||
<tt class="docutils literal"><span class="pre">is_superuser</span></tt> set to <tt class="docutils literal"><span class="pre">False</span></tt>. You can change this behavior by adding
|
||||
<tt class="docutils literal"><span class="pre">PAM_IS_STAFF=True</span></tt> and <tt class="docutils literal"><span class="pre">PAM_IS_SUPERUSER</span></tt> in your <tt class="docutils literal"><span class="pre">settings.py</span></tt> file.</p>
|
||||
<p>The default PAM service used is <tt class="docutils literal"><span class="pre">login</span></tt> but you can change it by setting the
|
||||
<tt class="docutils literal"><span class="pre">PAM_SERVICE</span></tt> variable in your <tt class="docutils literal"><span class="pre">settings.py</span></tt> file.</p>
|
||||
</div>
|
||||
<div class="section" id="attribute-management-in-authentic2">
|
||||
<h2>Attribute Management in Authentic2<a class="headerlink" href="#attribute-management-in-authentic2" title="Permalink to this headline">¶</a></h2>
|
||||
<p>See <a class="reference external" href="attribute_management.html">attribute management</a> page.</p>
|
||||
</div>
|
||||
<div class="section" id="roadmap">
|
||||
<h2>Roadmap<a class="headerlink" href="#roadmap" title="Permalink to this headline">¶</a></h2>
|
||||
<blockquote>
|
||||
<div><ul class="simple">
|
||||
<li>All (or nearly) settings will be configurable from the /admin panels</li>
|
||||
<li>Login page will remember user choices for authentication and authenticate
|
||||
the user passively using hidden iframes</li>
|
||||
<li>After a logout no passive login will be done</li>
|
||||
<li>CAS IdP will allow to whitelist service URL and proxy granting ticket URLs,
|
||||
and to refuse request from unkown URLs. It will also allow to use patterns
|
||||
as URLs.</li>
|
||||
<li>Extended CAS 2.0, with SAML attribute inside the CAS 2.0 validated ticket.</li>
|
||||
<li>A virtual LDAP directory based on the OpenLDAP socket backend would remove
|
||||
the need for a real LDAP directory to pass user attributes to CAS relying
|
||||
parties.</li>
|
||||
<li>WS-Trust token service endpoint</li>
|
||||
<li>Email forwarder, so that relying parties never get the real user email.</li>
|
||||
<li>Support slo in the CAS logout endpoint</li>
|
||||
</ul>
|
||||
</div></blockquote>
|
||||
<div class="section" id="copyright">
|
||||
<h3>Copyright<a class="headerlink" href="#copyright" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Authentic is copyrighted by Entr’ouvert and is licensed through the GNU General
|
||||
Public Licence, version 2 or later. A copy of the whole license text is
|
||||
available in the COPYING file.</p>
|
||||
<p>The OpenID IdP originates in the project django_openid_provider by Roman
|
||||
Barczy¿ski, which is under the Apache 2.0 licence. This imply that you must
|
||||
distribute authentic2 under the AGPL3 licence when distributing this part of the
|
||||
project which is the only AGPL licence version compatible with the Apache 2.0
|
||||
licence.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h3><a href="index.html">Table Of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Authentic2 - Versatile Identity Server</a><ul>
|
||||
<li><a class="reference internal" href="#features">Features</a></li>
|
||||
<li><a class="reference internal" href="#installation">Installation</a><ul>
|
||||
<li><a class="reference internal" href="#dependencies">Dependencies</a></li>
|
||||
<li><a class="reference internal" href="#quick-start">Quick Start</a></li>
|
||||
<li><a class="reference internal" href="#specifying-a-different-database">Specifying a different database</a></li>
|
||||
<li><a class="reference internal" href="#how-to-upgrade-to-a-new-version-of-authentic">How to upgrade to a new version of authentic ?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#using-authentic-with-an-ldap-directory">Using Authentic with an LDAP directory</a><ul>
|
||||
<li><a class="reference internal" href="#how-to-authenticate-users-against-an-ldap-server-with-anonymous-binding">How to authenticate users against an LDAP server with anonymous binding ?</a></li>
|
||||
<li><a class="reference internal" href="#how-to-allow-members-of-an-ldap-group-to-manage-authentic">How to allow members of an LDAP group to manage Authentic ?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#saml-2-0">SAML 2.0</a><ul>
|
||||
<li><a class="reference internal" href="#how-to-i-authenticate-against-authentic2-with-a-samlv2-service-provider">How to I authenticate against Authentic2 with a SAMLv2 service provider ?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#cas">CAS</a><ul>
|
||||
<li><a class="reference internal" href="#how-to-use-authentic2-as-a-cas-1-0-or-cas-2-0-identity-provider">How to use Authentic2 as a CAS 1.0 or CAS 2.0 identity provider ?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#pam-authentication">PAM authentication</a></li>
|
||||
<li><a class="reference internal" href="#attribute-management-in-authentic2">Attribute Management in Authentic2</a></li>
|
||||
<li><a class="reference internal" href="#roadmap">Roadmap</a><ul>
|
||||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="index.html"
|
||||
title="previous chapter">Welcome to Authentic2’s documentation!</a></p>
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/README.txt"
|
||||
rel="nofollow">Show Source</a></li>
|
||||
</ul>
|
||||
<div id="searchbox" style="display: none">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<input type="text" name="q" />
|
||||
<input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
<p class="searchtip" style="font-size: 90%">
|
||||
Enter search terms or a module, class or function name.
|
||||
</p>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="index.html" title="Welcome to Authentic2’s documentation!"
|
||||
>previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2011, Mikaël Ates.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 41 KiB |
|
@ -0,0 +1,295 @@
|
|||
.. _README:
|
||||
|
||||
======================================
|
||||
Authentic2 - Versatile Identity Server
|
||||
======================================
|
||||
|
||||
Authentic2 is a versatile identity provider aiming to address a broad
|
||||
range of needs, from simple to complex setups; it has support for many
|
||||
protocols and can bridge between them.
|
||||
|
||||
Authentic2 is under the GNU AGPL version 3 licence.
|
||||
|
||||
It has support for SAMLv2 thanks to Lasso, a free (GNU GPL)
|
||||
implementation of the Liberty Alliance specifications.
|
||||
|
||||
Features
|
||||
========
|
||||
|
||||
Authentic can authenticate users against:
|
||||
- an LDAP directory,
|
||||
- a SAML 2.0 identity provider,
|
||||
- an OpenID identity provider,
|
||||
- with an X509 certificate.
|
||||
|
||||
Authentic can provide authentication to web applications using the following
|
||||
protocols:
|
||||
- OpenID,
|
||||
- SAML 2.0,
|
||||
- CAS 1.0 & CAS 2.0.
|
||||
|
||||
Authentic can proxy authentication between any two different protocols it
|
||||
support.
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
You must install the following packages to use Authentic
|
||||
|
||||
- Python Lasso binding 2.3.5:
|
||||
|
||||
From sources: http://lasso.entrouvert.org/download
|
||||
Debian based distribution: apt-get install python-lasso
|
||||
|
||||
- Django 1.3:
|
||||
|
||||
From sources: http://www.djangoproject.com/download/1.3/tarball/
|
||||
|
||||
- Django-registration 0.8-alpha-1:
|
||||
|
||||
From sources: http://bitbucket.org/ubernostrum/django-registration/downloads
|
||||
Debian based distribution: apt-get install python-django-registration
|
||||
|
||||
- Django-authopenid 0.9.6:
|
||||
|
||||
From sources: http://bitbucket.org/benoitc/django-authopenid/downloads
|
||||
|
||||
- Django-south 0.7.3:
|
||||
|
||||
From sources:: http://south.aeracode.org/docs/installation.html
|
||||
|
||||
- Django-profiles 0.2:
|
||||
|
||||
From sources:: http://pypi.python.org/pypi/django-profiles
|
||||
|
||||
You install all the django libraries quickly using pip::
|
||||
|
||||
pip install django django-profiles django-registration \
|
||||
django-debug-toolbar django-authopenid south
|
||||
|
||||
or easy_install::
|
||||
|
||||
easy_install django django-profiles django-registration \
|
||||
django-debug-toolbar django-authopenid south
|
||||
|
||||
Quick Start
|
||||
-----------
|
||||
|
||||
Then launch the following commands::
|
||||
|
||||
python manage.py syncdb --migrate
|
||||
python manage.py runserver
|
||||
|
||||
You should see the following output::
|
||||
|
||||
Validating models...
|
||||
0 errors found
|
||||
|
||||
Django version 1.2, using settings 'authentic.settings'
|
||||
Development server is running at http://127.0.0.1:8000/
|
||||
Quit the server with CONTROL-C.
|
||||
|
||||
You can access the running application on http://127.0.0.1:8000/
|
||||
|
||||
|
||||
Specifying a different database
|
||||
-------------------------------
|
||||
|
||||
This is done by modifying the DATABASES dictionary in your local_settings.py file
|
||||
(create it in Authentic project directory); for example::
|
||||
|
||||
DATABASES['default'] = {
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
'NAME': 'authentic',
|
||||
'USER': 'admindb',
|
||||
'PASSWORD': 'foobar',
|
||||
'HOST': 'db.example.com',
|
||||
'PORT': '', # empty string means default value
|
||||
}
|
||||
|
||||
You should refer to the Django documentation on databases settings at
|
||||
http://docs.djangoproject.com/en/dev/ref/settings/#databases for all
|
||||
the details.
|
||||
|
||||
How to upgrade to a new version of authentic ?
|
||||
----------------------------------------------
|
||||
|
||||
Authentic store all its data in a relational database as specified in its
|
||||
settings.py or local_settings.py file. So in order to upgrade to a new version
|
||||
of authentic you have to update your database schema using the
|
||||
migration command — you will need to have installed the dependency django-south,
|
||||
see the beginning of this README file.::
|
||||
|
||||
python ./manage.py migrate
|
||||
|
||||
Then you will need to create new tables if there are.::
|
||||
|
||||
python ./manage.py syncdb
|
||||
|
||||
Using Authentic with an LDAP directory
|
||||
======================================
|
||||
|
||||
Authentic use the module django_auth_ldap to synchronize the Django user tables
|
||||
with an LDAP. For complex use case, we will refer you to the django_auth_ldap
|
||||
documentation, see http://packages.python.org/django-auth-ldap/.
|
||||
|
||||
How to authenticate users against an LDAP server with anonymous binding ?
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
1. Install the django_auth_ldap module for Django::
|
||||
|
||||
pip install django_auth_ldap
|
||||
|
||||
|
||||
2. Configure your local_settings.py file for authenticating against LDAP.
|
||||
|
||||
The next lines must be added::
|
||||
|
||||
AUTHENTICATION_BACKENDS += ( 'django_auth_ldap.backend.LDAPBackend', )
|
||||
|
||||
import ldap
|
||||
from django_auth_ldap.config import LDAPSearch
|
||||
|
||||
# Here put the LDAP URL of your server
|
||||
AUTH_LDAP_SERVER_URI = 'ldap://ldap.example.com'
|
||||
# Let the bind DN and bind password blank for anonymous binding
|
||||
AUTH_LDAP_BIND_DN = ""
|
||||
AUTH_LDAP_BIND_PASSWORD = ""
|
||||
# Lookup user under the branch o=base and by mathcing their uid against the
|
||||
# received login name
|
||||
AUTH_LDAP_USER_SEARCH = LDAPSearch("o=base",
|
||||
ldap.SCOPE_SUBTREE, "(uid=%(user)s)")
|
||||
|
||||
How to allow members of an LDAP group to manage Authentic ?
|
||||
-----------------------------------------------------------
|
||||
|
||||
1. First you must know the objectClass of groups in your LDAP schema, this FAQ
|
||||
will show you the configuration for two usual classes: groupOfNames and
|
||||
groupOfUniqueNames.
|
||||
|
||||
2. Find the relevant groupname. We will say it is: cn=admin,o=mycompany
|
||||
|
||||
3. Add the following lines::
|
||||
|
||||
from django_auth_ldap.config import GroupOfNamesType
|
||||
AUTH_LDAP_GROUP_TYPE = GroupOfNamesType()
|
||||
AUTH_LDAP_GROUP_SEARCH = LDAPSearch("o=mycompany",
|
||||
ldap.SCOPE_SUBTREE, "(objectClass=groupOfNames)")
|
||||
AUTH_LDAP_USER_FLAGS_BY_GROUP = {
|
||||
"is_staff": "cn=admin,o=mycompany"
|
||||
}
|
||||
|
||||
For an objectClass of groupOfUniqueNames you would change the string
|
||||
GroupOfNamesType to GroupOfUniqueNamesType and grouOfNames to
|
||||
groupOfUniqueNames. For more complex cases see the django_auth_ldap
|
||||
documentation.
|
||||
|
||||
SAML 2.0
|
||||
========
|
||||
|
||||
How to I authenticate against Authentic2 with a SAMLv2 service provider ?
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
1. Grab the SAML2 IdP metadata:
|
||||
|
||||
http[s]://your.domain.com/idp/saml2/metadata
|
||||
|
||||
2. And configure your service provider with it.
|
||||
|
||||
Go to the providers admin panel on:
|
||||
|
||||
http[s]://your.domain.com/admin/saml/libertyprovider/add/
|
||||
|
||||
There create a new provider using the service provider metadata and enable it
|
||||
as a service provider, you can customize some behaviours like the preferred
|
||||
assertion consumer or encryption for the NameID or the Assertion element.
|
||||
|
||||
CAS
|
||||
===
|
||||
|
||||
How to use Authentic2 as a CAS 1.0 or CAS 2.0 identity provider ?
|
||||
-----------------------------------------------------------------
|
||||
|
||||
1. Activate CAS IdP support in settings.py::
|
||||
|
||||
IDP_CAS = True
|
||||
|
||||
2. Then create the database table to hold CAS service tickets::
|
||||
|
||||
python authentic2/manage.py syncdb --migrate
|
||||
|
||||
3. Also configure authentic2 to authenticate against your LDAP directory (see
|
||||
above) if your want your user attributes to be accessible from your service,
|
||||
if it is not necessary you can use the normal relational database storage
|
||||
for you users.
|
||||
|
||||
4. Finally configure your service to point to the CAS endpoint at:
|
||||
|
||||
http[s]://your.domain.com/idp/cas/
|
||||
|
||||
5. If needed configure your service to resolve authenticated user with your
|
||||
LDAP directory (if user attributes are needed for your service)
|
||||
|
||||
|
||||
PAM authentication
|
||||
==================
|
||||
|
||||
This module is copied from https://bitbucket.org/wnielson/django-pam/ by Weston
|
||||
Nielson and the pam ctype module by Chris Atlee http://atlee.ca/software/pam/.
|
||||
|
||||
Add 'authentic2.vendor.dpam.backends.PAMBackend' to your
|
||||
``settings.py``::
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
...
|
||||
'authentic2.vendor.dpam.backends.PAMBackend',
|
||||
...
|
||||
)
|
||||
|
||||
Now you can login via the system-login credentials. If the user is
|
||||
successfully authenticated but has never logged-in before, a new ``User``
|
||||
object is created. By default this new ``User`` has both ``is_staff`` and
|
||||
``is_superuser`` set to ``False``. You can change this behavior by adding
|
||||
``PAM_IS_STAFF=True`` and ``PAM_IS_SUPERUSER`` in your ``settings.py`` file.
|
||||
|
||||
The default PAM service used is ``login`` but you can change it by setting the
|
||||
``PAM_SERVICE`` variable in your ``settings.py`` file.
|
||||
|
||||
Attribute Management in Authentic2
|
||||
==================================
|
||||
|
||||
See `attribute management <attribute_management.html>`_ page.
|
||||
|
||||
Roadmap
|
||||
=======
|
||||
|
||||
- All (or nearly) settings will be configurable from the /admin panels
|
||||
- Login page will remember user choices for authentication and authenticate
|
||||
the user passively using hidden iframes
|
||||
- After a logout no passive login will be done
|
||||
- CAS IdP will allow to whitelist service URL and proxy granting ticket URLs,
|
||||
and to refuse request from unkown URLs. It will also allow to use patterns
|
||||
as URLs.
|
||||
- Extended CAS 2.0, with SAML attribute inside the CAS 2.0 validated ticket.
|
||||
- A virtual LDAP directory based on the OpenLDAP socket backend would remove
|
||||
the need for a real LDAP directory to pass user attributes to CAS relying
|
||||
parties.
|
||||
- WS-Trust token service endpoint
|
||||
- Email forwarder, so that relying parties never get the real user email.
|
||||
- Support slo in the CAS logout endpoint
|
||||
|
||||
Copyright
|
||||
---------
|
||||
|
||||
Authentic is copyrighted by Entr'ouvert and is licensed through the GNU General
|
||||
Public Licence, version 2 or later. A copy of the whole license text is
|
||||
available in the COPYING file.
|
||||
|
||||
The OpenID IdP originates in the project django_openid_provider by Roman
|
||||
Barczy¿ski, which is under the Apache 2.0 licence. This imply that you must
|
||||
distribute authentic2 under the AGPL3 licence when distributing this part of the
|
||||
project which is the only AGPL licence version compatible with the Apache 2.0
|
||||
licence.
|
|
@ -0,0 +1,653 @@
|
|||
.. _attribute_management:
|
||||
|
||||
==================================
|
||||
Attribute Management in Authentic2
|
||||
==================================
|
||||
|
||||
Summary
|
||||
=======
|
||||
|
||||
Attribute management currently allows to configure attribute policies
|
||||
associated with SAML2 service providers to define attributes that are
|
||||
pushed in SAML2 successful authentication response delivered by Authentic2.
|
||||
|
||||
User attributes can be taken from LDAP directories, the user Django
|
||||
profile or taken from the user Django session if Authentic2 is also configured
|
||||
as a SAML2 service provider.
|
||||
|
||||
Indeed, when Authentic2 acts also as a SAML2 service provider,
|
||||
attributes contained in the SAML2 assertion received from third IdP are put in
|
||||
the user session.
|
||||
|
||||
Attributes can thus be proxyfied during SSO with Authentic2
|
||||
configured as a SAML2 proxy.
|
||||
|
||||
The namespace of attributes received from another SAML2 IdP or pushed in the
|
||||
assertion given in to service providers can be configured per attribute or per
|
||||
service provider.
|
||||
|
||||
By default, the namespace and format of attributes in assertion is conformant
|
||||
to the SAMLV2.0 X500/LDAP Attribute profile::
|
||||
|
||||
<saml:Attribute
|
||||
xmlns:x500="urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500"
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
|
||||
Name="urn:oid:2.5.4.42" FriendlyName="givenName">
|
||||
<saml:AttributeValue xsi:type="xs:string"
|
||||
x500:Encoding="LDAP">Mikaël</saml:AttributeValue>
|
||||
</saml:Attribute>
|
||||
|
||||
But the http://schemas.xmlsoap.org/ws/2005/05/identity/claims from the ISI
|
||||
profile can also be used, for instance::
|
||||
|
||||
<saml:Attribute
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
|
||||
Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"
|
||||
FriendlyName="First Name">
|
||||
<saml:AttributeValue>Mikaël</saml:AttributeValue>
|
||||
</saml:Attribute>
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Configure sources of attributes
|
||||
-------------------------------
|
||||
|
||||
The source of attributes for authentic2 are of two kinds. The LDAP sources and
|
||||
the user django profile.
|
||||
|
||||
Declare the Django profile source
|
||||
_________________________________
|
||||
|
||||
Add an attribute source named USER_PROFILE with namespace 'Default'.
|
||||
|
||||
Then, it is necessary that users create their profile.
|
||||
|
||||
Add an LDAP Source
|
||||
__________________
|
||||
|
||||
For LDAP sources, objects of type 'LDAPSource' must be created.
|
||||
|
||||
**Even if the authentication is based on LDAP authentification, thus that a
|
||||
server is configured in settings.py, it is
|
||||
necessary to create a corresponding 'LDAPSource' to use it as a source of
|
||||
attribute.**
|
||||
|
||||
1. Go to http[s]://your.domain.com/admin/attribute_aggregator/ldapsource/add/
|
||||
|
||||
2. Fill form fields
|
||||
|
||||
Only the field Name, Server, User, Password, Base and Port are used for now.
|
||||
**The namespace of LDAP source must be kept to 'Default', since the system
|
||||
namespace is based on LDAP.**
|
||||
|
||||
.. image:: pictures/ldapsource.png
|
||||
:width: 800 px
|
||||
|
||||
3. Save
|
||||
|
||||
.. image:: pictures/ldapsource_saved.png
|
||||
:width: 800 px
|
||||
|
||||
Manage user distinguished names in LDAP directories
|
||||
___________________________________________________
|
||||
|
||||
To find the user in a LDAP directory, authentic2 must know its distinguished
|
||||
name (DN). If this LDAP has been used when the user has authenticated,
|
||||
Authentic2 learn the user DN. Nothing has to be done from this point of view.
|
||||
|
||||
However, if it is expected that user attributes be taken in a directory that
|
||||
is not used by the user for authentication, it is necessary to manually
|
||||
indicate to Authentic2 what is the user DN in the directory. For this, a
|
||||
user alias in source is created for the user:
|
||||
|
||||
1. Go to http[s]://your.domain.com/admin/attribute_aggregator/useraliasinsource/add/
|
||||
|
||||
2. Fill form fields
|
||||
|
||||
.. image:: pictures/alias_in_source.png
|
||||
:width: 800 px
|
||||
|
||||
3. Save
|
||||
|
||||
.. image:: pictures/alias_in_source_saved.png
|
||||
:width: 800 px
|
||||
|
||||
Configure attributes pushed to SAML2 service providers in SSO response
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Reminder:
|
||||
|
||||
- The default name format in SAML2 assertions is URI
|
||||
- The default namespace called 'Default' is LDAP
|
||||
|
||||
In summary:
|
||||
|
||||
1. Create attribute items indicating an attribute name, a source, the name format expected and the namespace expected for the attribute name and friendly name if any.
|
||||
|
||||
2. Create a named list of attribute items.
|
||||
|
||||
3. Create an attribute policy and associate the previous list or associate the previous list to a existing attribute policy.
|
||||
|
||||
4. Associate the policy to a service provider.
|
||||
|
||||
Create attribute items
|
||||
______________________
|
||||
|
||||
1. Go to http[s]://your.domain.com/admin/idp/attributeitem/add/
|
||||
|
||||
2. Fill form fields
|
||||
|
||||
.. image:: pictures/attribute_item.png
|
||||
:width: 800 px
|
||||
|
||||
3. Save
|
||||
|
||||
.. image:: pictures/attribute_item_saved.png
|
||||
:width: 800 px
|
||||
|
||||
Create a named list of attribute items
|
||||
______________________________________
|
||||
|
||||
1. Go to http[s]://your.domain.com/admin/idp/attributelist/add/
|
||||
|
||||
2. Name the list and add items to list
|
||||
|
||||
.. image:: pictures/attribute_list.png
|
||||
:width: 800 px
|
||||
|
||||
3. Save
|
||||
|
||||
.. image:: pictures/attribute_list_saved.png
|
||||
:width: 800 px
|
||||
|
||||
Create or modify an attribute policy
|
||||
____________________________________
|
||||
|
||||
1. Go to http[s]://your.domain.com/admin/idp/attributepolicy/add/
|
||||
|
||||
2. Add list to the policy
|
||||
|
||||
.. image:: pictures/policy_pull.png
|
||||
:width: 800 px
|
||||
|
||||
3. Save
|
||||
|
||||
.. image:: pictures/policy_pull_saved.png
|
||||
:width: 800 px
|
||||
|
||||
Associate the policy to a service provider
|
||||
__________________________________________
|
||||
|
||||
1. Go to http[s]://your.domain.com/admin/saml/libertyprovider/1/
|
||||
|
||||
2. Add policy to the service provider
|
||||
|
||||
.. image:: pictures/sp_policy_pull.png
|
||||
:width: 800 px
|
||||
|
||||
3. Save
|
||||
|
||||
.. image:: pictures/sp_policy_pull_saved.png
|
||||
:width: 800 px
|
||||
|
||||
4. The display name of the policy has changed
|
||||
|
||||
.. image:: pictures/policy_pull_renamed.png
|
||||
:width: 800 px
|
||||
|
||||
Handle attributes provided by other Identity providers, proxy attributes
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Link to configure first Authentic as a sp to have attributes in session
|
||||
|
||||
Add a source if mapping set to true
|
||||
|
||||
Modifying supported namespaces and attribute name mappings
|
||||
==========================================================
|
||||
|
||||
TBD
|
||||
|
||||
Explanation (Draft)
|
||||
===================
|
||||
|
||||
Attribute aggegrator module
|
||||
---------------------------
|
||||
|
||||
The core attribute management is based on the attribute aggregator module.
|
||||
|
||||
Intro
|
||||
_____
|
||||
|
||||
Attribute aggregator provides a main Model class called UserAttributeProfile,
|
||||
functions to load attributes and extract attributes.
|
||||
|
||||
The mapping between attribute namespaces is built-in and depends on a unique
|
||||
file (mapping.py).
|
||||
|
||||
A main schema is defined and is based on LDAP/X500 for naming. The support
|
||||
of http://schemas.xmlsoap.org/ws/2005/05/identity/claims is partly complete.
|
||||
|
||||
Source of attributes are connected with attribute loading functions using
|
||||
signals.
|
||||
|
||||
FAQ
|
||||
___
|
||||
|
||||
Why not use the Django User profile?
|
||||
|
||||
The django user profile needs to define attributes as class attributes and
|
||||
then support form filling or mapping with LDAP.
|
||||
|
||||
That is useful and may be used, especially because the profile can be used as
|
||||
a source of attribute to load in the attribute_aggregator profile.
|
||||
|
||||
The attribute_aggregator profile allow to load multivalued attributes from any
|
||||
source supported (LDAP, Django profile and attributes in Django session for
|
||||
now) from any namespace defined in mapping.py (LDAP/X500 and claims for now).
|
||||
|
||||
The profile can be loaded giving a source or a list of attribute, or can be
|
||||
from any known source, or with a dictionnary.
|
||||
|
||||
Attributes can be extracted with many functions in any namespace supported.
|
||||
|
||||
Quick explanation
|
||||
_________________
|
||||
|
||||
The schema is defined in mapping.py and is made of definitions like this::
|
||||
|
||||
"sn": {
|
||||
"oid": "2.5.4.4",
|
||||
"display_name": _("sn surname"),
|
||||
"alias": ['surname'],
|
||||
"profile_field_name": 'last_name',
|
||||
"type": "http://www.w3.org/2001/XMLSchema#string",
|
||||
"namespaces": {
|
||||
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims": {
|
||||
"identifiers":
|
||||
[
|
||||
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname",
|
||||
],
|
||||
"friendly_names":
|
||||
[
|
||||
"Last Name",
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
The profile store all the data in a text field taht contains a cPickle list of
|
||||
instances of the class AttributeData.
|
||||
|
||||
The profile is attached to a user and then can be created or loaded with::
|
||||
|
||||
profile = load_or_create_user_profile(user=user)
|
||||
|
||||
User may be None to create a temporary profile for an anonymous user. But
|
||||
that need a DB cleaning function not implemented.
|
||||
|
||||
The model *UserAttributeProfile*
|
||||
________________________________
|
||||
|
||||
The model 'UserAttributeProfile' can be attached to a user and then persist
|
||||
(as a Model).
|
||||
|
||||
When the profile is loaded, all data stored are removed expect if the
|
||||
the data has an expiration date later.
|
||||
|
||||
The profile provide several methods to store and extract attributes.
|
||||
|
||||
All the methods to add attributes are based on a main one accepting a
|
||||
dictionnary of attribute is parameters 'load_by_dic()'. The other methods
|
||||
('load_listed_attributes()', 'load_greedy()') send a signal with a list of
|
||||
attributes (listed_attributes_call) or not (any_attributes_call) to grab a
|
||||
dictionnary. The list is given with the definition name, oid or friendly name
|
||||
of the attribute in the system namespace.
|
||||
|
||||
Into the dictionnary, attributes are given with their name, oid or friendly
|
||||
name in the default namespace or with their name in a namepsace. An expiration
|
||||
date can also be given (ISO8601 format), if none, attribute will be deleted at
|
||||
next profile loading. The dictionnary format is as follows::
|
||||
|
||||
attributes = dict()
|
||||
data_from_source = list()
|
||||
a1 = dict()
|
||||
a1['oid'] = definition_name
|
||||
Or
|
||||
a1['definition'] = definition_name
|
||||
definition may be the definition name like 'gn'
|
||||
or an alias like 'givenName'
|
||||
Or
|
||||
a1['name'] = attribute_name_in_ns
|
||||
a1['namespace'] = ns_name
|
||||
a1['expiration_date'] = date
|
||||
a1['values'] = list_of_values
|
||||
data_from_source.append(a1)
|
||||
...
|
||||
data_from_source.append(a2)
|
||||
attributes[source_name] = data_from_source
|
||||
|
||||
Getters are defined to extract data from a profile. Only AttributeData
|
||||
instances are extracted that assume that any attribute namespace can be used.
|
||||
|
||||
* get_data_of_definition(definition)
|
||||
|
||||
Return a list of AttributeData instances corresponding to the definition
|
||||
given.
|
||||
|
||||
* get_freshest_data_of_definition(definition)
|
||||
|
||||
Return the freshest AttributeData instance. If multiple with no or same exp
|
||||
date, random. Should use the creation date soon.
|
||||
|
||||
* get_data_of_source
|
||||
|
||||
Return a list of AttributeData instances corresponding to the source given.
|
||||
|
||||
* get_data_of_source_by_name
|
||||
|
||||
Idem but source name is given, not a Source instance.
|
||||
|
||||
* get_data_of_definition_and_source
|
||||
|
||||
Return a list of AttributeData instances corresponding to the definition and
|
||||
source given.
|
||||
|
||||
* get_data_of_definition_and_source_by_name
|
||||
|
||||
Idem but source name is given, not a Source instance.
|
||||
|
||||
SAML2 attribute representation in assertions
|
||||
--------------------------------------------
|
||||
|
||||
SAML2 attribute profile (saml-profiles-2.0-os - Section 8) defines two kind of
|
||||
attribute element syntax in the attribute statement of assertions, also
|
||||
called *name format*:
|
||||
|
||||
- BASIC::
|
||||
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
|
||||
|
||||
- URI::
|
||||
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
|
||||
|
||||
*URI should be used when attributes have "universally" known unique names
|
||||
like OID.*
|
||||
|
||||
Example::
|
||||
|
||||
<saml:Attribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
|
||||
Name="FirstName">
|
||||
<saml:AttributeValue xsi:type="xs:string">By-Tor</saml:AttributeValue>
|
||||
</saml:Attribute>
|
||||
|
||||
<saml:Attribute
|
||||
xmlns:x500="urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500"
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
|
||||
Name="urn:oid:2.5.4.42" FriendlyName="givenName">
|
||||
<saml:AttributeValue xsi:type="xs:string"
|
||||
x500:Encoding="LDAP">Steven</saml:AttributeValue>
|
||||
</saml:Attribute>
|
||||
|
||||
|
||||
BASIC
|
||||
_____
|
||||
|
||||
|
||||
Two <Attribute> elements refer to the same SAML attribute if and only if the
|
||||
values of their Name XML attributes are equal in the sense of Section 3.3.6 of
|
||||
[Schema2].
|
||||
|
||||
No additional XML attributes are defined for use with the <Attribute> element.
|
||||
|
||||
The schema type of the contents of the <AttributeValue> element MUST be drawn
|
||||
from one of the types defined in Section 3.3 of [Schema2]. The xsi:type
|
||||
attribute MUST be present and be given the appropriate value.
|
||||
|
||||
X.500/LDAP Attribute Profile (URI)
|
||||
__________________________________
|
||||
|
||||
**Extracted from the SAML2 core specifications**
|
||||
|
||||
Two <Attribute> elements refer to the same SAML attribute if and only if their
|
||||
Name XML attribute values are equal in the sense of [RFC3061]. The
|
||||
FriendlyName attribute plays no role in the comparison.
|
||||
|
||||
Directory attribute type definitions for use in native X.500 directories
|
||||
specify the syntax of the attribute using ASN.1 [ASN.1]. For use in LDAP,
|
||||
directory attribute definitions additionally include an LDAP syntax which
|
||||
specifies how attribute or assertion values conforming to the syntax are to be
|
||||
represented when transferred in the LDAP protocol (known as an LDAP-specific
|
||||
encoding). The LDAP-specific encoding commonly produces Unicode characters in
|
||||
UTF-8 form. This SAML attribute profile specifies the form of SAML attribute
|
||||
values only for those directory attributes which have LDAP syntaxes. Future
|
||||
extensions to this profile may define attribute value formats for directory
|
||||
attributes whose syntaxes specify other encodings.
|
||||
|
||||
To represent the encoding rules in use for a particular attribute value, the
|
||||
<AttributeValue> element MUST contain an XML attribute named Encoding defined
|
||||
in the XML namespace urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500.
|
||||
|
||||
For any directory attribute with a syntax whose LDAP-specific encoding
|
||||
exclusively produces UTF-8 character strings as values, the SAML attribute
|
||||
value is encoded as simply the UTF-8 string itself, as the content of the
|
||||
<AttributeValue> element, with no additional whitespace.
|
||||
In such cases, the xsi:type XML attribute MUST be set to xs:string.
|
||||
The profile-specific Encoding XML attribute is provided, with a value of LDAP.
|
||||
|
||||
The AttributeData instances have a field expiration_data. It the profile
|
||||
exists, obsolete data are removed at loading.
|
||||
|
||||
|
||||
When authentic 2 deals with attributes and needs mapping?
|
||||
---------------------------------------------------------
|
||||
|
||||
Authentic2 behaves as an attribute provider:
|
||||
* At the SSO login
|
||||
* When an attribute request is received
|
||||
|
||||
Authentic requests (e.g. by soap) are not yet supported.
|
||||
|
||||
When Authentic2 behaves as an attribute provider at SSO login
|
||||
_____________________________________________________________
|
||||
|
||||
At a SSO request, just before responding to the service provider, the saml2
|
||||
idp module sends the signal 'add_attributes_to_response' giving the SP entity
|
||||
ID.
|
||||
|
||||
The signal is connected to the function 'provide_attributes_at_sso()' in
|
||||
charge of providing the attributes at the SSO for this SP.
|
||||
|
||||
**Attributes sources are of two kinds. The first ones are the sources that can
|
||||
be requested by the IdP with a syncrhonous binding without user intercations.
|
||||
These sources are called pull sources. They are for now limited to LDAP
|
||||
sources. The other ones are sources are asyncrhonous bindings, usually
|
||||
requiring user interactions. These sources are called push sources. They are
|
||||
now limited to the attributes provided at SSO requests when the IdP acts as a
|
||||
SAML2 SP. There attributes are put/found in the Django session.**
|
||||
|
||||
Each source in the system is declared with an instance of the AttributeSource
|
||||
model. We'll see later that to forward attributes of push sources it is not
|
||||
necessary that a source is declared in some circumstances.
|
||||
|
||||
To manage these sources an attribute policy is attached to services providers.
|
||||
Then the service provider model must be extended with a attribute
|
||||
attributes_at_sso_policy. The service provider must send the signal
|
||||
'add_attributes_to_response'.
|
||||
|
||||
The implementation is actually done for SAML2 providers.
|
||||
|
||||
**In such a policy attributes from pull and push sources are treated
|
||||
differently.**
|
||||
|
||||
**For pull sources, a list of attributes is indicated. Either an attribute is
|
||||
searched in all the pull sources and whatever attribute value found is
|
||||
returned. Or each attribute is indicated with a source. With each attribute is
|
||||
indicated the output format and namespace.**
|
||||
|
||||
**The policy may also indicate that all the attributes in the Django session
|
||||
must be forwarded. Then, no AttributeSource instance is required. All the
|
||||
attributes are then forwarded without treating input namespace considerations.
|
||||
When an AttributeSource instance is found, the input namespace of this source
|
||||
is considered. An option can then be set to tell that the output format and
|
||||
namespace must be taken. A list of attribute can also be given.
|
||||
This list can be use to filter attributes to forward without or without taking
|
||||
care of the source. The output namespace and format can also be trated per
|
||||
attribute.**
|
||||
|
||||
If the namespace is default, the attribute names will be taken from the
|
||||
system namespace. In BASIC the name will be the definition name. In URI, the
|
||||
Name will be the OID in urn format and the friendly name will be the
|
||||
definition name. If a namespace is given, the first identifier of this
|
||||
attribute is taken as Name in BASIC. In URI, the same and the first friendly
|
||||
name is taken.
|
||||
|
||||
::
|
||||
|
||||
class LibertyServiceProvider(models.Model):
|
||||
...
|
||||
attribute_policy = models.ForeignKey(AttributePolicy,
|
||||
verbose_name=_("Attribute policy"), null=True, blank=True)
|
||||
|
||||
class AttributePolicy(models.Model):
|
||||
# List of attributes to provide from pull sources at SSO Login.
|
||||
# If an attribute is indicate without a source, from any source.
|
||||
# The output format and namespace is given by each attribute.
|
||||
attribute_list_for_sso_from_pull_sources = \
|
||||
models.ForeignKey(LibertyAttributeMap,
|
||||
related_name = "attributes of pull sources",
|
||||
blank = True, null = True)
|
||||
|
||||
# Set to true for proxying attributes from pull sources at SSO Login.
|
||||
# Attributes are in session.
|
||||
# All attributes are forwarded as is except if the parameter
|
||||
# 'attribute_list_for_sso_from_push_sources' is initialized
|
||||
forward_attributes_from_pull_sources = models.BooleanField(default=False)
|
||||
|
||||
# Map attributes in session
|
||||
# forward_attributes_in_session must be true
|
||||
# At False, all attributes are forwarded as is
|
||||
# At true, look for the namespace of the source for input, If not found,
|
||||
# system namespace. Look for the options attribute_name_format and
|
||||
# output_namespace of the attribute policy for output.
|
||||
map_attributes_from_pull_sources = models.BooleanField(default=False)
|
||||
|
||||
# ATTRIBUTE_VALUE_FORMATS[0] =>
|
||||
# (lasso.SAML2_ATTRIBUTE_NAME_FORMAT_BASIC, 'SAMLv2 BASIC')
|
||||
output_name_format = models.CharField(max_length = 100,
|
||||
choices = ATTRIBUTE_VALUE_FORMATS,
|
||||
default = ATTRIBUTE_VALUE_FORMATS[0])
|
||||
|
||||
#ATTRIBUTES_NS[0] => ('Default', 'Default')
|
||||
output_namespace = models.CharField(max_length = 100,
|
||||
choices = ATTRIBUTES_NS, default = ATTRIBUTES_NS[0])
|
||||
|
||||
# Filter attributes pushed from source.
|
||||
source_filter_for_sso_from_push_sources = \
|
||||
models.ManyToManyField(AttributeSource,
|
||||
related_name = "attributes of pull sources",
|
||||
blank = True, null = True)
|
||||
|
||||
# List of attributes to filter from pull sources at SSO Login.
|
||||
attribute_filter_for_sso_from_push_sources = \
|
||||
models.ForeignKey(LibertyAttributeMap,
|
||||
related_name = "attributes of pull sources",
|
||||
blank = True, null = True)
|
||||
|
||||
# The sources of attributes of the previous list are considered.
|
||||
# May be used conjointly with 'source_filter_for_sso_from_push_sources'
|
||||
filter_source_of_filtered_attributes = models.BooleanField(default=False)
|
||||
|
||||
# To map the attributes of forwarded attributes with the defaut output
|
||||
# format and namespace, use 'map_attributes_from_pull_sources'
|
||||
# Use the following option to use the output format and namespace
|
||||
# indicated for each attribute.
|
||||
map_attributes_of_filtered_attributes = models.BooleanField(default=False)
|
||||
|
||||
|
||||
# Set to true to take in account missing required attributes
|
||||
send_error_and_no_attrs_if_missing_required_attrs = \
|
||||
models.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('attribute options policy')
|
||||
verbose_name_plural = _('attribute options policies')
|
||||
|
||||
|
||||
class AttributeList(models.Model):
|
||||
name = models.CharField(max_length = 40, unique = True)
|
||||
attributes = models.ManyToManyField(AttributeItem,
|
||||
related_name = "attributes of the list",
|
||||
blank = True, null = True)
|
||||
|
||||
|
||||
class AttributeItem(models.Model):
|
||||
attribute_name = models.CharField(max_length = 100, choices = ATTRIBUTES,
|
||||
default = ATTRIBUTES[0])
|
||||
# ATTRIBUTE_VALUE_FORMATS[0] =>
|
||||
# (lasso.SAML2_ATTRIBUTE_NAME_FORMAT_BASIC, 'SAMLv2 BASIC')
|
||||
output_attribute_name_format = models.CharField(max_length = 100,
|
||||
choices = ATTRIBUTE_VALUE_FORMATS,
|
||||
default = ATTRIBUTE_VALUE_FORMATS[0])
|
||||
#ATTRIBUTES_NS[0] => ('Default', 'Default')
|
||||
output_namespace = models.CharField(max_length = 100,
|
||||
choices = ATTRIBUTES_NS, default = ATTRIBUTES_NS[0])
|
||||
required = models.BooleanField(default=False)
|
||||
source = models.ForeignKey(AttributeSource, blank = True, null = True)
|
||||
|
||||
|
||||
A list of attributes can also be taken from the service provider metadata and
|
||||
added to 'attribute_list_for_sso_from_pull_sources'. The namespace may be
|
||||
extracted from the metadata. This namespace is then used to look for the
|
||||
corresponding definition and then to provide the attribute in the right
|
||||
namespace. Read attributes from metadata is not yet supported.
|
||||
|
||||
For the attributes of pull sources, once the list of attributes is defined,
|
||||
They are loaded in the user profile.
|
||||
|
||||
As explained before the attribute_aggregator loading function send signals to
|
||||
grab dictionnary of attributes. Up to know, only the ldap loading function are
|
||||
connected to these signals. The namespace of LDAP sources is assumed to be
|
||||
the same as the system namespace. There is here then no mapping needed. Other
|
||||
kind of sources than LDAP can be defined in attribute aggregator.
|
||||
|
||||
To grab attributes from a LDAP the user dn in the LDAP or at least a local
|
||||
identifier in the LDAP is required. For this purpose, each user has alias
|
||||
associated with LDAP source. These aliases must their DN in the LDAP. When
|
||||
the authentication LDAP backend will be taken in account, the dn will be taken
|
||||
direclty from the user Model instance.
|
||||
|
||||
Each LDAP sources are declared with the binding parameters. The LDAP namespace
|
||||
is always 'Default'.
|
||||
|
||||
If an attribute to load is not found and is required the answer should report
|
||||
an error (Not yet implemented).
|
||||
|
||||
Attributes in response can also be provided with other means than from an LDAP
|
||||
source. Attributes can be put in the user Django session and then loaded in
|
||||
the profile. An option of the service provier indicate if attributes in the
|
||||
session must be provided to the service provider.
|
||||
|
||||
To have the attribute loaded from the session, they must be provided in the
|
||||
session as follows:
|
||||
request.session['attributes'][source_name] = list()
|
||||
|
||||
The source_name must be the name of an existing instance of an
|
||||
'AttributeSource'. Such an instance contains a field namespace indicating the
|
||||
namespace of attributes.
|
||||
|
||||
This is currently implemented only for the SAML2 service provider module of
|
||||
authentic2. Authsaml2, the SP module, parse the assertion and put the
|
||||
attributes in the session.
|
||||
|
||||
::
|
||||
|
||||
if not 'multisource_attributes' in request.session:
|
||||
request.session['attributes'] = dict{}
|
||||
request.session['multisource_attributes'] \
|
||||
[login.assertion.issuer.content] = attributes
|
||||
|
||||
Then, Authentic2 can be used as a SAML2 proxy forwarding attributes in
|
||||
assertion, eventually doing a namespace mapping. For this, the option
|
||||
forward attributes in sesion must be set (by default False).
|
|
@ -0,0 +1,61 @@
|
|||
.. _auth_ldap:
|
||||
|
||||
==============================================
|
||||
Authentication with an existing LDAP directory
|
||||
==============================================
|
||||
|
||||
Authentic use the module django_auth_ldap to synchronize the Django user tables
|
||||
with an LDAP. For complex use case, we will refer you to the django_auth_ldap
|
||||
documentation, see http://packages.python.org/django-auth-ldap/.
|
||||
|
||||
How to authenticate users against an LDAP server with anonymous binding ?
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
1. Install the django_auth_ldap module for Django::
|
||||
|
||||
pip install django_auth_ldap
|
||||
|
||||
|
||||
2. Configure your local_settings.py file for authenticating against LDAP.
|
||||
|
||||
The next lines must be added::
|
||||
|
||||
AUTHENTICATION_BACKENDS += ( 'django_auth_ldap.backend.LDAPBackend', )
|
||||
|
||||
import ldap
|
||||
from django_auth_ldap.config import LDAPSearch
|
||||
|
||||
# Here put the LDAP URL of your server
|
||||
AUTH_LDAP_SERVER_URI = 'ldap://ldap.example.com'
|
||||
# Let the bind DN and bind password blank for anonymous binding
|
||||
AUTH_LDAP_BIND_DN = ""
|
||||
AUTH_LDAP_BIND_PASSWORD = ""
|
||||
# Lookup user under the branch o=base and by mathcing their uid against the
|
||||
# received login name
|
||||
AUTH_LDAP_USER_SEARCH = LDAPSearch("o=base",
|
||||
ldap.SCOPE_SUBTREE, "(uid=%(user)s)")
|
||||
|
||||
How to allow members of an LDAP group to manage Authentic ?
|
||||
-----------------------------------------------------------
|
||||
|
||||
1. First you must know the objectClass of groups in your LDAP schema, this FAQ
|
||||
will show you the configuration for two usual classes: groupOfNames and
|
||||
groupOfUniqueNames.
|
||||
|
||||
2. Find the relevant groupname. We will say it is: cn=admin,o=mycompany
|
||||
|
||||
3. Add the following lines::
|
||||
|
||||
from django_auth_ldap.config import GroupOfNamesType
|
||||
AUTH_LDAP_GROUP_TYPE = GroupOfNamesType()
|
||||
AUTH_LDAP_GROUP_SEARCH = LDAPSearch("o=mycompany",
|
||||
ldap.SCOPE_SUBTREE, "(objectClass=groupOfNames)")
|
||||
AUTH_LDAP_USER_FLAGS_BY_GROUP = {
|
||||
"is_staff": "cn=admin,o=mycompany"
|
||||
}
|
||||
|
||||
For an objectClass of groupOfUniqueNames you would change the string
|
||||
GroupOfNamesType to GroupOfUniqueNamesType and grouOfNames to
|
||||
groupOfUniqueNames. For more complex cases see the django_auth_ldap
|
||||
documentation.
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
.. _auth_pam:
|
||||
|
||||
=====================================
|
||||
Authentication on Authentic2 with PAM
|
||||
=====================================
|
||||
|
||||
This module is copied from https://bitbucket.org/wnielson/django-pam/ by Weston
|
||||
Nielson and the pam ctype module by Chris Atlee http://atlee.ca/software/pam/.
|
||||
|
||||
Add 'authentic2.vendor.dpam.backends.PAMBackend' to your
|
||||
``settings.py``::
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
...
|
||||
'authentic2.vendor.dpam.backends.PAMBackend',
|
||||
...
|
||||
)
|
||||
|
||||
Now you can login via the system-login credentials. If the user is
|
||||
successfully authenticated but has never logged-in before, a new ``User``
|
||||
object is created. By default this new ``User`` has both ``is_staff`` and
|
||||
``is_superuser`` set to ``False``. You can change this behavior by adding
|
||||
``PAM_IS_STAFF=True`` and ``PAM_IS_SUPERUSER`` in your ``settings.py`` file.
|
||||
|
||||
The default PAM service used is ``login`` but you can change it by setting the
|
||||
``PAM_SERVICE`` variable in your ``settings.py`` file.
|
|
@ -0,0 +1,5 @@
|
|||
.. _config_cas_idp:
|
||||
|
||||
====================================
|
||||
Configure Authentic2 as a CAS client
|
||||
====================================
|
|
@ -0,0 +1,28 @@
|
|||
.. _config_cas_sp:
|
||||
|
||||
====================================
|
||||
Configure Authentic2 as a CAS server
|
||||
====================================
|
||||
|
||||
How to use Authentic2 as a CAS 1.0 or CAS 2.0 identity provider ?
|
||||
-----------------------------------------------------------------
|
||||
|
||||
1. Activate CAS IdP support in settings.py::
|
||||
|
||||
IDP_CAS = True
|
||||
|
||||
2. Then create the database table to hold CAS service tickets::
|
||||
|
||||
python authentic2/manage.py syncdb --migrate
|
||||
|
||||
3. Also configure authentic2 to authenticate against your LDAP directory (see
|
||||
above) if your want your user attributes to be accessible from your service,
|
||||
if it is not necessary you can use the normal relational database storage
|
||||
for you users.
|
||||
|
||||
4. Finally configure your service to point to the CAS endpoint at:
|
||||
|
||||
http[s]://your.domain.com/idp/cas/
|
||||
|
||||
5. If needed configure your service to resolve authenticated user with your
|
||||
LDAP directory (if user attributes are needed for your service)
|
|
@ -0,0 +1,12 @@
|
|||
.. _config_saml2_idp:
|
||||
|
||||
=================================================================
|
||||
Configure Authentic2 as a SAML2 service provider or a SAML2 proxy
|
||||
=================================================================
|
||||
|
||||
1. Create instance of SP settings
|
||||
|
||||
2. Declare IdP
|
||||
|
||||
3. Get authsaml2 md
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _config_saml2_sp:
|
||||
|
||||
====================================
|
||||
Configure SAML 2.0 service providers
|
||||
====================================
|
||||
|
||||
How to I authenticate against Authentic2 with a SAMLv2 service provider ?
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
1. Grab the SAML2 IdP metadata:
|
||||
|
||||
http[s]://your.domain.com/idp/saml2/metadata
|
||||
|
||||
2. And configure your service provider with it.
|
||||
|
||||
3. Go to the providers admin panel on:
|
||||
|
||||
http[s]://your.domain.com/admin/saml/libertyprovider/add/
|
||||
|
||||
There create a new provider using the service provider metadata and enable it
|
||||
as a service provider, you can customize some behaviours like the preferred
|
||||
assertion consumer or encryption for the NameID or the Assertion element.
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
.. _download:
|
||||
|
||||
========
|
||||
Download
|
||||
========
|
||||
|
||||
1. Pypi: http://pypi.python.org/pypi/authentic2/1.9.0
|
||||
|
||||
2. Git repository: http://repos.entrouvert.org/authentic.git
|
||||
|
||||
3. `Browse source <http://dev.entrouvert.org/projects/authentic/repository>`_
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _features:
|
||||
|
||||
========
|
||||
Features
|
||||
========
|
||||
|
||||
Authentic can authenticate users against:
|
||||
|
||||
- an LDAP directory,
|
||||
- a SAML 2.0 identity provider,
|
||||
- an OpenID identity provider,
|
||||
- with an X509 certificate.
|
||||
|
||||
Authentic can provide authentication to web applications using the following
|
||||
protocols:
|
||||
|
||||
- OpenID,
|
||||
- SAML 2.0,
|
||||
- CAS 1.0 & CAS 2.0.
|
||||
|
||||
Authentic can proxy authentication between any two different protocols it
|
||||
support.
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
.. Authentic2 documentation master file, created by
|
||||
sphinx-quickstart on Thu Oct 13 09:53:03 2011.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
==========================
|
||||
Authentic2's documentation
|
||||
==========================
|
||||
|
||||
Authentic2 is a versatile identity provider addressing a broad
|
||||
range of needs, from simple to advanced setups, around web authentication,
|
||||
attribute sharing, namespace mapping and authorization management.
|
||||
|
||||
Authentic2 supports many protocols and standards, including SAML2, CAS, OpenID,
|
||||
LDAP, X509, OATH, and can bridge between them.
|
||||
|
||||
Authentic2 is under the GNU AGPL version 3 licence.
|
||||
|
||||
It has support for SAMLv2 thanks to `Lasso <http://lasso.entrouvert.org>`_,
|
||||
a free (GNU GPL) implementation of the Liberty Alliance and OASIS
|
||||
specifications of SAML2, ID-FF1.2 and ID-WSF2.
|
||||
|
||||
- `Authentic2 project site <http://dev.entrouvert.org/projects/authentic>`_
|
||||
- `Authentic2 roadmap <http://dev.entrouvert.org/projects/authentic/roadmap>`_
|
||||
|
||||
Documentation content
|
||||
=====================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
features
|
||||
|
||||
download
|
||||
|
||||
installation
|
||||
|
||||
auth_ldap
|
||||
|
||||
auth_pam
|
||||
|
||||
config_saml2_sp
|
||||
|
||||
config_saml2_idp
|
||||
|
||||
config_cas_sp
|
||||
|
||||
config_cas_idp
|
||||
|
||||
attribute_management
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
Authentic is copyrighted by Entr'ouvert and is licensed through the GNU General
|
||||
Public Licence, version 2 or later. A copy of the whole license text is
|
||||
available in the COPYING file.
|
||||
|
||||
The OpenID IdP originates in the project django_openid_provider by Roman
|
||||
Barczy¿ski, which is under the Apache 2.0 licence. This imply that you must
|
||||
distribute authentic2 under the AGPL3 licence when distributing this part of the
|
||||
project which is the only AGPL licence version compatible with the Apache 2.0
|
||||
licence.
|
||||
|
||||
.. Indices and tables
|
||||
.. ==================
|
||||
|
||||
.. * :ref:`genindex`
|
||||
.. * :ref:`modindex`
|
||||
.. * :ref:`search`
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
.. _installation:
|
||||
|
||||
============
|
||||
Installation
|
||||
============
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
You must install the following packages to use Authentic
|
||||
|
||||
- Python Lasso binding 2.3.5:
|
||||
|
||||
From sources: http://lasso.entrouvert.org/download
|
||||
Debian based distribution: apt-get install python-lasso
|
||||
|
||||
- Django 1.3:
|
||||
|
||||
From sources: http://www.djangoproject.com/download/1.3/tarball/
|
||||
|
||||
- Django-registration 0.8-alpha-1:
|
||||
|
||||
From sources: http://bitbucket.org/ubernostrum/django-registration/downloads
|
||||
Debian based distribution: apt-get install python-django-registration
|
||||
|
||||
- Django-authopenid 0.9.6:
|
||||
|
||||
From sources: http://bitbucket.org/benoitc/django-authopenid/downloads
|
||||
|
||||
- Django-south 0.7.3:
|
||||
|
||||
From sources:: http://south.aeracode.org/docs/installation.html
|
||||
|
||||
- Django-profiles 0.2:
|
||||
|
||||
From sources:: http://pypi.python.org/pypi/django-profiles
|
||||
|
||||
You install all the django libraries quickly using pip::
|
||||
|
||||
pip install django django-profiles django-registration \
|
||||
django-debug-toolbar django-authopenid south
|
||||
|
||||
or easy_install::
|
||||
|
||||
easy_install django django-profiles django-registration \
|
||||
django-debug-toolbar django-authopenid south
|
||||
|
||||
Quick Start
|
||||
-----------
|
||||
|
||||
Then launch the following commands::
|
||||
|
||||
python manage.py syncdb --migrate
|
||||
python manage.py runserver
|
||||
|
||||
You should see the following output::
|
||||
|
||||
Validating models...
|
||||
0 errors found
|
||||
|
||||
Django version 1.2, using settings 'authentic.settings'
|
||||
Development server is running at http://127.0.0.1:8000/
|
||||
Quit the server with CONTROL-C.
|
||||
|
||||
You can access the running application on http://127.0.0.1:8000/
|
||||
|
||||
|
||||
Specifying a different database
|
||||
-------------------------------
|
||||
|
||||
This is done by modifying the DATABASES dictionary in your local_settings.py file
|
||||
(create it in Authentic project directory); for example::
|
||||
|
||||
DATABASES['default'] = {
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
'NAME': 'authentic',
|
||||
'USER': 'admindb',
|
||||
'PASSWORD': 'foobar',
|
||||
'HOST': 'db.example.com',
|
||||
'PORT': '', # empty string means default value
|
||||
}
|
||||
|
||||
You should refer to the Django documentation on databases settings at
|
||||
http://docs.djangoproject.com/en/dev/ref/settings/#databases for all
|
||||
the details.
|
||||
|
||||
How to upgrade to a new version of authentic ?
|
||||
----------------------------------------------
|
||||
|
||||
Authentic store all its data in a relational database as specified in its
|
||||
settings.py or local_settings.py file. So in order to upgrade to a new version
|
||||
of authentic you have to update your database schema using the
|
||||
migration command — you will need to have installed the dependency django-south,
|
||||
see the beginning of this README file.::
|
||||
|
||||
python ./manage.py migrate
|
||||
|
||||
Then you will need to create new tables if there are.::
|
||||
|
||||
python ./manage.py syncdb
|
After Width: | Height: | Size: 673 B |
|
@ -0,0 +1,540 @@
|
|||
/*
|
||||
* basic.css
|
||||
* ~~~~~~~~~
|
||||
*
|
||||
* Sphinx stylesheet -- basic theme.
|
||||
*
|
||||
* :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
/* -- main layout ----------------------------------------------------------- */
|
||||
|
||||
div.clearer {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* -- relbar ---------------------------------------------------------------- */
|
||||
|
||||
div.related {
|
||||
width: 100%;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
div.related h3 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.related ul {
|
||||
margin: 0;
|
||||
padding: 0 0 0 10px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
div.related li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
div.related li.right {
|
||||
float: right;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
/* -- sidebar --------------------------------------------------------------- */
|
||||
|
||||
div.sphinxsidebarwrapper {
|
||||
padding: 10px 5px 0 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
float: left;
|
||||
width: 230px;
|
||||
margin-left: -100%;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul ul,
|
||||
div.sphinxsidebar ul.want-points {
|
||||
margin-left: 20px;
|
||||
list-style: square;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar form {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input {
|
||||
border: 1px solid #98dbcc;
|
||||
font-family: sans-serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input[type="text"] {
|
||||
width: 170px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input[type="submit"] {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* -- search page ----------------------------------------------------------- */
|
||||
|
||||
ul.search {
|
||||
margin: 10px 0 0 20px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul.search li {
|
||||
padding: 5px 0 5px 20px;
|
||||
background-image: url(file.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 7px;
|
||||
}
|
||||
|
||||
ul.search li a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ul.search li div.context {
|
||||
color: #888;
|
||||
margin: 2px 0 0 30px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
ul.keywordmatches li.goodmatch a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* -- index page ------------------------------------------------------------ */
|
||||
|
||||
table.contentstable {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
table.contentstable p.biglink {
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
a.biglink {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
span.linkdescr {
|
||||
font-style: italic;
|
||||
padding-top: 5px;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
/* -- general index --------------------------------------------------------- */
|
||||
|
||||
table.indextable {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table.indextable td {
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table.indextable dl, table.indextable dd {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
table.indextable tr.pcap {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
table.indextable tr.cap {
|
||||
margin-top: 10px;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
img.toggler {
|
||||
margin-right: 3px;
|
||||
margin-top: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.modindex-jumpbox {
|
||||
border-top: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
margin: 1em 0 1em 0;
|
||||
padding: 0.4em;
|
||||
}
|
||||
|
||||
div.genindex-jumpbox {
|
||||
border-top: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
margin: 1em 0 1em 0;
|
||||
padding: 0.4em;
|
||||
}
|
||||
|
||||
/* -- general body styles --------------------------------------------------- */
|
||||
|
||||
a.headerlink {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
h1:hover > a.headerlink,
|
||||
h2:hover > a.headerlink,
|
||||
h3:hover > a.headerlink,
|
||||
h4:hover > a.headerlink,
|
||||
h5:hover > a.headerlink,
|
||||
h6:hover > a.headerlink,
|
||||
dt:hover > a.headerlink {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
div.body p.caption {
|
||||
text-align: inherit;
|
||||
}
|
||||
|
||||
div.body td {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.field-list ul {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.first {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
p.rubric {
|
||||
margin-top: 30px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
img.align-left, .figure.align-left, object.align-left {
|
||||
clear: left;
|
||||
float: left;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
img.align-right, .figure.align-right, object.align-right {
|
||||
clear: right;
|
||||
float: right;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
img.align-center, .figure.align-center, object.align-center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* -- sidebars -------------------------------------------------------------- */
|
||||
|
||||
div.sidebar {
|
||||
margin: 0 0 0.5em 1em;
|
||||
border: 1px solid #ddb;
|
||||
padding: 7px 7px 0 7px;
|
||||
background-color: #ffe;
|
||||
width: 40%;
|
||||
float: right;
|
||||
}
|
||||
|
||||
p.sidebar-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* -- topics ---------------------------------------------------------------- */
|
||||
|
||||
div.topic {
|
||||
border: 1px solid #ccc;
|
||||
padding: 7px 7px 0 7px;
|
||||
margin: 10px 0 10px 0;
|
||||
}
|
||||
|
||||
p.topic-title {
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* -- admonitions ----------------------------------------------------------- */
|
||||
|
||||
div.admonition {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
div.admonition dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.admonition dl {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
margin: 0px 10px 5px 0px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.body p.centered {
|
||||
text-align: center;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
/* -- tables ---------------------------------------------------------------- */
|
||||
|
||||
table.docutils {
|
||||
border: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table.docutils td, table.docutils th {
|
||||
padding: 1px 8px 1px 5px;
|
||||
border-top: 0;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
border-bottom: 1px solid #aaa;
|
||||
}
|
||||
|
||||
table.field-list td, table.field-list th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
table.footnote td, table.footnote th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
table.citation {
|
||||
border-left: solid 1px gray;
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
table.citation td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* -- other body styles ----------------------------------------------------- */
|
||||
|
||||
ol.arabic {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
ol.loweralpha {
|
||||
list-style: lower-alpha;
|
||||
}
|
||||
|
||||
ol.upperalpha {
|
||||
list-style: upper-alpha;
|
||||
}
|
||||
|
||||
ol.lowerroman {
|
||||
list-style: lower-roman;
|
||||
}
|
||||
|
||||
ol.upperroman {
|
||||
list-style: upper-roman;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
dd p {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
dd ul, dd table {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-top: 3px;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
dt:target, .highlighted {
|
||||
background-color: #fbe54e;
|
||||
}
|
||||
|
||||
dl.glossary dt {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.field-list ul {
|
||||
margin: 0;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.field-list p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.refcount {
|
||||
color: #060;
|
||||
}
|
||||
|
||||
.optional {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.versionmodified {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.system-message {
|
||||
background-color: #fda;
|
||||
padding: 5px;
|
||||
border: 3px solid red;
|
||||
}
|
||||
|
||||
.footnote:target {
|
||||
background-color: #ffa;
|
||||
}
|
||||
|
||||
.line-block {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.line-block .line-block {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
margin-left: 1.5em;
|
||||
}
|
||||
|
||||
.guilabel, .menuselection {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.accelerator {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.classifier {
|
||||
font-style: oblique;
|
||||
}
|
||||
|
||||
abbr, acronym {
|
||||
border-bottom: dotted 1px;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
/* -- code displays --------------------------------------------------------- */
|
||||
|
||||
pre {
|
||||
overflow: auto;
|
||||
overflow-y: hidden; /* fixes display issues on Chrome browsers */
|
||||
}
|
||||
|
||||
td.linenos pre {
|
||||
padding: 5px 0px;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
table.highlighttable {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
table.highlighttable td {
|
||||
padding: 0 0.5em 0 0.5em;
|
||||
}
|
||||
|
||||
tt.descname {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
tt.descclassname {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
tt.xref, a tt {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.viewcode-link {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.viewcode-back {
|
||||
float: right;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
div.viewcode-block:target {
|
||||
margin: -1px -10px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
/* -- math display ---------------------------------------------------------- */
|
||||
|
||||
img.math {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
div.body div.math p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
span.eqno {
|
||||
float: right;
|
||||
}
|
||||
|
||||
/* -- printout stylesheet --------------------------------------------------- */
|
||||
|
||||
@media print {
|
||||
div.document,
|
||||
div.documentwrapper,
|
||||
div.bodywrapper {
|
||||
margin: 0 !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.sphinxsidebar,
|
||||
div.related,
|
||||
div.footer,
|
||||
#top-link {
|
||||
display: none;
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.4 KiB |
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
* default.css_t
|
||||
* ~~~~~~~~~~~~~
|
||||
*
|
||||
* Sphinx stylesheet -- default theme.
|
||||
*
|
||||
* :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
@import url("basic.css");
|
||||
|
||||
/* -- page layout ----------------------------------------------------------- */
|
||||
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
font-size: 100%;
|
||||
background-color: #11303d;
|
||||
color: #000;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.document {
|
||||
background-color: #1c4e63;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0 0 0 230px;
|
||||
}
|
||||
|
||||
div.body {
|
||||
background-color: #ffffff;
|
||||
color: #000000;
|
||||
padding: 0 20px 30px 20px;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
color: #ffffff;
|
||||
width: 100%;
|
||||
padding: 9px 0 9px 0;
|
||||
text-align: center;
|
||||
font-size: 75%;
|
||||
}
|
||||
|
||||
div.footer a {
|
||||
color: #ffffff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.related {
|
||||
background-color: #133f52;
|
||||
line-height: 30px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
div.related a {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3 {
|
||||
font-family: 'Trebuchet MS', sans-serif;
|
||||
color: #ffffff;
|
||||
font-size: 1.4em;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3 a {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h4 {
|
||||
font-family: 'Trebuchet MS', sans-serif;
|
||||
color: #ffffff;
|
||||
font-size: 1.3em;
|
||||
font-weight: normal;
|
||||
margin: 5px 0 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p.topless {
|
||||
margin: 5px 10px 10px 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
margin: 10px;
|
||||
padding: 0;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a {
|
||||
color: #98dbcc;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input {
|
||||
border: 1px solid #98dbcc;
|
||||
font-family: sans-serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* -- hyperlink styles ------------------------------------------------------ */
|
||||
|
||||
a {
|
||||
color: #355f7c;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #355f7c;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* -- body styles ----------------------------------------------------------- */
|
||||
|
||||
div.body h1,
|
||||
div.body h2,
|
||||
div.body h3,
|
||||
div.body h4,
|
||||
div.body h5,
|
||||
div.body h6 {
|
||||
font-family: 'Trebuchet MS', sans-serif;
|
||||
background-color: #f2f2f2;
|
||||
font-weight: normal;
|
||||
color: #20435c;
|
||||
border-bottom: 1px solid #ccc;
|
||||
margin: 20px -20px 10px -20px;
|
||||
padding: 3px 0 3px 10px;
|
||||
}
|
||||
|
||||
div.body h1 { margin-top: 0; font-size: 200%; }
|
||||
div.body h2 { font-size: 160%; }
|
||||
div.body h3 { font-size: 140%; }
|
||||
div.body h4 { font-size: 120%; }
|
||||
div.body h5 { font-size: 110%; }
|
||||
div.body h6 { font-size: 100%; }
|
||||
|
||||
a.headerlink {
|
||||
color: #c60f0f;
|
||||
font-size: 0.8em;
|
||||
padding: 0 4px 0 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.headerlink:hover {
|
||||
background-color: #c60f0f;
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.body p, div.body dd, div.body li {
|
||||
text-align: justify;
|
||||
line-height: 130%;
|
||||
}
|
||||
|
||||
div.admonition p.admonition-title + p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
div.admonition p {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
div.admonition pre {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
div.admonition ul, div.admonition ol {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
div.note {
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.seealso {
|
||||
background-color: #ffc;
|
||||
border: 1px solid #ff6;
|
||||
}
|
||||
|
||||
div.topic {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
div.warning {
|
||||
background-color: #ffe4e4;
|
||||
border: 1px solid #f66;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p.admonition-title:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 5px;
|
||||
background-color: #eeffcc;
|
||||
color: #333333;
|
||||
line-height: 120%;
|
||||
border: 1px solid #ac9;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
tt {
|
||||
background-color: #ecf0f3;
|
||||
padding: 0 1px 0 1px;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #ede;
|
||||
}
|
||||
|
||||
.warning tt {
|
||||
background: #efc2c2;
|
||||
}
|
||||
|
||||
.note tt {
|
||||
background: #d6d6d6;
|
||||
}
|
||||
|
||||
.viewcode-back {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
div.viewcode-block:target {
|
||||
background-color: #f4debf;
|
||||
border-top: 1px solid #ac9;
|
||||
border-bottom: 1px solid #ac9;
|
||||
}
|
|
@ -0,0 +1,247 @@
|
|||
/*
|
||||
* doctools.js
|
||||
* ~~~~~~~~~~~
|
||||
*
|
||||
* Sphinx JavaScript utilties for all documentation.
|
||||
*
|
||||
* :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* select a different prefix for underscore
|
||||
*/
|
||||
$u = _.noConflict();
|
||||
|
||||
/**
|
||||
* make the code below compatible with browsers without
|
||||
* an installed firebug like debugger
|
||||
if (!window.console || !console.firebug) {
|
||||
var names = ["log", "debug", "info", "warn", "error", "assert", "dir",
|
||||
"dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace",
|
||||
"profile", "profileEnd"];
|
||||
window.console = {};
|
||||
for (var i = 0; i < names.length; ++i)
|
||||
window.console[names[i]] = function() {};
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* small helper function to urldecode strings
|
||||
*/
|
||||
jQuery.urldecode = function(x) {
|
||||
return decodeURIComponent(x).replace(/\+/g, ' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* small helper function to urlencode strings
|
||||
*/
|
||||
jQuery.urlencode = encodeURIComponent;
|
||||
|
||||
/**
|
||||
* This function returns the parsed url parameters of the
|
||||
* current request. Multiple values per key are supported,
|
||||
* it will always return arrays of strings for the value parts.
|
||||
*/
|
||||
jQuery.getQueryParameters = function(s) {
|
||||
if (typeof s == 'undefined')
|
||||
s = document.location.search;
|
||||
var parts = s.substr(s.indexOf('?') + 1).split('&');
|
||||
var result = {};
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
var tmp = parts[i].split('=', 2);
|
||||
var key = jQuery.urldecode(tmp[0]);
|
||||
var value = jQuery.urldecode(tmp[1]);
|
||||
if (key in result)
|
||||
result[key].push(value);
|
||||
else
|
||||
result[key] = [value];
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* small function to check if an array contains
|
||||
* a given item.
|
||||
*/
|
||||
jQuery.contains = function(arr, item) {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (arr[i] == item)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* highlight a given string on a jquery object by wrapping it in
|
||||
* span elements with the given class name.
|
||||
*/
|
||||
jQuery.fn.highlightText = function(text, className) {
|
||||
function highlight(node) {
|
||||
if (node.nodeType == 3) {
|
||||
var val = node.nodeValue;
|
||||
var pos = val.toLowerCase().indexOf(text);
|
||||
if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) {
|
||||
var span = document.createElement("span");
|
||||
span.className = className;
|
||||
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
|
||||
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
|
||||
document.createTextNode(val.substr(pos + text.length)),
|
||||
node.nextSibling));
|
||||
node.nodeValue = val.substr(0, pos);
|
||||
}
|
||||
}
|
||||
else if (!jQuery(node).is("button, select, textarea")) {
|
||||
jQuery.each(node.childNodes, function() {
|
||||
highlight(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
return this.each(function() {
|
||||
highlight(this);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Small JavaScript module for the documentation.
|
||||
*/
|
||||
var Documentation = {
|
||||
|
||||
init : function() {
|
||||
this.fixFirefoxAnchorBug();
|
||||
this.highlightSearchWords();
|
||||
this.initIndexTable();
|
||||
},
|
||||
|
||||
/**
|
||||
* i18n support
|
||||
*/
|
||||
TRANSLATIONS : {},
|
||||
PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; },
|
||||
LOCALE : 'unknown',
|
||||
|
||||
// gettext and ngettext don't access this so that the functions
|
||||
// can safely bound to a different name (_ = Documentation.gettext)
|
||||
gettext : function(string) {
|
||||
var translated = Documentation.TRANSLATIONS[string];
|
||||
if (typeof translated == 'undefined')
|
||||
return string;
|
||||
return (typeof translated == 'string') ? translated : translated[0];
|
||||
},
|
||||
|
||||
ngettext : function(singular, plural, n) {
|
||||
var translated = Documentation.TRANSLATIONS[singular];
|
||||
if (typeof translated == 'undefined')
|
||||
return (n == 1) ? singular : plural;
|
||||
return translated[Documentation.PLURALEXPR(n)];
|
||||
},
|
||||
|
||||
addTranslations : function(catalog) {
|
||||
for (var key in catalog.messages)
|
||||
this.TRANSLATIONS[key] = catalog.messages[key];
|
||||
this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')');
|
||||
this.LOCALE = catalog.locale;
|
||||
},
|
||||
|
||||
/**
|
||||
* add context elements like header anchor links
|
||||
*/
|
||||
addContextElements : function() {
|
||||
$('div[id] > :header:first').each(function() {
|
||||
$('<a class="headerlink">\u00B6</a>').
|
||||
attr('href', '#' + this.id).
|
||||
attr('title', _('Permalink to this headline')).
|
||||
appendTo(this);
|
||||
});
|
||||
$('dt[id]').each(function() {
|
||||
$('<a class="headerlink">\u00B6</a>').
|
||||
attr('href', '#' + this.id).
|
||||
attr('title', _('Permalink to this definition')).
|
||||
appendTo(this);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* workaround a firefox stupidity
|
||||
*/
|
||||
fixFirefoxAnchorBug : function() {
|
||||
if (document.location.hash && $.browser.mozilla)
|
||||
window.setTimeout(function() {
|
||||
document.location.href += '';
|
||||
}, 10);
|
||||
},
|
||||
|
||||
/**
|
||||
* highlight the search words provided in the url in the text
|
||||
*/
|
||||
highlightSearchWords : function() {
|
||||
var params = $.getQueryParameters();
|
||||
var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : [];
|
||||
if (terms.length) {
|
||||
var body = $('div.body');
|
||||
window.setTimeout(function() {
|
||||
$.each(terms, function() {
|
||||
body.highlightText(this.toLowerCase(), 'highlighted');
|
||||
});
|
||||
}, 10);
|
||||
$('<p class="highlight-link"><a href="javascript:Documentation.' +
|
||||
'hideSearchWords()">' + _('Hide Search Matches') + '</a></p>')
|
||||
.appendTo($('#searchbox'));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* init the domain index toggle buttons
|
||||
*/
|
||||
initIndexTable : function() {
|
||||
var togglers = $('img.toggler').click(function() {
|
||||
var src = $(this).attr('src');
|
||||
var idnum = $(this).attr('id').substr(7);
|
||||
$('tr.cg-' + idnum).toggle();
|
||||
if (src.substr(-9) == 'minus.png')
|
||||
$(this).attr('src', src.substr(0, src.length-9) + 'plus.png');
|
||||
else
|
||||
$(this).attr('src', src.substr(0, src.length-8) + 'minus.png');
|
||||
}).css('display', '');
|
||||
if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) {
|
||||
togglers.click();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* helper function to hide the search marks again
|
||||
*/
|
||||
hideSearchWords : function() {
|
||||
$('#searchbox .highlight-link').fadeOut(300);
|
||||
$('span.highlighted').removeClass('highlighted');
|
||||
},
|
||||
|
||||
/**
|
||||
* make the url absolute
|
||||
*/
|
||||
makeURL : function(relativeURL) {
|
||||
return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL;
|
||||
},
|
||||
|
||||
/**
|
||||
* get the current relative url
|
||||
*/
|
||||
getCurrentURL : function() {
|
||||
var path = document.location.pathname;
|
||||
var parts = path.split(/\//);
|
||||
$.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() {
|
||||
if (this == '..')
|
||||
parts.pop();
|
||||
});
|
||||
var url = parts.join('/');
|
||||
return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
|
||||
}
|
||||
};
|
||||
|
||||
// quick alias for translations
|
||||
_ = Documentation.gettext;
|
||||
|
||||
$(document).ready(function() {
|
||||
Documentation.init();
|
||||
});
|
After Width: | Height: | Size: 368 B |
After Width: | Height: | Size: 363 B |
After Width: | Height: | Size: 392 B |
|
@ -0,0 +1,154 @@
|
|||
/*!
|
||||
* jQuery JavaScript Library v1.4.2
|
||||
* http://jquery.com/
|
||||
*
|
||||
* Copyright 2010, John Resig
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* Includes Sizzle.js
|
||||
* http://sizzlejs.com/
|
||||
* Copyright 2010, The Dojo Foundation
|
||||
* Released under the MIT, BSD, and GPL Licenses.
|
||||
*
|
||||
* Date: Sat Feb 13 22:33:48 2010 -0500
|
||||
*/
|
||||
(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
|
||||
e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
|
||||
j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
|
||||
"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
|
||||
true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
|
||||
Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
|
||||
(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
|
||||
a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
|
||||
"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
|
||||
function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
|
||||
c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
|
||||
L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
|
||||
"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
|
||||
a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
|
||||
d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
|
||||
a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
|
||||
!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
|
||||
true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
|
||||
var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
|
||||
parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
|
||||
false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
|
||||
s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
|
||||
applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
|
||||
else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
|
||||
a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
|
||||
w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
|
||||
cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
|
||||
i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
|
||||
" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
|
||||
this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
|
||||
e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
|
||||
c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
|
||||
a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
|
||||
function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
|
||||
k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
|
||||
C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
|
||||
null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
|
||||
e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
|
||||
f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
|
||||
if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
|
||||
fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
|
||||
d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
|
||||
"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
|
||||
a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
|
||||
isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
|
||||
{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
|
||||
if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
|
||||
e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
|
||||
"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
|
||||
d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
|
||||
!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
|
||||
toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
|
||||
u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
|
||||
function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
|
||||
if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
|
||||
e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
|
||||
t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
|
||||
g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
|
||||
for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
|
||||
1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
|
||||
CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
|
||||
relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
|
||||
l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
|
||||
h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
|
||||
CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
|
||||
g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
|
||||
text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
|
||||
setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
|
||||
h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
|
||||
m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
|
||||
"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
|
||||
h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
|
||||
!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
|
||||
h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
|
||||
q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
|
||||
if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
|
||||
(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
|
||||
function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
|
||||
gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
|
||||
c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
|
||||
{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
|
||||
"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
|
||||
d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
|
||||
a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
|
||||
1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
|
||||
a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
|
||||
c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
|
||||
wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
|
||||
prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
|
||||
this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
|
||||
return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
|
||||
""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
|
||||
this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
|
||||
u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
|
||||
1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
|
||||
return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
|
||||
""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
|
||||
c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
|
||||
c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
|
||||
function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
|
||||
Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
|
||||
"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
|
||||
a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
|
||||
a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
|
||||
"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
|
||||
serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
|
||||
function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
|
||||
global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
|
||||
e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
|
||||
"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
|
||||
false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
|
||||
false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
|
||||
c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
|
||||
d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
|
||||
g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
|
||||
1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
|
||||
"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
|
||||
if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
|
||||
this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
|
||||
"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
|
||||
animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
|
||||
j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
|
||||
this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
|
||||
"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
|
||||
c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
|
||||
this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
|
||||
this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
|
||||
e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
|
||||
c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
|
||||
function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
|
||||
this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
|
||||
k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
|
||||
f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
|
||||
a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
|
||||
c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
|
||||
d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
|
||||
f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
|
||||
"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
|
||||
e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
|
After Width: | Height: | Size: 199 B |
After Width: | Height: | Size: 199 B |
|
@ -0,0 +1,62 @@
|
|||
.highlight .hll { background-color: #ffffcc }
|
||||
.highlight { background: #eeffcc; }
|
||||
.highlight .c { color: #408090; font-style: italic } /* Comment */
|
||||
.highlight .err { border: 1px solid #FF0000 } /* Error */
|
||||
.highlight .k { color: #007020; font-weight: bold } /* Keyword */
|
||||
.highlight .o { color: #666666 } /* Operator */
|
||||
.highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */
|
||||
.highlight .cp { color: #007020 } /* Comment.Preproc */
|
||||
.highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */
|
||||
.highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */
|
||||
.highlight .gd { color: #A00000 } /* Generic.Deleted */
|
||||
.highlight .ge { font-style: italic } /* Generic.Emph */
|
||||
.highlight .gr { color: #FF0000 } /* Generic.Error */
|
||||
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
||||
.highlight .gi { color: #00A000 } /* Generic.Inserted */
|
||||
.highlight .go { color: #303030 } /* Generic.Output */
|
||||
.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
|
||||
.highlight .gs { font-weight: bold } /* Generic.Strong */
|
||||
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
|
||||
.highlight .gt { color: #0040D0 } /* Generic.Traceback */
|
||||
.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */
|
||||
.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
|
||||
.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */
|
||||
.highlight .kp { color: #007020 } /* Keyword.Pseudo */
|
||||
.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
|
||||
.highlight .kt { color: #902000 } /* Keyword.Type */
|
||||
.highlight .m { color: #208050 } /* Literal.Number */
|
||||
.highlight .s { color: #4070a0 } /* Literal.String */
|
||||
.highlight .na { color: #4070a0 } /* Name.Attribute */
|
||||
.highlight .nb { color: #007020 } /* Name.Builtin */
|
||||
.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */
|
||||
.highlight .no { color: #60add5 } /* Name.Constant */
|
||||
.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */
|
||||
.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */
|
||||
.highlight .ne { color: #007020 } /* Name.Exception */
|
||||
.highlight .nf { color: #06287e } /* Name.Function */
|
||||
.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */
|
||||
.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
|
||||
.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */
|
||||
.highlight .nv { color: #bb60d5 } /* Name.Variable */
|
||||
.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */
|
||||
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
|
||||
.highlight .mf { color: #208050 } /* Literal.Number.Float */
|
||||
.highlight .mh { color: #208050 } /* Literal.Number.Hex */
|
||||
.highlight .mi { color: #208050 } /* Literal.Number.Integer */
|
||||
.highlight .mo { color: #208050 } /* Literal.Number.Oct */
|
||||
.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */
|
||||
.highlight .sc { color: #4070a0 } /* Literal.String.Char */
|
||||
.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
|
||||
.highlight .s2 { color: #4070a0 } /* Literal.String.Double */
|
||||
.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
|
||||
.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */
|
||||
.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
|
||||
.highlight .sx { color: #c65d09 } /* Literal.String.Other */
|
||||
.highlight .sr { color: #235388 } /* Literal.String.Regex */
|
||||
.highlight .s1 { color: #4070a0 } /* Literal.String.Single */
|
||||
.highlight .ss { color: #517918 } /* Literal.String.Symbol */
|
||||
.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */
|
||||
.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */
|
||||
.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */
|
||||
.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */
|
||||
.highlight .il { color: #208050 } /* Literal.Number.Integer.Long */
|
|
@ -0,0 +1,560 @@
|
|||
/*
|
||||
* searchtools.js_t
|
||||
* ~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* Sphinx JavaScript utilties for the full-text search.
|
||||
*
|
||||
* :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* helper function to return a node containing the
|
||||
* search summary for a given text. keywords is a list
|
||||
* of stemmed words, hlwords is the list of normal, unstemmed
|
||||
* words. the first one is used to find the occurance, the
|
||||
* latter for highlighting it.
|
||||
*/
|
||||
|
||||
jQuery.makeSearchSummary = function(text, keywords, hlwords) {
|
||||
var textLower = text.toLowerCase();
|
||||
var start = 0;
|
||||
$.each(keywords, function() {
|
||||
var i = textLower.indexOf(this.toLowerCase());
|
||||
if (i > -1)
|
||||
start = i;
|
||||
});
|
||||
start = Math.max(start - 120, 0);
|
||||
var excerpt = ((start > 0) ? '...' : '') +
|
||||
$.trim(text.substr(start, 240)) +
|
||||
((start + 240 - text.length) ? '...' : '');
|
||||
var rv = $('<div class="context"></div>').text(excerpt);
|
||||
$.each(hlwords, function() {
|
||||
rv = rv.highlightText(this, 'highlighted');
|
||||
});
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Porter Stemmer
|
||||
*/
|
||||
var Stemmer = function() {
|
||||
|
||||
var step2list = {
|
||||
ational: 'ate',
|
||||
tional: 'tion',
|
||||
enci: 'ence',
|
||||
anci: 'ance',
|
||||
izer: 'ize',
|
||||
bli: 'ble',
|
||||
alli: 'al',
|
||||
entli: 'ent',
|
||||
eli: 'e',
|
||||
ousli: 'ous',
|
||||
ization: 'ize',
|
||||
ation: 'ate',
|
||||
ator: 'ate',
|
||||
alism: 'al',
|
||||
iveness: 'ive',
|
||||
fulness: 'ful',
|
||||
ousness: 'ous',
|
||||
aliti: 'al',
|
||||
iviti: 'ive',
|
||||
biliti: 'ble',
|
||||
logi: 'log'
|
||||
};
|
||||
|
||||
var step3list = {
|
||||
icate: 'ic',
|
||||
ative: '',
|
||||
alize: 'al',
|
||||
iciti: 'ic',
|
||||
ical: 'ic',
|
||||
ful: '',
|
||||
ness: ''
|
||||
};
|
||||
|
||||
var c = "[^aeiou]"; // consonant
|
||||
var v = "[aeiouy]"; // vowel
|
||||
var C = c + "[^aeiouy]*"; // consonant sequence
|
||||
var V = v + "[aeiou]*"; // vowel sequence
|
||||
|
||||
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
|
||||
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
|
||||
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
|
||||
var s_v = "^(" + C + ")?" + v; // vowel in stem
|
||||
|
||||
this.stemWord = function (w) {
|
||||
var stem;
|
||||
var suffix;
|
||||
var firstch;
|
||||
var origword = w;
|
||||
|
||||
if (w.length < 3)
|
||||
return w;
|
||||
|
||||
var re;
|
||||
var re2;
|
||||
var re3;
|
||||
var re4;
|
||||
|
||||
firstch = w.substr(0,1);
|
||||
if (firstch == "y")
|
||||
w = firstch.toUpperCase() + w.substr(1);
|
||||
|
||||
// Step 1a
|
||||
re = /^(.+?)(ss|i)es$/;
|
||||
re2 = /^(.+?)([^s])s$/;
|
||||
|
||||
if (re.test(w))
|
||||
w = w.replace(re,"$1$2");
|
||||
else if (re2.test(w))
|
||||
w = w.replace(re2,"$1$2");
|
||||
|
||||
// Step 1b
|
||||
re = /^(.+?)eed$/;
|
||||
re2 = /^(.+?)(ed|ing)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(fp[1])) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1];
|
||||
re2 = new RegExp(s_v);
|
||||
if (re2.test(stem)) {
|
||||
w = stem;
|
||||
re2 = /(at|bl|iz)$/;
|
||||
re3 = new RegExp("([^aeiouylsz])\\1$");
|
||||
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re2.test(w))
|
||||
w = w + "e";
|
||||
else if (re3.test(w)) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
else if (re4.test(w))
|
||||
w = w + "e";
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1c
|
||||
re = /^(.+?)y$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(s_v);
|
||||
if (re.test(stem))
|
||||
w = stem + "i";
|
||||
}
|
||||
|
||||
// Step 2
|
||||
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem))
|
||||
w = stem + step2list[suffix];
|
||||
}
|
||||
|
||||
// Step 3
|
||||
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem))
|
||||
w = stem + step3list[suffix];
|
||||
}
|
||||
|
||||
// Step 4
|
||||
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
|
||||
re2 = /^(.+?)(s|t)(ion)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
if (re.test(stem))
|
||||
w = stem;
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1] + fp[2];
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re2.test(stem))
|
||||
w = stem;
|
||||
}
|
||||
|
||||
// Step 5
|
||||
re = /^(.+?)e$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
re2 = new RegExp(meq1);
|
||||
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
|
||||
w = stem;
|
||||
}
|
||||
re = /ll$/;
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re.test(w) && re2.test(w)) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
|
||||
// and turn initial Y back to y
|
||||
if (firstch == "y")
|
||||
w = firstch.toLowerCase() + w.substr(1);
|
||||
return w;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Search Module
|
||||
*/
|
||||
var Search = {
|
||||
|
||||
_index : null,
|
||||
_queued_query : null,
|
||||
_pulse_status : -1,
|
||||
|
||||
init : function() {
|
||||
var params = $.getQueryParameters();
|
||||
if (params.q) {
|
||||
var query = params.q[0];
|
||||
$('input[name="q"]')[0].value = query;
|
||||
this.performSearch(query);
|
||||
}
|
||||
},
|
||||
|
||||
loadIndex : function(url) {
|
||||
$.ajax({type: "GET", url: url, data: null, success: null,
|
||||
dataType: "script", cache: true});
|
||||
},
|
||||
|
||||
setIndex : function(index) {
|
||||
var q;
|
||||
this._index = index;
|
||||
if ((q = this._queued_query) !== null) {
|
||||
this._queued_query = null;
|
||||
Search.query(q);
|
||||
}
|
||||
},
|
||||
|
||||
hasIndex : function() {
|
||||
return this._index !== null;
|
||||
},
|
||||
|
||||
deferQuery : function(query) {
|
||||
this._queued_query = query;
|
||||
},
|
||||
|
||||
stopPulse : function() {
|
||||
this._pulse_status = 0;
|
||||
},
|
||||
|
||||
startPulse : function() {
|
||||
if (this._pulse_status >= 0)
|
||||
return;
|
||||
function pulse() {
|
||||
Search._pulse_status = (Search._pulse_status + 1) % 4;
|
||||
var dotString = '';
|
||||
for (var i = 0; i < Search._pulse_status; i++)
|
||||
dotString += '.';
|
||||
Search.dots.text(dotString);
|
||||
if (Search._pulse_status > -1)
|
||||
window.setTimeout(pulse, 500);
|
||||
};
|
||||
pulse();
|
||||
},
|
||||
|
||||
/**
|
||||
* perform a search for something
|
||||
*/
|
||||
performSearch : function(query) {
|
||||
// create the required interface elements
|
||||
this.out = $('#search-results');
|
||||
this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out);
|
||||
this.dots = $('<span></span>').appendTo(this.title);
|
||||
this.status = $('<p style="display: none"></p>').appendTo(this.out);
|
||||
this.output = $('<ul class="search"/>').appendTo(this.out);
|
||||
|
||||
$('#search-progress').text(_('Preparing search...'));
|
||||
this.startPulse();
|
||||
|
||||
// index already loaded, the browser was quick!
|
||||
if (this.hasIndex())
|
||||
this.query(query);
|
||||
else
|
||||
this.deferQuery(query);
|
||||
},
|
||||
|
||||
query : function(query) {
|
||||
var stopwords = ["and","then","into","it","as","are","in","if","for","no","there","their","was","is","be","to","that","but","they","not","such","with","by","a","on","these","of","will","this","near","the","or","at"];
|
||||
|
||||
// Stem the searchterms and add them to the correct list
|
||||
var stemmer = new Stemmer();
|
||||
var searchterms = [];
|
||||
var excluded = [];
|
||||
var hlterms = [];
|
||||
var tmp = query.split(/\s+/);
|
||||
var objectterms = [];
|
||||
for (var i = 0; i < tmp.length; i++) {
|
||||
if (tmp[i] != "") {
|
||||
objectterms.push(tmp[i].toLowerCase());
|
||||
}
|
||||
|
||||
if ($u.indexOf(stopwords, tmp[i]) != -1 || tmp[i].match(/^\d+$/) ||
|
||||
tmp[i] == "") {
|
||||
// skip this "word"
|
||||
continue;
|
||||
}
|
||||
// stem the word
|
||||
var word = stemmer.stemWord(tmp[i]).toLowerCase();
|
||||
// select the correct list
|
||||
if (word[0] == '-') {
|
||||
var toAppend = excluded;
|
||||
word = word.substr(1);
|
||||
}
|
||||
else {
|
||||
var toAppend = searchterms;
|
||||
hlterms.push(tmp[i].toLowerCase());
|
||||
}
|
||||
// only add if not already in the list
|
||||
if (!$.contains(toAppend, word))
|
||||
toAppend.push(word);
|
||||
};
|
||||
var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" "));
|
||||
|
||||
// console.debug('SEARCH: searching for:');
|
||||
// console.info('required: ', searchterms);
|
||||
// console.info('excluded: ', excluded);
|
||||
|
||||
// prepare search
|
||||
var filenames = this._index.filenames;
|
||||
var titles = this._index.titles;
|
||||
var terms = this._index.terms;
|
||||
var fileMap = {};
|
||||
var files = null;
|
||||
// different result priorities
|
||||
var importantResults = [];
|
||||
var objectResults = [];
|
||||
var regularResults = [];
|
||||
var unimportantResults = [];
|
||||
$('#search-progress').empty();
|
||||
|
||||
// lookup as object
|
||||
for (var i = 0; i < objectterms.length; i++) {
|
||||
var others = [].concat(objectterms.slice(0,i),
|
||||
objectterms.slice(i+1, objectterms.length))
|
||||
var results = this.performObjectSearch(objectterms[i], others);
|
||||
// Assume first word is most likely to be the object,
|
||||
// other words more likely to be in description.
|
||||
// Therefore put matches for earlier words first.
|
||||
// (Results are eventually used in reverse order).
|
||||
objectResults = results[0].concat(objectResults);
|
||||
importantResults = results[1].concat(importantResults);
|
||||
unimportantResults = results[2].concat(unimportantResults);
|
||||
}
|
||||
|
||||
// perform the search on the required terms
|
||||
for (var i = 0; i < searchterms.length; i++) {
|
||||
var word = searchterms[i];
|
||||
// no match but word was a required one
|
||||
if ((files = terms[word]) == null)
|
||||
break;
|
||||
if (files.length == undefined) {
|
||||
files = [files];
|
||||
}
|
||||
// create the mapping
|
||||
for (var j = 0; j < files.length; j++) {
|
||||
var file = files[j];
|
||||
if (file in fileMap)
|
||||
fileMap[file].push(word);
|
||||
else
|
||||
fileMap[file] = [word];
|
||||
}
|
||||
}
|
||||
|
||||
// now check if the files don't contain excluded terms
|
||||
for (var file in fileMap) {
|
||||
var valid = true;
|
||||
|
||||
// check if all requirements are matched
|
||||
if (fileMap[file].length != searchterms.length)
|
||||
continue;
|
||||
|
||||
// ensure that none of the excluded terms is in the
|
||||
// search result.
|
||||
for (var i = 0; i < excluded.length; i++) {
|
||||
if (terms[excluded[i]] == file ||
|
||||
$.contains(terms[excluded[i]] || [], file)) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if we have still a valid result we can add it
|
||||
// to the result list
|
||||
if (valid)
|
||||
regularResults.push([filenames[file], titles[file], '', null]);
|
||||
}
|
||||
|
||||
// delete unused variables in order to not waste
|
||||
// memory until list is retrieved completely
|
||||
delete filenames, titles, terms;
|
||||
|
||||
// now sort the regular results descending by title
|
||||
regularResults.sort(function(a, b) {
|
||||
var left = a[1].toLowerCase();
|
||||
var right = b[1].toLowerCase();
|
||||
return (left > right) ? -1 : ((left < right) ? 1 : 0);
|
||||
});
|
||||
|
||||
// combine all results
|
||||
var results = unimportantResults.concat(regularResults)
|
||||
.concat(objectResults).concat(importantResults);
|
||||
|
||||
// print the results
|
||||
var resultCount = results.length;
|
||||
function displayNextItem() {
|
||||
// results left, load the summary and display it
|
||||
if (results.length) {
|
||||
var item = results.pop();
|
||||
var listItem = $('<li style="display:none"></li>');
|
||||
if (DOCUMENTATION_OPTIONS.FILE_SUFFIX == '') {
|
||||
// dirhtml builder
|
||||
var dirname = item[0] + '/';
|
||||
if (dirname.match(/\/index\/$/)) {
|
||||
dirname = dirname.substring(0, dirname.length-6);
|
||||
} else if (dirname == 'index/') {
|
||||
dirname = '';
|
||||
}
|
||||
listItem.append($('<a/>').attr('href',
|
||||
DOCUMENTATION_OPTIONS.URL_ROOT + dirname +
|
||||
highlightstring + item[2]).html(item[1]));
|
||||
} else {
|
||||
// normal html builders
|
||||
listItem.append($('<a/>').attr('href',
|
||||
item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX +
|
||||
highlightstring + item[2]).html(item[1]));
|
||||
}
|
||||
if (item[3]) {
|
||||
listItem.append($('<span> (' + item[3] + ')</span>'));
|
||||
Search.output.append(listItem);
|
||||
listItem.slideDown(5, function() {
|
||||
displayNextItem();
|
||||
});
|
||||
} else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
|
||||
$.get(DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' +
|
||||
item[0] + '.txt', function(data) {
|
||||
if (data != '') {
|
||||
listItem.append($.makeSearchSummary(data, searchterms, hlterms));
|
||||
Search.output.append(listItem);
|
||||
}
|
||||
listItem.slideDown(5, function() {
|
||||
displayNextItem();
|
||||
});
|
||||
}, "text");
|
||||
} else {
|
||||
// no source available, just display title
|
||||
Search.output.append(listItem);
|
||||
listItem.slideDown(5, function() {
|
||||
displayNextItem();
|
||||
});
|
||||
}
|
||||
}
|
||||
// search finished, update title and status message
|
||||
else {
|
||||
Search.stopPulse();
|
||||
Search.title.text(_('Search Results'));
|
||||
if (!resultCount)
|
||||
Search.status.text(_('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.'));
|
||||
else
|
||||
Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount));
|
||||
Search.status.fadeIn(500);
|
||||
}
|
||||
}
|
||||
displayNextItem();
|
||||
},
|
||||
|
||||
performObjectSearch : function(object, otherterms) {
|
||||
var filenames = this._index.filenames;
|
||||
var objects = this._index.objects;
|
||||
var objnames = this._index.objnames;
|
||||
var titles = this._index.titles;
|
||||
|
||||
var importantResults = [];
|
||||
var objectResults = [];
|
||||
var unimportantResults = [];
|
||||
|
||||
for (var prefix in objects) {
|
||||
for (var name in objects[prefix]) {
|
||||
var fullname = (prefix ? prefix + '.' : '') + name;
|
||||
if (fullname.toLowerCase().indexOf(object) > -1) {
|
||||
var match = objects[prefix][name];
|
||||
var objname = objnames[match[1]][2];
|
||||
var title = titles[match[0]];
|
||||
// If more than one term searched for, we require other words to be
|
||||
// found in the name/title/description
|
||||
if (otherterms.length > 0) {
|
||||
var haystack = (prefix + ' ' + name + ' ' +
|
||||
objname + ' ' + title).toLowerCase();
|
||||
var allfound = true;
|
||||
for (var i = 0; i < otherterms.length; i++) {
|
||||
if (haystack.indexOf(otherterms[i]) == -1) {
|
||||
allfound = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allfound) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
var descr = objname + _(', in ') + title;
|
||||
anchor = match[3];
|
||||
if (anchor == '')
|
||||
anchor = fullname;
|
||||
else if (anchor == '-')
|
||||
anchor = objnames[match[1]][1] + '-' + fullname;
|
||||
result = [filenames[match[0]], fullname, '#'+anchor, descr];
|
||||
switch (match[2]) {
|
||||
case 1: objectResults.push(result); break;
|
||||
case 0: importantResults.push(result); break;
|
||||
case 2: unimportantResults.push(result); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sort results descending
|
||||
objectResults.sort(function(a, b) {
|
||||
return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
|
||||
});
|
||||
|
||||
importantResults.sort(function(a, b) {
|
||||
return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
|
||||
});
|
||||
|
||||
unimportantResults.sort(function(a, b) {
|
||||
return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
|
||||
});
|
||||
|
||||
return [importantResults, objectResults, unimportantResults]
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
Search.init();
|
||||
});
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* sidebar.js
|
||||
* ~~~~~~~~~~
|
||||
*
|
||||
* This script makes the Sphinx sidebar collapsible.
|
||||
*
|
||||
* .sphinxsidebar contains .sphinxsidebarwrapper. This script adds
|
||||
* in .sphixsidebar, after .sphinxsidebarwrapper, the #sidebarbutton
|
||||
* used to collapse and expand the sidebar.
|
||||
*
|
||||
* When the sidebar is collapsed the .sphinxsidebarwrapper is hidden
|
||||
* and the width of the sidebar and the margin-left of the document
|
||||
* are decreased. When the sidebar is expanded the opposite happens.
|
||||
* This script saves a per-browser/per-session cookie used to
|
||||
* remember the position of the sidebar among the pages.
|
||||
* Once the browser is closed the cookie is deleted and the position
|
||||
* reset to the default (expanded).
|
||||
*
|
||||
* :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
$(function() {
|
||||
// global elements used by the functions.
|
||||
// the 'sidebarbutton' element is defined as global after its
|
||||
// creation, in the add_sidebar_button function
|
||||
var bodywrapper = $('.bodywrapper');
|
||||
var sidebar = $('.sphinxsidebar');
|
||||
var sidebarwrapper = $('.sphinxsidebarwrapper');
|
||||
|
||||
// for some reason, the document has no sidebar; do not run into errors
|
||||
if (!sidebar.length) return;
|
||||
|
||||
// original margin-left of the bodywrapper and width of the sidebar
|
||||
// with the sidebar expanded
|
||||
var bw_margin_expanded = bodywrapper.css('margin-left');
|
||||
var ssb_width_expanded = sidebar.width();
|
||||
|
||||
// margin-left of the bodywrapper and width of the sidebar
|
||||
// with the sidebar collapsed
|
||||
var bw_margin_collapsed = '.8em';
|
||||
var ssb_width_collapsed = '.8em';
|
||||
|
||||
// colors used by the current theme
|
||||
var dark_color = $('.related').css('background-color');
|
||||
var light_color = $('.document').css('background-color');
|
||||
|
||||
function sidebar_is_collapsed() {
|
||||
return sidebarwrapper.is(':not(:visible)');
|
||||
}
|
||||
|
||||
function toggle_sidebar() {
|
||||
if (sidebar_is_collapsed())
|
||||
expand_sidebar();
|
||||
else
|
||||
collapse_sidebar();
|
||||
}
|
||||
|
||||
function collapse_sidebar() {
|
||||
sidebarwrapper.hide();
|
||||
sidebar.css('width', ssb_width_collapsed);
|
||||
bodywrapper.css('margin-left', bw_margin_collapsed);
|
||||
sidebarbutton.css({
|
||||
'margin-left': '0',
|
||||
'height': bodywrapper.height()
|
||||
});
|
||||
sidebarbutton.find('span').text('»');
|
||||
sidebarbutton.attr('title', _('Expand sidebar'));
|
||||
document.cookie = 'sidebar=collapsed';
|
||||
}
|
||||
|
||||
function expand_sidebar() {
|
||||
bodywrapper.css('margin-left', bw_margin_expanded);
|
||||
sidebar.css('width', ssb_width_expanded);
|
||||
sidebarwrapper.show();
|
||||
sidebarbutton.css({
|
||||
'margin-left': ssb_width_expanded-12,
|
||||
'height': bodywrapper.height()
|
||||
});
|
||||
sidebarbutton.find('span').text('«');
|
||||
sidebarbutton.attr('title', _('Collapse sidebar'));
|
||||
document.cookie = 'sidebar=expanded';
|
||||
}
|
||||
|
||||
function add_sidebar_button() {
|
||||
sidebarwrapper.css({
|
||||
'float': 'left',
|
||||
'margin-right': '0',
|
||||
'width': ssb_width_expanded - 28
|
||||
});
|
||||
// create the button
|
||||
sidebar.append(
|
||||
'<div id="sidebarbutton"><span>«</span></div>'
|
||||
);
|
||||
var sidebarbutton = $('#sidebarbutton');
|
||||
light_color = sidebarbutton.css('background-color');
|
||||
// find the height of the viewport to center the '<<' in the page
|
||||
var viewport_height;
|
||||
if (window.innerHeight)
|
||||
viewport_height = window.innerHeight;
|
||||
else
|
||||
viewport_height = $(window).height();
|
||||
sidebarbutton.find('span').css({
|
||||
'display': 'block',
|
||||
'margin-top': (viewport_height - sidebar.position().top - 20) / 2
|
||||
});
|
||||
|
||||
sidebarbutton.click(toggle_sidebar);
|
||||
sidebarbutton.attr('title', _('Collapse sidebar'));
|
||||
sidebarbutton.css({
|
||||
'color': '#FFFFFF',
|
||||
'border-left': '1px solid ' + dark_color,
|
||||
'font-size': '1.2em',
|
||||
'cursor': 'pointer',
|
||||
'height': bodywrapper.height(),
|
||||
'padding-top': '1px',
|
||||
'margin-left': ssb_width_expanded - 12
|
||||
});
|
||||
|
||||
sidebarbutton.hover(
|
||||
function () {
|
||||
$(this).css('background-color', dark_color);
|
||||
},
|
||||
function () {
|
||||
$(this).css('background-color', light_color);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function set_position_from_cookie() {
|
||||
if (!document.cookie)
|
||||
return;
|
||||
var items = document.cookie.split(';');
|
||||
for(var k=0; k<items.length; k++) {
|
||||
var key_val = items[k].split('=');
|
||||
var key = key_val[0];
|
||||
if (key == 'sidebar') {
|
||||
var value = key_val[1];
|
||||
if ((value == 'collapsed') && (!sidebar_is_collapsed()))
|
||||
collapse_sidebar();
|
||||
else if ((value == 'expanded') && (sidebar_is_collapsed()))
|
||||
expand_sidebar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_sidebar_button();
|
||||
var sidebarbutton = $('#sidebarbutton');
|
||||
set_position_from_cookie();
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
// Underscore.js 0.5.5
|
||||
// (c) 2009 Jeremy Ashkenas, DocumentCloud Inc.
|
||||
// Underscore is freely distributable under the terms of the MIT license.
|
||||
// Portions of Underscore are inspired by or borrowed from Prototype.js,
|
||||
// Oliver Steele's Functional, and John Resig's Micro-Templating.
|
||||
// For all details and documentation:
|
||||
// http://documentcloud.github.com/underscore/
|
||||
(function(){var j=this,n=j._,i=function(a){this._wrapped=a},m=typeof StopIteration!=="undefined"?StopIteration:"__break__",b=j._=function(a){return new i(a)};if(typeof exports!=="undefined")exports._=b;var k=Array.prototype.slice,o=Array.prototype.unshift,p=Object.prototype.toString,q=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;b.VERSION="0.5.5";b.each=function(a,c,d){try{if(a.forEach)a.forEach(c,d);else if(b.isArray(a)||b.isArguments(a))for(var e=0,f=a.length;e<f;e++)c.call(d,
|
||||
a[e],e,a);else{var g=b.keys(a);f=g.length;for(e=0;e<f;e++)c.call(d,a[g[e]],g[e],a)}}catch(h){if(h!=m)throw h;}return a};b.map=function(a,c,d){if(a&&b.isFunction(a.map))return a.map(c,d);var e=[];b.each(a,function(f,g,h){e.push(c.call(d,f,g,h))});return e};b.reduce=function(a,c,d,e){if(a&&b.isFunction(a.reduce))return a.reduce(b.bind(d,e),c);b.each(a,function(f,g,h){c=d.call(e,c,f,g,h)});return c};b.reduceRight=function(a,c,d,e){if(a&&b.isFunction(a.reduceRight))return a.reduceRight(b.bind(d,e),c);
|
||||
var f=b.clone(b.toArray(a)).reverse();b.each(f,function(g,h){c=d.call(e,c,g,h,a)});return c};b.detect=function(a,c,d){var e;b.each(a,function(f,g,h){if(c.call(d,f,g,h)){e=f;b.breakLoop()}});return e};b.select=function(a,c,d){if(a&&b.isFunction(a.filter))return a.filter(c,d);var e=[];b.each(a,function(f,g,h){c.call(d,f,g,h)&&e.push(f)});return e};b.reject=function(a,c,d){var e=[];b.each(a,function(f,g,h){!c.call(d,f,g,h)&&e.push(f)});return e};b.all=function(a,c,d){c=c||b.identity;if(a&&b.isFunction(a.every))return a.every(c,
|
||||
d);var e=true;b.each(a,function(f,g,h){(e=e&&c.call(d,f,g,h))||b.breakLoop()});return e};b.any=function(a,c,d){c=c||b.identity;if(a&&b.isFunction(a.some))return a.some(c,d);var e=false;b.each(a,function(f,g,h){if(e=c.call(d,f,g,h))b.breakLoop()});return e};b.include=function(a,c){if(b.isArray(a))return b.indexOf(a,c)!=-1;var d=false;b.each(a,function(e){if(d=e===c)b.breakLoop()});return d};b.invoke=function(a,c){var d=b.rest(arguments,2);return b.map(a,function(e){return(c?e[c]:e).apply(e,d)})};b.pluck=
|
||||
function(a,c){return b.map(a,function(d){return d[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);var e={computed:-Infinity};b.each(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g>=e.computed&&(e={value:f,computed:g})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};b.each(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g<e.computed&&(e={value:f,computed:g})});return e.value};b.sortBy=function(a,c,d){return b.pluck(b.map(a,
|
||||
function(e,f,g){return{value:e,criteria:c.call(d,e,f,g)}}).sort(function(e,f){e=e.criteria;f=f.criteria;return e<f?-1:e>f?1:0}),"value")};b.sortedIndex=function(a,c,d){d=d||b.identity;for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?(e=g+1):(f=g)}return e};b.toArray=function(a){if(!a)return[];if(a.toArray)return a.toArray();if(b.isArray(a))return a;if(b.isArguments(a))return k.call(a);return b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=function(a,c,d){return c&&!d?k.call(a,
|
||||
0,c):a[0]};b.rest=function(a,c,d){return k.call(a,b.isUndefined(c)||d?1:c)};b.last=function(a){return a[a.length-1]};b.compact=function(a){return b.select(a,function(c){return!!c})};b.flatten=function(a){return b.reduce(a,[],function(c,d){if(b.isArray(d))return c.concat(b.flatten(d));c.push(d);return c})};b.without=function(a){var c=b.rest(arguments);return b.select(a,function(d){return!b.include(c,d)})};b.uniq=function(a,c){return b.reduce(a,[],function(d,e,f){if(0==f||(c===true?b.last(d)!=e:!b.include(d,
|
||||
e)))d.push(e);return d})};b.intersect=function(a){var c=b.rest(arguments);return b.select(b.uniq(a),function(d){return b.all(c,function(e){return b.indexOf(e,d)>=0})})};b.zip=function(){for(var a=b.toArray(arguments),c=b.max(b.pluck(a,"length")),d=new Array(c),e=0;e<c;e++)d[e]=b.pluck(a,String(e));return d};b.indexOf=function(a,c){if(a.indexOf)return a.indexOf(c);for(var d=0,e=a.length;d<e;d++)if(a[d]===c)return d;return-1};b.lastIndexOf=function(a,c){if(a.lastIndexOf)return a.lastIndexOf(c);for(var d=
|
||||
a.length;d--;)if(a[d]===c)return d;return-1};b.range=function(a,c,d){var e=b.toArray(arguments),f=e.length<=1;a=f?0:e[0];c=f?e[0]:e[1];d=e[2]||1;e=Math.ceil((c-a)/d);if(e<=0)return[];e=new Array(e);f=a;for(var g=0;1;f+=d){if((d>0?f-c:c-f)>=0)return e;e[g++]=f}};b.bind=function(a,c){var d=b.rest(arguments,2);return function(){return a.apply(c||j,d.concat(b.toArray(arguments)))}};b.bindAll=function(a){var c=b.rest(arguments);if(c.length==0)c=b.functions(a);b.each(c,function(d){a[d]=b.bind(a[d],a)});
|
||||
return a};b.delay=function(a,c){var d=b.rest(arguments,2);return setTimeout(function(){return a.apply(a,d)},c)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(b.rest(arguments)))};b.wrap=function(a,c){return function(){var d=[a].concat(b.toArray(arguments));return c.apply(c,d)}};b.compose=function(){var a=b.toArray(arguments);return function(){for(var c=b.toArray(arguments),d=a.length-1;d>=0;d--)c=[a[d].apply(this,c)];return c[0]}};b.keys=function(a){if(b.isArray(a))return b.range(0,a.length);
|
||||
var c=[];for(var d in a)q.call(a,d)&&c.push(d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=function(a){return b.select(b.keys(a),function(c){return b.isFunction(a[c])}).sort()};b.extend=function(a,c){for(var d in c)a[d]=c[d];return a};b.clone=function(a){if(b.isArray(a))return a.slice(0);return b.extend({},a)};b.tap=function(a,c){c(a);return a};b.isEqual=function(a,c){if(a===c)return true;var d=typeof a;if(d!=typeof c)return false;if(a==c)return true;if(!a&&c||a&&!c)return false;
|
||||
if(a.isEqual)return a.isEqual(c);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return true;if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return false;if(a.length&&a.length!==c.length)return false;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return false;for(var f in a)if(!b.isEqual(a[f],c[f]))return false;return true};b.isEmpty=function(a){return b.keys(a).length==
|
||||
0};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=function(a){return!!(a&&a.concat&&a.unshift)};b.isArguments=function(a){return a&&b.isNumber(a.length)&&!b.isArray(a)&&!r.call(a,"length")};b.isFunction=function(a){return!!(a&&a.constructor&&a.call&&a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};b.isNumber=function(a){return p.call(a)==="[object Number]"};b.isDate=function(a){return!!(a&&a.getTimezoneOffset&&a.setUTCFullYear)};b.isRegExp=function(a){return!!(a&&
|
||||
a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};b.isNaN=function(a){return b.isNumber(a)&&isNaN(a)};b.isNull=function(a){return a===null};b.isUndefined=function(a){return typeof a=="undefined"};b.noConflict=function(){j._=n;return this};b.identity=function(a){return a};b.breakLoop=function(){throw m;};var s=0;b.uniqueId=function(a){var c=s++;return a?a+c:c};b.template=function(a,c){a=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g,
|
||||
" ").replace(/'(?=[^%]*%>)/g,"\t").split("'").join("\\'").split("\t").join("'").replace(/<%=(.+?)%>/g,"',$1,'").split("<%").join("');").split("%>").join("p.push('")+"');}return p.join('');");return c?a(c):a};b.forEach=b.each;b.foldl=b.inject=b.reduce;b.foldr=b.reduceRight;b.filter=b.select;b.every=b.all;b.some=b.any;b.head=b.first;b.tail=b.rest;b.methods=b.functions;var l=function(a,c){return c?b(a).chain():a};b.each(b.functions(b),function(a){var c=b[a];i.prototype[a]=function(){var d=b.toArray(arguments);
|
||||
o.call(d,this._wrapped);return l(c.apply(b,d),this._chain)}});b.each(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var c=Array.prototype[a];i.prototype[a]=function(){c.apply(this._wrapped,arguments);return l(this._wrapped,this._chain)}});b.each(["concat","join","slice"],function(a){var c=Array.prototype[a];i.prototype[a]=function(){return l(c.apply(this._wrapped,arguments),this._chain)}});i.prototype.chain=function(){this._chain=true;return this};i.prototype.value=function(){return this._wrapped}})();
|
After Width: | Height: | Size: 372 B |
After Width: | Height: | Size: 363 B |
|
@ -0,0 +1,808 @@
|
|||
/*
|
||||
* websupport.js
|
||||
* ~~~~~~~~~~~~~
|
||||
*
|
||||
* sphinx.websupport utilties for all documentation.
|
||||
*
|
||||
* :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
$.fn.autogrow = function() {
|
||||
return this.each(function() {
|
||||
var textarea = this;
|
||||
|
||||
$.fn.autogrow.resize(textarea);
|
||||
|
||||
$(textarea)
|
||||
.focus(function() {
|
||||
textarea.interval = setInterval(function() {
|
||||
$.fn.autogrow.resize(textarea);
|
||||
}, 500);
|
||||
})
|
||||
.blur(function() {
|
||||
clearInterval(textarea.interval);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.autogrow.resize = function(textarea) {
|
||||
var lineHeight = parseInt($(textarea).css('line-height'), 10);
|
||||
var lines = textarea.value.split('\n');
|
||||
var columns = textarea.cols;
|
||||
var lineCount = 0;
|
||||
$.each(lines, function() {
|
||||
lineCount += Math.ceil(this.length / columns) || 1;
|
||||
});
|
||||
var height = lineHeight * (lineCount + 1);
|
||||
$(textarea).css('height', height);
|
||||
};
|
||||
})(jQuery);
|
||||
|
||||
(function($) {
|
||||
var comp, by;
|
||||
|
||||
function init() {
|
||||
initEvents();
|
||||
initComparator();
|
||||
}
|
||||
|
||||
function initEvents() {
|
||||
$('a.comment-close').live("click", function(event) {
|
||||
event.preventDefault();
|
||||
hide($(this).attr('id').substring(2));
|
||||
});
|
||||
$('a.vote').live("click", function(event) {
|
||||
event.preventDefault();
|
||||
handleVote($(this));
|
||||
});
|
||||
$('a.reply').live("click", function(event) {
|
||||
event.preventDefault();
|
||||
openReply($(this).attr('id').substring(2));
|
||||
});
|
||||
$('a.close-reply').live("click", function(event) {
|
||||
event.preventDefault();
|
||||
closeReply($(this).attr('id').substring(2));
|
||||
});
|
||||
$('a.sort-option').live("click", function(event) {
|
||||
event.preventDefault();
|
||||
handleReSort($(this));
|
||||
});
|
||||
$('a.show-proposal').live("click", function(event) {
|
||||
event.preventDefault();
|
||||
showProposal($(this).attr('id').substring(2));
|
||||
});
|
||||
$('a.hide-proposal').live("click", function(event) {
|
||||
event.preventDefault();
|
||||
hideProposal($(this).attr('id').substring(2));
|
||||
});
|
||||
$('a.show-propose-change').live("click", function(event) {
|
||||
event.preventDefault();
|
||||
showProposeChange($(this).attr('id').substring(2));
|
||||
});
|
||||
$('a.hide-propose-change').live("click", function(event) {
|
||||
event.preventDefault();
|
||||
hideProposeChange($(this).attr('id').substring(2));
|
||||
});
|
||||
$('a.accept-comment').live("click", function(event) {
|
||||
event.preventDefault();
|
||||
acceptComment($(this).attr('id').substring(2));
|
||||
});
|
||||
$('a.delete-comment').live("click", function(event) {
|
||||
event.preventDefault();
|
||||
deleteComment($(this).attr('id').substring(2));
|
||||
});
|
||||
$('a.comment-markup').live("click", function(event) {
|
||||
event.preventDefault();
|
||||
toggleCommentMarkupBox($(this).attr('id').substring(2));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set comp, which is a comparator function used for sorting and
|
||||
* inserting comments into the list.
|
||||
*/
|
||||
function setComparator() {
|
||||
// If the first three letters are "asc", sort in ascending order
|
||||
// and remove the prefix.
|
||||
if (by.substring(0,3) == 'asc') {
|
||||
var i = by.substring(3);
|
||||
comp = function(a, b) { return a[i] - b[i]; };
|
||||
} else {
|
||||
// Otherwise sort in descending order.
|
||||
comp = function(a, b) { return b[by] - a[by]; };
|
||||
}
|
||||
|
||||
// Reset link styles and format the selected sort option.
|
||||
$('a.sel').attr('href', '#').removeClass('sel');
|
||||
$('a.by' + by).removeAttr('href').addClass('sel');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a comp function. If the user has preferences stored in
|
||||
* the sortBy cookie, use those, otherwise use the default.
|
||||
*/
|
||||
function initComparator() {
|
||||
by = 'rating'; // Default to sort by rating.
|
||||
// If the sortBy cookie is set, use that instead.
|
||||
if (document.cookie.length > 0) {
|
||||
var start = document.cookie.indexOf('sortBy=');
|
||||
if (start != -1) {
|
||||
start = start + 7;
|
||||
var end = document.cookie.indexOf(";", start);
|
||||
if (end == -1) {
|
||||
end = document.cookie.length;
|
||||
by = unescape(document.cookie.substring(start, end));
|
||||
}
|
||||
}
|
||||
}
|
||||
setComparator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a comment div.
|
||||
*/
|
||||
function show(id) {
|
||||
$('#ao' + id).hide();
|
||||
$('#ah' + id).show();
|
||||
var context = $.extend({id: id}, opts);
|
||||
var popup = $(renderTemplate(popupTemplate, context)).hide();
|
||||
popup.find('textarea[name="proposal"]').hide();
|
||||
popup.find('a.by' + by).addClass('sel');
|
||||
var form = popup.find('#cf' + id);
|
||||
form.submit(function(event) {
|
||||
event.preventDefault();
|
||||
addComment(form);
|
||||
});
|
||||
$('#s' + id).after(popup);
|
||||
popup.slideDown('fast', function() {
|
||||
getComments(id);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide a comment div.
|
||||
*/
|
||||
function hide(id) {
|
||||
$('#ah' + id).hide();
|
||||
$('#ao' + id).show();
|
||||
var div = $('#sc' + id);
|
||||
div.slideUp('fast', function() {
|
||||
div.remove();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an ajax request to get comments for a node
|
||||
* and insert the comments into the comments tree.
|
||||
*/
|
||||
function getComments(id) {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: opts.getCommentsURL,
|
||||
data: {node: id},
|
||||
success: function(data, textStatus, request) {
|
||||
var ul = $('#cl' + id);
|
||||
var speed = 100;
|
||||
$('#cf' + id)
|
||||
.find('textarea[name="proposal"]')
|
||||
.data('source', data.source);
|
||||
|
||||
if (data.comments.length === 0) {
|
||||
ul.html('<li>No comments yet.</li>');
|
||||
ul.data('empty', true);
|
||||
} else {
|
||||
// If there are comments, sort them and put them in the list.
|
||||
var comments = sortComments(data.comments);
|
||||
speed = data.comments.length * 100;
|
||||
appendComments(comments, ul);
|
||||
ul.data('empty', false);
|
||||
}
|
||||
$('#cn' + id).slideUp(speed + 200);
|
||||
ul.slideDown(speed);
|
||||
},
|
||||
error: function(request, textStatus, error) {
|
||||
showError('Oops, there was a problem retrieving the comments.');
|
||||
},
|
||||
dataType: 'json'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a comment via ajax and insert the comment into the comment tree.
|
||||
*/
|
||||
function addComment(form) {
|
||||
var node_id = form.find('input[name="node"]').val();
|
||||
var parent_id = form.find('input[name="parent"]').val();
|
||||
var text = form.find('textarea[name="comment"]').val();
|
||||
var proposal = form.find('textarea[name="proposal"]').val();
|
||||
|
||||
if (text == '') {
|
||||
showError('Please enter a comment.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable the form that is being submitted.
|
||||
form.find('textarea,input').attr('disabled', 'disabled');
|
||||
|
||||
// Send the comment to the server.
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: opts.addCommentURL,
|
||||
dataType: 'json',
|
||||
data: {
|
||||
node: node_id,
|
||||
parent: parent_id,
|
||||
text: text,
|
||||
proposal: proposal
|
||||
},
|
||||
success: function(data, textStatus, error) {
|
||||
// Reset the form.
|
||||
if (node_id) {
|
||||
hideProposeChange(node_id);
|
||||
}
|
||||
form.find('textarea')
|
||||
.val('')
|
||||
.add(form.find('input'))
|
||||
.removeAttr('disabled');
|
||||
var ul = $('#cl' + (node_id || parent_id));
|
||||
if (ul.data('empty')) {
|
||||
$(ul).empty();
|
||||
ul.data('empty', false);
|
||||
}
|
||||
insertComment(data.comment);
|
||||
var ao = $('#ao' + node_id);
|
||||
ao.find('img').attr({'src': opts.commentBrightImage});
|
||||
if (node_id) {
|
||||
// if this was a "root" comment, remove the commenting box
|
||||
// (the user can get it back by reopening the comment popup)
|
||||
$('#ca' + node_id).slideUp();
|
||||
}
|
||||
},
|
||||
error: function(request, textStatus, error) {
|
||||
form.find('textarea,input').removeAttr('disabled');
|
||||
showError('Oops, there was a problem adding the comment.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively append comments to the main comment list and children
|
||||
* lists, creating the comment tree.
|
||||
*/
|
||||
function appendComments(comments, ul) {
|
||||
$.each(comments, function() {
|
||||
var div = createCommentDiv(this);
|
||||
ul.append($(document.createElement('li')).html(div));
|
||||
appendComments(this.children, div.find('ul.comment-children'));
|
||||
// To avoid stagnating data, don't store the comments children in data.
|
||||
this.children = null;
|
||||
div.data('comment', this);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* After adding a new comment, it must be inserted in the correct
|
||||
* location in the comment tree.
|
||||
*/
|
||||
function insertComment(comment) {
|
||||
var div = createCommentDiv(comment);
|
||||
|
||||
// To avoid stagnating data, don't store the comments children in data.
|
||||
comment.children = null;
|
||||
div.data('comment', comment);
|
||||
|
||||
var ul = $('#cl' + (comment.node || comment.parent));
|
||||
var siblings = getChildren(ul);
|
||||
|
||||
var li = $(document.createElement('li'));
|
||||
li.hide();
|
||||
|
||||
// Determine where in the parents children list to insert this comment.
|
||||
for(i=0; i < siblings.length; i++) {
|
||||
if (comp(comment, siblings[i]) <= 0) {
|
||||
$('#cd' + siblings[i].id)
|
||||
.parent()
|
||||
.before(li.html(div));
|
||||
li.slideDown('fast');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, this comment rates lower than all the others,
|
||||
// or it is the only comment in the list.
|
||||
ul.append(li.html(div));
|
||||
li.slideDown('fast');
|
||||
}
|
||||
|
||||
function acceptComment(id) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: opts.acceptCommentURL,
|
||||
data: {id: id},
|
||||
success: function(data, textStatus, request) {
|
||||
$('#cm' + id).fadeOut('fast');
|
||||
$('#cd' + id).removeClass('moderate');
|
||||
},
|
||||
error: function(request, textStatus, error) {
|
||||
showError('Oops, there was a problem accepting the comment.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteComment(id) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: opts.deleteCommentURL,
|
||||
data: {id: id},
|
||||
success: function(data, textStatus, request) {
|
||||
var div = $('#cd' + id);
|
||||
if (data == 'delete') {
|
||||
// Moderator mode: remove the comment and all children immediately
|
||||
div.slideUp('fast', function() {
|
||||
div.remove();
|
||||
});
|
||||
return;
|
||||
}
|
||||
// User mode: only mark the comment as deleted
|
||||
div
|
||||
.find('span.user-id:first')
|
||||
.text('[deleted]').end()
|
||||
.find('div.comment-text:first')
|
||||
.text('[deleted]').end()
|
||||
.find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id +
|
||||
', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id)
|
||||
.remove();
|
||||
var comment = div.data('comment');
|
||||
comment.username = '[deleted]';
|
||||
comment.text = '[deleted]';
|
||||
div.data('comment', comment);
|
||||
},
|
||||
error: function(request, textStatus, error) {
|
||||
showError('Oops, there was a problem deleting the comment.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showProposal(id) {
|
||||
$('#sp' + id).hide();
|
||||
$('#hp' + id).show();
|
||||
$('#pr' + id).slideDown('fast');
|
||||
}
|
||||
|
||||
function hideProposal(id) {
|
||||
$('#hp' + id).hide();
|
||||
$('#sp' + id).show();
|
||||
$('#pr' + id).slideUp('fast');
|
||||
}
|
||||
|
||||
function showProposeChange(id) {
|
||||
$('#pc' + id).hide();
|
||||
$('#hc' + id).show();
|
||||
var textarea = $('#pt' + id);
|
||||
textarea.val(textarea.data('source'));
|
||||
$.fn.autogrow.resize(textarea[0]);
|
||||
textarea.slideDown('fast');
|
||||
}
|
||||
|
||||
function hideProposeChange(id) {
|
||||
$('#hc' + id).hide();
|
||||
$('#pc' + id).show();
|
||||
var textarea = $('#pt' + id);
|
||||
textarea.val('').removeAttr('disabled');
|
||||
textarea.slideUp('fast');
|
||||
}
|
||||
|
||||
function toggleCommentMarkupBox(id) {
|
||||
$('#mb' + id).toggle();
|
||||
}
|
||||
|
||||
/** Handle when the user clicks on a sort by link. */
|
||||
function handleReSort(link) {
|
||||
var classes = link.attr('class').split(/\s+/);
|
||||
for (var i=0; i<classes.length; i++) {
|
||||
if (classes[i] != 'sort-option') {
|
||||
by = classes[i].substring(2);
|
||||
}
|
||||
}
|
||||
setComparator();
|
||||
// Save/update the sortBy cookie.
|
||||
var expiration = new Date();
|
||||
expiration.setDate(expiration.getDate() + 365);
|
||||
document.cookie= 'sortBy=' + escape(by) +
|
||||
';expires=' + expiration.toUTCString();
|
||||
$('ul.comment-ul').each(function(index, ul) {
|
||||
var comments = getChildren($(ul), true);
|
||||
comments = sortComments(comments);
|
||||
appendComments(comments, $(ul).empty());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to process a vote when a user clicks an arrow.
|
||||
*/
|
||||
function handleVote(link) {
|
||||
if (!opts.voting) {
|
||||
showError("You'll need to login to vote.");
|
||||
return;
|
||||
}
|
||||
|
||||
var id = link.attr('id');
|
||||
if (!id) {
|
||||
// Didn't click on one of the voting arrows.
|
||||
return;
|
||||
}
|
||||
// If it is an unvote, the new vote value is 0,
|
||||
// Otherwise it's 1 for an upvote, or -1 for a downvote.
|
||||
var value = 0;
|
||||
if (id.charAt(1) != 'u') {
|
||||
value = id.charAt(0) == 'u' ? 1 : -1;
|
||||
}
|
||||
// The data to be sent to the server.
|
||||
var d = {
|
||||
comment_id: id.substring(2),
|
||||
value: value
|
||||
};
|
||||
|
||||
// Swap the vote and unvote links.
|
||||
link.hide();
|
||||
$('#' + id.charAt(0) + (id.charAt(1) == 'u' ? 'v' : 'u') + d.comment_id)
|
||||
.show();
|
||||
|
||||
// The div the comment is displayed in.
|
||||
var div = $('div#cd' + d.comment_id);
|
||||
var data = div.data('comment');
|
||||
|
||||
// If this is not an unvote, and the other vote arrow has
|
||||
// already been pressed, unpress it.
|
||||
if ((d.value !== 0) && (data.vote === d.value * -1)) {
|
||||
$('#' + (d.value == 1 ? 'd' : 'u') + 'u' + d.comment_id).hide();
|
||||
$('#' + (d.value == 1 ? 'd' : 'u') + 'v' + d.comment_id).show();
|
||||
}
|
||||
|
||||
// Update the comments rating in the local data.
|
||||
data.rating += (data.vote === 0) ? d.value : (d.value - data.vote);
|
||||
data.vote = d.value;
|
||||
div.data('comment', data);
|
||||
|
||||
// Change the rating text.
|
||||
div.find('.rating:first')
|
||||
.text(data.rating + ' point' + (data.rating == 1 ? '' : 's'));
|
||||
|
||||
// Send the vote information to the server.
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: opts.processVoteURL,
|
||||
data: d,
|
||||
error: function(request, textStatus, error) {
|
||||
showError('Oops, there was a problem casting that vote.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a reply form used to reply to an existing comment.
|
||||
*/
|
||||
function openReply(id) {
|
||||
// Swap out the reply link for the hide link
|
||||
$('#rl' + id).hide();
|
||||
$('#cr' + id).show();
|
||||
|
||||
// Add the reply li to the children ul.
|
||||
var div = $(renderTemplate(replyTemplate, {id: id})).hide();
|
||||
$('#cl' + id)
|
||||
.prepend(div)
|
||||
// Setup the submit handler for the reply form.
|
||||
.find('#rf' + id)
|
||||
.submit(function(event) {
|
||||
event.preventDefault();
|
||||
addComment($('#rf' + id));
|
||||
closeReply(id);
|
||||
})
|
||||
.find('input[type=button]')
|
||||
.click(function() {
|
||||
closeReply(id);
|
||||
});
|
||||
div.slideDown('fast', function() {
|
||||
$('#rf' + id).find('textarea').focus();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the reply form opened with openReply.
|
||||
*/
|
||||
function closeReply(id) {
|
||||
// Remove the reply div from the DOM.
|
||||
$('#rd' + id).slideUp('fast', function() {
|
||||
$(this).remove();
|
||||
});
|
||||
|
||||
// Swap out the hide link for the reply link
|
||||
$('#cr' + id).hide();
|
||||
$('#rl' + id).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively sort a tree of comments using the comp comparator.
|
||||
*/
|
||||
function sortComments(comments) {
|
||||
comments.sort(comp);
|
||||
$.each(comments, function() {
|
||||
this.children = sortComments(this.children);
|
||||
});
|
||||
return comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the children comments from a ul. If recursive is true,
|
||||
* recursively include childrens' children.
|
||||
*/
|
||||
function getChildren(ul, recursive) {
|
||||
var children = [];
|
||||
ul.children().children("[id^='cd']")
|
||||
.each(function() {
|
||||
var comment = $(this).data('comment');
|
||||
if (recursive)
|
||||
comment.children = getChildren($(this).find('#cl' + comment.id), true);
|
||||
children.push(comment);
|
||||
});
|
||||
return children;
|
||||
}
|
||||
|
||||
/** Create a div to display a comment in. */
|
||||
function createCommentDiv(comment) {
|
||||
if (!comment.displayed && !opts.moderator) {
|
||||
return $('<div class="moderate">Thank you! Your comment will show up '
|
||||
+ 'once it is has been approved by a moderator.</div>');
|
||||
}
|
||||
// Prettify the comment rating.
|
||||
comment.pretty_rating = comment.rating + ' point' +
|
||||
(comment.rating == 1 ? '' : 's');
|
||||
// Make a class (for displaying not yet moderated comments differently)
|
||||
comment.css_class = comment.displayed ? '' : ' moderate';
|
||||
// Create a div for this comment.
|
||||
var context = $.extend({}, opts, comment);
|
||||
var div = $(renderTemplate(commentTemplate, context));
|
||||
|
||||
// If the user has voted on this comment, highlight the correct arrow.
|
||||
if (comment.vote) {
|
||||
var direction = (comment.vote == 1) ? 'u' : 'd';
|
||||
div.find('#' + direction + 'v' + comment.id).hide();
|
||||
div.find('#' + direction + 'u' + comment.id).show();
|
||||
}
|
||||
|
||||
if (opts.moderator || comment.text != '[deleted]') {
|
||||
div.find('a.reply').show();
|
||||
if (comment.proposal_diff)
|
||||
div.find('#sp' + comment.id).show();
|
||||
if (opts.moderator && !comment.displayed)
|
||||
div.find('#cm' + comment.id).show();
|
||||
if (opts.moderator || (opts.username == comment.username))
|
||||
div.find('#dc' + comment.id).show();
|
||||
}
|
||||
return div;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple template renderer. Placeholders such as <%id%> are replaced
|
||||
* by context['id'] with items being escaped. Placeholders such as <#id#>
|
||||
* are not escaped.
|
||||
*/
|
||||
function renderTemplate(template, context) {
|
||||
var esc = $(document.createElement('div'));
|
||||
|
||||
function handle(ph, escape) {
|
||||
var cur = context;
|
||||
$.each(ph.split('.'), function() {
|
||||
cur = cur[this];
|
||||
});
|
||||
return escape ? esc.text(cur || "").html() : cur;
|
||||
}
|
||||
|
||||
return template.replace(/<([%#])([\w\.]*)\1>/g, function() {
|
||||
return handle(arguments[2], arguments[1] == '%' ? true : false);
|
||||
});
|
||||
}
|
||||
|
||||
/** Flash an error message briefly. */
|
||||
function showError(message) {
|
||||
$(document.createElement('div')).attr({'class': 'popup-error'})
|
||||
.append($(document.createElement('div'))
|
||||
.attr({'class': 'error-message'}).text(message))
|
||||
.appendTo('body')
|
||||
.fadeIn("slow")
|
||||
.delay(2000)
|
||||
.fadeOut("slow");
|
||||
}
|
||||
|
||||
/** Add a link the user uses to open the comments popup. */
|
||||
$.fn.comment = function() {
|
||||
return this.each(function() {
|
||||
var id = $(this).attr('id').substring(1);
|
||||
var count = COMMENT_METADATA[id];
|
||||
var title = count + ' comment' + (count == 1 ? '' : 's');
|
||||
var image = count > 0 ? opts.commentBrightImage : opts.commentImage;
|
||||
var addcls = count == 0 ? ' nocomment' : '';
|
||||
$(this)
|
||||
.append(
|
||||
$(document.createElement('a')).attr({
|
||||
href: '#',
|
||||
'class': 'sphinx-comment-open' + addcls,
|
||||
id: 'ao' + id
|
||||
})
|
||||
.append($(document.createElement('img')).attr({
|
||||
src: image,
|
||||
alt: 'comment',
|
||||
title: title
|
||||
}))
|
||||
.click(function(event) {
|
||||
event.preventDefault();
|
||||
show($(this).attr('id').substring(2));
|
||||
})
|
||||
)
|
||||
.append(
|
||||
$(document.createElement('a')).attr({
|
||||
href: '#',
|
||||
'class': 'sphinx-comment-close hidden',
|
||||
id: 'ah' + id
|
||||
})
|
||||
.append($(document.createElement('img')).attr({
|
||||
src: opts.closeCommentImage,
|
||||
alt: 'close',
|
||||
title: 'close'
|
||||
}))
|
||||
.click(function(event) {
|
||||
event.preventDefault();
|
||||
hide($(this).attr('id').substring(2));
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
var opts = {
|
||||
processVoteURL: '/_process_vote',
|
||||
addCommentURL: '/_add_comment',
|
||||
getCommentsURL: '/_get_comments',
|
||||
acceptCommentURL: '/_accept_comment',
|
||||
deleteCommentURL: '/_delete_comment',
|
||||
commentImage: '/static/_static/comment.png',
|
||||
closeCommentImage: '/static/_static/comment-close.png',
|
||||
loadingImage: '/static/_static/ajax-loader.gif',
|
||||
commentBrightImage: '/static/_static/comment-bright.png',
|
||||
upArrow: '/static/_static/up.png',
|
||||
downArrow: '/static/_static/down.png',
|
||||
upArrowPressed: '/static/_static/up-pressed.png',
|
||||
downArrowPressed: '/static/_static/down-pressed.png',
|
||||
voting: false,
|
||||
moderator: false
|
||||
};
|
||||
|
||||
if (typeof COMMENT_OPTIONS != "undefined") {
|
||||
opts = jQuery.extend(opts, COMMENT_OPTIONS);
|
||||
}
|
||||
|
||||
var popupTemplate = '\
|
||||
<div class="sphinx-comments" id="sc<%id%>">\
|
||||
<p class="sort-options">\
|
||||
Sort by:\
|
||||
<a href="#" class="sort-option byrating">best rated</a>\
|
||||
<a href="#" class="sort-option byascage">newest</a>\
|
||||
<a href="#" class="sort-option byage">oldest</a>\
|
||||
</p>\
|
||||
<div class="comment-header">Comments</div>\
|
||||
<div class="comment-loading" id="cn<%id%>">\
|
||||
loading comments... <img src="<%loadingImage%>" alt="" /></div>\
|
||||
<ul id="cl<%id%>" class="comment-ul"></ul>\
|
||||
<div id="ca<%id%>">\
|
||||
<p class="add-a-comment">Add a comment\
|
||||
(<a href="#" class="comment-markup" id="ab<%id%>">markup</a>):</p>\
|
||||
<div class="comment-markup-box" id="mb<%id%>">\
|
||||
reStructured text markup: <i>*emph*</i>, <b>**strong**</b>, \
|
||||
<tt>``code``</tt>, \
|
||||
code blocks: <tt>::</tt> and an indented block after blank line</div>\
|
||||
<form method="post" id="cf<%id%>" class="comment-form" action="">\
|
||||
<textarea name="comment" cols="80"></textarea>\
|
||||
<p class="propose-button">\
|
||||
<a href="#" id="pc<%id%>" class="show-propose-change">\
|
||||
Propose a change ▹\
|
||||
</a>\
|
||||
<a href="#" id="hc<%id%>" class="hide-propose-change">\
|
||||
Propose a change ▿\
|
||||
</a>\
|
||||
</p>\
|
||||
<textarea name="proposal" id="pt<%id%>" cols="80"\
|
||||
spellcheck="false"></textarea>\
|
||||
<input type="submit" value="Add comment" />\
|
||||
<input type="hidden" name="node" value="<%id%>" />\
|
||||
<input type="hidden" name="parent" value="" />\
|
||||
</form>\
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
var commentTemplate = '\
|
||||
<div id="cd<%id%>" class="sphinx-comment<%css_class%>">\
|
||||
<div class="vote">\
|
||||
<div class="arrow">\
|
||||
<a href="#" id="uv<%id%>" class="vote" title="vote up">\
|
||||
<img src="<%upArrow%>" />\
|
||||
</a>\
|
||||
<a href="#" id="uu<%id%>" class="un vote" title="vote up">\
|
||||
<img src="<%upArrowPressed%>" />\
|
||||
</a>\
|
||||
</div>\
|
||||
<div class="arrow">\
|
||||
<a href="#" id="dv<%id%>" class="vote" title="vote down">\
|
||||
<img src="<%downArrow%>" id="da<%id%>" />\
|
||||
</a>\
|
||||
<a href="#" id="du<%id%>" class="un vote" title="vote down">\
|
||||
<img src="<%downArrowPressed%>" />\
|
||||
</a>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class="comment-content">\
|
||||
<p class="tagline comment">\
|
||||
<span class="user-id"><%username%></span>\
|
||||
<span class="rating"><%pretty_rating%></span>\
|
||||
<span class="delta"><%time.delta%></span>\
|
||||
</p>\
|
||||
<div class="comment-text comment"><#text#></div>\
|
||||
<p class="comment-opts comment">\
|
||||
<a href="#" class="reply hidden" id="rl<%id%>">reply ▹</a>\
|
||||
<a href="#" class="close-reply" id="cr<%id%>">reply ▿</a>\
|
||||
<a href="#" id="sp<%id%>" class="show-proposal">proposal ▹</a>\
|
||||
<a href="#" id="hp<%id%>" class="hide-proposal">proposal ▿</a>\
|
||||
<a href="#" id="dc<%id%>" class="delete-comment hidden">delete</a>\
|
||||
<span id="cm<%id%>" class="moderation hidden">\
|
||||
<a href="#" id="ac<%id%>" class="accept-comment">accept</a>\
|
||||
</span>\
|
||||
</p>\
|
||||
<pre class="proposal" id="pr<%id%>">\
|
||||
<#proposal_diff#>\
|
||||
</pre>\
|
||||
<ul class="comment-children" id="cl<%id%>"></ul>\
|
||||
</div>\
|
||||
<div class="clearleft"></div>\
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
var replyTemplate = '\
|
||||
<li>\
|
||||
<div class="reply-div" id="rd<%id%>">\
|
||||
<form id="rf<%id%>">\
|
||||
<textarea name="comment" cols="80"></textarea>\
|
||||
<input type="submit" value="Add reply" />\
|
||||
<input type="button" value="Cancel" />\
|
||||
<input type="hidden" name="parent" value="<%id%>" />\
|
||||
<input type="hidden" name="node" value="" />\
|
||||
</form>\
|
||||
</div>\
|
||||
</li>';
|
||||
|
||||
$(document).ready(function() {
|
||||
init();
|
||||
});
|
||||
})(jQuery);
|
||||
|
||||
$(document).ready(function() {
|
||||
// add comment anchors for all paragraphs that are commentable
|
||||
$('.sphinx-has-comment').comment();
|
||||
|
||||
// highlight search words in search results
|
||||
$("div.context").each(function() {
|
||||
var params = $.getQueryParameters();
|
||||
var terms = (params.q) ? params.q[0].split(/\s+/) : [];
|
||||
var result = $(this);
|
||||
$.each(terms, function() {
|
||||
result.highlightText(this.toLowerCase(), 'highlighted');
|
||||
});
|
||||
});
|
||||
|
||||
// directly open comment window if requested
|
||||
var anchor = document.location.hash;
|
||||
if (anchor.substring(0, 9) == '#comment-') {
|
||||
$('#ao' + anchor.substring(9)).click();
|
||||
document.location.hash = '#s' + anchor.substring(9);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,697 @@
|
|||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Attribute Management in Authentic2 — Authentic2 1.9.2 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '',
|
||||
VERSION: '1.9.2',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="top" title="Authentic2 1.9.2 documentation" href="index.html" />
|
||||
<link rel="prev" title="Configure Authentic2 as a CAS client" href="config_cas_idp.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="config_cas_idp.html" title="Configure Authentic2 as a CAS client"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
<div class="section" id="attribute-management-in-authentic2">
|
||||
<span id="attribute-management"></span><h1>Attribute Management in Authentic2<a class="headerlink" href="#attribute-management-in-authentic2" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="section" id="summary">
|
||||
<h2>Summary<a class="headerlink" href="#summary" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Attribute management currently allows to configure attribute policies
|
||||
associated with SAML2 service providers to define attributes that are
|
||||
pushed in SAML2 successful authentication response delivered by Authentic2.</p>
|
||||
<p>User attributes can be taken from LDAP directories, the user Django
|
||||
profile or taken from the user Django session if Authentic2 is also configured
|
||||
as a SAML2 service provider.</p>
|
||||
<p>Indeed, when Authentic2 acts also as a SAML2 service provider,
|
||||
attributes contained in the SAML2 assertion received from third IdP are put in
|
||||
the user session.</p>
|
||||
<p>Attributes can thus be proxyfied during SSO with Authentic2
|
||||
configured as a SAML2 proxy.</p>
|
||||
<p>The namespace of attributes received from another SAML2 IdP or pushed in the
|
||||
assertion given in to service providers can be configured per attribute or per
|
||||
service provider.</p>
|
||||
<p>By default, the namespace and format of attributes in assertion is conformant
|
||||
to the SAMLV2.0 X500/LDAP Attribute profile:</p>
|
||||
<div class="highlight-python"><pre><saml:Attribute
|
||||
xmlns:x500="urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500"
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
|
||||
Name="urn:oid:2.5.4.42" FriendlyName="givenName">
|
||||
<saml:AttributeValue xsi:type="xs:string"
|
||||
x500:Encoding="LDAP">Mikaël</saml:AttributeValue>
|
||||
</saml:Attribute></pre>
|
||||
</div>
|
||||
<p>But the <a class="reference external" href="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">http://schemas.xmlsoap.org/ws/2005/05/identity/claims</a> from the ISI
|
||||
profile can also be used, for instance:</p>
|
||||
<div class="highlight-python"><pre><saml:Attribute
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
|
||||
Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"
|
||||
FriendlyName="First Name">
|
||||
<saml:AttributeValue>Mikaël</saml:AttributeValue>
|
||||
</saml:Attribute></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="configuration">
|
||||
<h2>Configuration<a class="headerlink" href="#configuration" title="Permalink to this headline">¶</a></h2>
|
||||
<div class="section" id="configure-sources-of-attributes">
|
||||
<h3>Configure sources of attributes<a class="headerlink" href="#configure-sources-of-attributes" title="Permalink to this headline">¶</a></h3>
|
||||
<p>The source of attributes for authentic2 are of two kinds. The LDAP sources and
|
||||
the user django profile.</p>
|
||||
<div class="section" id="declare-the-django-profile-source">
|
||||
<h4>Declare the Django profile source<a class="headerlink" href="#declare-the-django-profile-source" title="Permalink to this headline">¶</a></h4>
|
||||
<p>Add an attribute source named USER_PROFILE with namespace ‘Default’.</p>
|
||||
<p>Then, it is necessary that users create their profile.</p>
|
||||
</div>
|
||||
<div class="section" id="add-an-ldap-source">
|
||||
<h4>Add an LDAP Source<a class="headerlink" href="#add-an-ldap-source" title="Permalink to this headline">¶</a></h4>
|
||||
<p>For LDAP sources, objects of type ‘LDAPSource’ must be created.</p>
|
||||
<p><strong>Even if the authentication is based on LDAP authentification, thus that a
|
||||
server is configured in settings.py, it is
|
||||
necessary to create a corresponding ‘LDAPSource’ to use it as a source of
|
||||
attribute.</strong></p>
|
||||
<ol class="arabic simple">
|
||||
<li>Go to http[s]://your.domain.com/admin/attribute_aggregator/ldapsource/add/</li>
|
||||
<li>Fill form fields</li>
|
||||
</ol>
|
||||
<p>Only the field Name, Server, User, Password, Base and Port are used for now.
|
||||
<strong>The namespace of LDAP source must be kept to ‘Default’, since the system
|
||||
namespace is based on LDAP.</strong></p>
|
||||
<img alt="_images/ldapsource.png" src="_images/ldapsource.png" style="width: 800px;" />
|
||||
<ol class="arabic simple" start="3">
|
||||
<li>Save</li>
|
||||
</ol>
|
||||
<img alt="_images/ldapsource_saved.png" src="_images/ldapsource_saved.png" style="width: 800px;" />
|
||||
</div>
|
||||
<div class="section" id="manage-user-distinguished-names-in-ldap-directories">
|
||||
<h4>Manage user distinguished names in LDAP directories<a class="headerlink" href="#manage-user-distinguished-names-in-ldap-directories" title="Permalink to this headline">¶</a></h4>
|
||||
<p>To find the user in a LDAP directory, authentic2 must know its distinguished
|
||||
name (DN). If this LDAP has been used when the user has authenticated,
|
||||
Authentic2 learn the user DN. Nothing has to be done from this point of view.</p>
|
||||
<p>However, if it is expected that user attributes be taken in a directory that
|
||||
is not used by the user for authentication, it is necessary to manually
|
||||
indicate to Authentic2 what is the user DN in the directory. For this, a
|
||||
user alias in source is created for the user:</p>
|
||||
<ol class="arabic simple">
|
||||
<li>Go to http[s]://your.domain.com/admin/attribute_aggregator/useraliasinsource/add/</li>
|
||||
<li>Fill form fields</li>
|
||||
</ol>
|
||||
<img alt="_images/alias_in_source.png" src="_images/alias_in_source.png" style="width: 800px;" />
|
||||
<ol class="arabic simple" start="3">
|
||||
<li>Save</li>
|
||||
</ol>
|
||||
<img alt="_images/alias_in_source_saved.png" src="_images/alias_in_source_saved.png" style="width: 800px;" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="configure-attributes-pushed-to-saml2-service-providers-in-sso-response">
|
||||
<h3>Configure attributes pushed to SAML2 service providers in SSO response<a class="headerlink" href="#configure-attributes-pushed-to-saml2-service-providers-in-sso-response" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Reminder:</p>
|
||||
<ul class="simple">
|
||||
<li>The default name format in SAML2 assertions is URI</li>
|
||||
<li>The default namespace called ‘Default’ is LDAP</li>
|
||||
</ul>
|
||||
<p>In summary:</p>
|
||||
<ol class="arabic simple">
|
||||
<li>Create attribute items indicating an attribute name, a source, the name format expected and the namespace expected for the attribute name and friendly name if any.</li>
|
||||
<li>Create a named list of attribute items.</li>
|
||||
<li>Create an attribute policy and associate the previous list or associate the previous list to a existing attribute policy.</li>
|
||||
<li>Associate the policy to a service provider.</li>
|
||||
</ol>
|
||||
<div class="section" id="create-attribute-items">
|
||||
<h4>Create attribute items<a class="headerlink" href="#create-attribute-items" title="Permalink to this headline">¶</a></h4>
|
||||
<ol class="arabic simple">
|
||||
<li>Go to http[s]://your.domain.com/admin/idp/attributeitem/add/</li>
|
||||
<li>Fill form fields</li>
|
||||
</ol>
|
||||
<img alt="_images/attribute_item.png" src="_images/attribute_item.png" style="width: 800px;" />
|
||||
<ol class="arabic simple" start="3">
|
||||
<li>Save</li>
|
||||
</ol>
|
||||
<img alt="_images/attribute_item_saved.png" src="_images/attribute_item_saved.png" style="width: 800px;" />
|
||||
</div>
|
||||
<div class="section" id="create-a-named-list-of-attribute-items">
|
||||
<h4>Create a named list of attribute items<a class="headerlink" href="#create-a-named-list-of-attribute-items" title="Permalink to this headline">¶</a></h4>
|
||||
<ol class="arabic simple">
|
||||
<li>Go to http[s]://your.domain.com/admin/idp/attributelist/add/</li>
|
||||
<li>Name the list and add items to list</li>
|
||||
</ol>
|
||||
<img alt="_images/attribute_list.png" src="_images/attribute_list.png" style="width: 800px;" />
|
||||
<ol class="arabic simple" start="3">
|
||||
<li>Save</li>
|
||||
</ol>
|
||||
<img alt="_images/attribute_list_saved.png" src="_images/attribute_list_saved.png" style="width: 800px;" />
|
||||
</div>
|
||||
<div class="section" id="create-or-modify-an-attribute-policy">
|
||||
<h4>Create or modify an attribute policy<a class="headerlink" href="#create-or-modify-an-attribute-policy" title="Permalink to this headline">¶</a></h4>
|
||||
<ol class="arabic simple">
|
||||
<li>Go to http[s]://your.domain.com/admin/idp/attributepolicy/add/</li>
|
||||
<li>Add list to the policy</li>
|
||||
</ol>
|
||||
<img alt="_images/policy_pull.png" src="_images/policy_pull.png" style="width: 800px;" />
|
||||
<ol class="arabic simple" start="3">
|
||||
<li>Save</li>
|
||||
</ol>
|
||||
<img alt="_images/policy_pull_saved.png" src="_images/policy_pull_saved.png" style="width: 800px;" />
|
||||
</div>
|
||||
<div class="section" id="associate-the-policy-to-a-service-provider">
|
||||
<h4>Associate the policy to a service provider<a class="headerlink" href="#associate-the-policy-to-a-service-provider" title="Permalink to this headline">¶</a></h4>
|
||||
<ol class="arabic simple">
|
||||
<li>Go to http[s]://your.domain.com/admin/saml/libertyprovider/1/</li>
|
||||
<li>Add policy to the service provider</li>
|
||||
</ol>
|
||||
<img alt="_images/sp_policy_pull.png" src="_images/sp_policy_pull.png" style="width: 800px;" />
|
||||
<ol class="arabic simple" start="3">
|
||||
<li>Save</li>
|
||||
</ol>
|
||||
<img alt="_images/sp_policy_pull_saved.png" src="_images/sp_policy_pull_saved.png" style="width: 800px;" />
|
||||
<ol class="arabic simple" start="4">
|
||||
<li>The display name of the policy has changed</li>
|
||||
</ol>
|
||||
<img alt="_images/policy_pull_renamed.png" src="_images/policy_pull_renamed.png" style="width: 800px;" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="handle-attributes-provided-by-other-identity-providers-proxy-attributes">
|
||||
<h3>Handle attributes provided by other Identity providers, proxy attributes<a class="headerlink" href="#handle-attributes-provided-by-other-identity-providers-proxy-attributes" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Link to configure first Authentic as a sp to have attributes in session</p>
|
||||
<p>Add a source if mapping set to true</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="modifying-supported-namespaces-and-attribute-name-mappings">
|
||||
<h2>Modifying supported namespaces and attribute name mappings<a class="headerlink" href="#modifying-supported-namespaces-and-attribute-name-mappings" title="Permalink to this headline">¶</a></h2>
|
||||
<p>TBD</p>
|
||||
</div>
|
||||
<div class="section" id="explanation-draft">
|
||||
<h2>Explanation (Draft)<a class="headerlink" href="#explanation-draft" title="Permalink to this headline">¶</a></h2>
|
||||
<div class="section" id="attribute-aggegrator-module">
|
||||
<h3>Attribute aggegrator module<a class="headerlink" href="#attribute-aggegrator-module" title="Permalink to this headline">¶</a></h3>
|
||||
<p>The core attribute management is based on the attribute aggregator module.</p>
|
||||
<div class="section" id="intro">
|
||||
<h4>Intro<a class="headerlink" href="#intro" title="Permalink to this headline">¶</a></h4>
|
||||
<p>Attribute aggregator provides a main Model class called UserAttributeProfile,
|
||||
functions to load attributes and extract attributes.</p>
|
||||
<p>The mapping between attribute namespaces is built-in and depends on a unique
|
||||
file (mapping.py).</p>
|
||||
<p>A main schema is defined and is based on LDAP/X500 for naming. The support
|
||||
of <a class="reference external" href="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">http://schemas.xmlsoap.org/ws/2005/05/identity/claims</a> is partly complete.</p>
|
||||
<p>Source of attributes are connected with attribute loading functions using
|
||||
signals.</p>
|
||||
</div>
|
||||
<div class="section" id="faq">
|
||||
<h4>FAQ<a class="headerlink" href="#faq" title="Permalink to this headline">¶</a></h4>
|
||||
<p>Why not use the Django User profile?</p>
|
||||
<p>The django user profile needs to define attributes as class attributes and
|
||||
then support form filling or mapping with LDAP.</p>
|
||||
<p>That is useful and may be used, especially because the profile can be used as
|
||||
a source of attribute to load in the attribute_aggregator profile.</p>
|
||||
<p>The attribute_aggregator profile allow to load multivalued attributes from any
|
||||
source supported (LDAP, Django profile and attributes in Django session for
|
||||
now) from any namespace defined in mapping.py (LDAP/X500 and claims for now).</p>
|
||||
<p>The profile can be loaded giving a source or a list of attribute, or can be
|
||||
from any known source, or with a dictionnary.</p>
|
||||
<p>Attributes can be extracted with many functions in any namespace supported.</p>
|
||||
</div>
|
||||
<div class="section" id="quick-explanation">
|
||||
<h4>Quick explanation<a class="headerlink" href="#quick-explanation" title="Permalink to this headline">¶</a></h4>
|
||||
<p>The schema is defined in mapping.py and is made of definitions like this:</p>
|
||||
<div class="highlight-python"><pre>"sn": {
|
||||
"oid": "2.5.4.4",
|
||||
"display_name": _("sn surname"),
|
||||
"alias": ['surname'],
|
||||
"profile_field_name": 'last_name',
|
||||
"type": "http://www.w3.org/2001/XMLSchema#string",
|
||||
"namespaces": {
|
||||
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims": {
|
||||
"identifiers":
|
||||
[
|
||||
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname",
|
||||
],
|
||||
"friendly_names":
|
||||
[
|
||||
"Last Name",
|
||||
],
|
||||
}
|
||||
}
|
||||
},</pre>
|
||||
</div>
|
||||
<p>The profile store all the data in a text field taht contains a cPickle list of
|
||||
instances of the class AttributeData.</p>
|
||||
<p>The profile is attached to a user and then can be created or loaded with:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre><span class="n">profile</span> <span class="o">=</span> <span class="n">load_or_create_user_profile</span><span class="p">(</span><span class="n">user</span><span class="o">=</span><span class="n">user</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>User may be None to create a temporary profile for an anonymous user. But
|
||||
that need a DB cleaning function not implemented.</p>
|
||||
</div>
|
||||
<div class="section" id="the-model-userattributeprofile">
|
||||
<h4>The model <em>UserAttributeProfile</em><a class="headerlink" href="#the-model-userattributeprofile" title="Permalink to this headline">¶</a></h4>
|
||||
<p>The model ‘UserAttributeProfile’ can be attached to a user and then persist
|
||||
(as a Model).</p>
|
||||
<p>When the profile is loaded, all data stored are removed expect if the
|
||||
the data has an expiration date later.</p>
|
||||
<p>The profile provide several methods to store and extract attributes.</p>
|
||||
<p>All the methods to add attributes are based on a main one accepting a
|
||||
dictionnary of attribute is parameters ‘load_by_dic()’. The other methods
|
||||
(‘load_listed_attributes()’, ‘load_greedy()’) send a signal with a list of
|
||||
attributes (listed_attributes_call) or not (any_attributes_call) to grab a
|
||||
dictionnary. The list is given with the definition name, oid or friendly name
|
||||
of the attribute in the system namespace.</p>
|
||||
<p>Into the dictionnary, attributes are given with their name, oid or friendly
|
||||
name in the default namespace or with their name in a namepsace. An expiration
|
||||
date can also be given (ISO8601 format), if none, attribute will be deleted at
|
||||
next profile loading. The dictionnary format is as follows:</p>
|
||||
<div class="highlight-python"><pre>attributes = dict()
|
||||
data_from_source = list()
|
||||
a1 = dict()
|
||||
a1['oid'] = definition_name
|
||||
Or
|
||||
a1['definition'] = definition_name
|
||||
definition may be the definition name like 'gn'
|
||||
or an alias like 'givenName'
|
||||
Or
|
||||
a1['name'] = attribute_name_in_ns
|
||||
a1['namespace'] = ns_name
|
||||
a1['expiration_date'] = date
|
||||
a1['values'] = list_of_values
|
||||
data_from_source.append(a1)
|
||||
...
|
||||
data_from_source.append(a2)
|
||||
attributes[source_name] = data_from_source</pre>
|
||||
</div>
|
||||
<p>Getters are defined to extract data from a profile. Only AttributeData
|
||||
instances are extracted that assume that any attribute namespace can be used.</p>
|
||||
<ul class="simple">
|
||||
<li>get_data_of_definition(definition)</li>
|
||||
</ul>
|
||||
<p>Return a list of AttributeData instances corresponding to the definition
|
||||
given.</p>
|
||||
<ul class="simple">
|
||||
<li>get_freshest_data_of_definition(definition)</li>
|
||||
</ul>
|
||||
<p>Return the freshest AttributeData instance. If multiple with no or same exp
|
||||
date, random. Should use the creation date soon.</p>
|
||||
<ul class="simple">
|
||||
<li>get_data_of_source</li>
|
||||
</ul>
|
||||
<p>Return a list of AttributeData instances corresponding to the source given.</p>
|
||||
<ul class="simple">
|
||||
<li>get_data_of_source_by_name</li>
|
||||
</ul>
|
||||
<p>Idem but source name is given, not a Source instance.</p>
|
||||
<ul class="simple">
|
||||
<li>get_data_of_definition_and_source</li>
|
||||
</ul>
|
||||
<p>Return a list of AttributeData instances corresponding to the definition and
|
||||
source given.</p>
|
||||
<ul class="simple">
|
||||
<li>get_data_of_definition_and_source_by_name</li>
|
||||
</ul>
|
||||
<p>Idem but source name is given, not a Source instance.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="saml2-attribute-representation-in-assertions">
|
||||
<h3>SAML2 attribute representation in assertions<a class="headerlink" href="#saml2-attribute-representation-in-assertions" title="Permalink to this headline">¶</a></h3>
|
||||
<p>SAML2 attribute profile (saml-profiles-2.0-os - Section 8) defines two kind of
|
||||
attribute element syntax in the attribute statement of assertions, also
|
||||
called <em>name format</em>:</p>
|
||||
<ul>
|
||||
<li><p class="first">BASIC:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre><span class="n">NameFormat</span><span class="o">=</span><span class="s">"urn:oasis:names:tc:SAML:2.0:attrname-format:basic"</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</li>
|
||||
<li><p class="first">URI:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre><span class="n">NameFormat</span><span class="o">=</span><span class="s">"urn:oasis:names:tc:SAML:2.0:attrname-format:uri"</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<p><em>URI should be used when attributes have “universally” known unique names
|
||||
like OID.</em></p>
|
||||
<p>Example:</p>
|
||||
<div class="highlight-python"><pre><saml:Attribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
|
||||
Name="FirstName">
|
||||
<saml:AttributeValue xsi:type="xs:string">By-Tor</saml:AttributeValue>
|
||||
</saml:Attribute>
|
||||
|
||||
<saml:Attribute
|
||||
xmlns:x500="urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500"
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
|
||||
Name="urn:oid:2.5.4.42" FriendlyName="givenName">
|
||||
<saml:AttributeValue xsi:type="xs:string"
|
||||
x500:Encoding="LDAP">Steven</saml:AttributeValue>
|
||||
</saml:Attribute></pre>
|
||||
</div>
|
||||
<div class="section" id="basic">
|
||||
<h4>BASIC<a class="headerlink" href="#basic" title="Permalink to this headline">¶</a></h4>
|
||||
<p>Two <Attribute> elements refer to the same SAML attribute if and only if the
|
||||
values of their Name XML attributes are equal in the sense of Section 3.3.6 of
|
||||
[Schema2].</p>
|
||||
<p>No additional XML attributes are defined for use with the <Attribute> element.</p>
|
||||
<p>The schema type of the contents of the <AttributeValue> element MUST be drawn
|
||||
from one of the types defined in Section 3.3 of [Schema2]. The xsi:type
|
||||
attribute MUST be present and be given the appropriate value.</p>
|
||||
</div>
|
||||
<div class="section" id="x-500-ldap-attribute-profile-uri">
|
||||
<h4>X.500/LDAP Attribute Profile (URI)<a class="headerlink" href="#x-500-ldap-attribute-profile-uri" title="Permalink to this headline">¶</a></h4>
|
||||
<p><strong>Extracted from the SAML2 core specifications</strong></p>
|
||||
<p>Two <Attribute> elements refer to the same SAML attribute if and only if their
|
||||
Name XML attribute values are equal in the sense of [RFC3061]. The
|
||||
FriendlyName attribute plays no role in the comparison.</p>
|
||||
<p>Directory attribute type definitions for use in native X.500 directories
|
||||
specify the syntax of the attribute using ASN.1 [ASN.1]. For use in LDAP,
|
||||
directory attribute definitions additionally include an LDAP syntax which
|
||||
specifies how attribute or assertion values conforming to the syntax are to be
|
||||
represented when transferred in the LDAP protocol (known as an LDAP-specific
|
||||
encoding). The LDAP-specific encoding commonly produces Unicode characters in
|
||||
UTF-8 form. This SAML attribute profile specifies the form of SAML attribute
|
||||
values only for those directory attributes which have LDAP syntaxes. Future
|
||||
extensions to this profile may define attribute value formats for directory
|
||||
attributes whose syntaxes specify other encodings.</p>
|
||||
<p>To represent the encoding rules in use for a particular attribute value, the
|
||||
<AttributeValue> element MUST contain an XML attribute named Encoding defined
|
||||
in the XML namespace <a class="reference external" href="urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500">urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500</a>.</p>
|
||||
<p>For any directory attribute with a syntax whose LDAP-specific encoding
|
||||
exclusively produces UTF-8 character strings as values, the SAML attribute
|
||||
value is encoded as simply the UTF-8 string itself, as the content of the
|
||||
<AttributeValue> element, with no additional whitespace.
|
||||
In such cases, the xsi:type XML attribute MUST be set to xs:string.
|
||||
The profile-specific Encoding XML attribute is provided, with a value of LDAP.</p>
|
||||
<p>The AttributeData instances have a field expiration_data. It the profile
|
||||
exists, obsolete data are removed at loading.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="when-authentic-2-deals-with-attributes-and-needs-mapping">
|
||||
<h3>When authentic 2 deals with attributes and needs mapping?<a class="headerlink" href="#when-authentic-2-deals-with-attributes-and-needs-mapping" title="Permalink to this headline">¶</a></h3>
|
||||
<p>Authentic2 behaves as an attribute provider:
|
||||
* At the SSO login
|
||||
* When an attribute request is received</p>
|
||||
<p>Authentic requests (e.g. by soap) are not yet supported.</p>
|
||||
<div class="section" id="when-authentic2-behaves-as-an-attribute-provider-at-sso-login">
|
||||
<h4>When Authentic2 behaves as an attribute provider at SSO login<a class="headerlink" href="#when-authentic2-behaves-as-an-attribute-provider-at-sso-login" title="Permalink to this headline">¶</a></h4>
|
||||
<p>At a SSO request, just before responding to the service provider, the saml2
|
||||
idp module sends the signal ‘add_attributes_to_response’ giving the SP entity
|
||||
ID.</p>
|
||||
<p>The signal is connected to the function ‘provide_attributes_at_sso()’ in
|
||||
charge of providing the attributes at the SSO for this SP.</p>
|
||||
<p><strong>Attributes sources are of two kinds. The first ones are the sources that can
|
||||
be requested by the IdP with a syncrhonous binding without user intercations.
|
||||
These sources are called pull sources. They are for now limited to LDAP
|
||||
sources. The other ones are sources are asyncrhonous bindings, usually
|
||||
requiring user interactions. These sources are called push sources. They are
|
||||
now limited to the attributes provided at SSO requests when the IdP acts as a
|
||||
SAML2 SP. There attributes are put/found in the Django session.</strong></p>
|
||||
<p>Each source in the system is declared with an instance of the AttributeSource
|
||||
model. We’ll see later that to forward attributes of push sources it is not
|
||||
necessary that a source is declared in some circumstances.</p>
|
||||
<p>To manage these sources an attribute policy is attached to services providers.
|
||||
Then the service provider model must be extended with a attribute
|
||||
attributes_at_sso_policy. The service provider must send the signal
|
||||
‘add_attributes_to_response’.</p>
|
||||
<p>The implementation is actually done for SAML2 providers.</p>
|
||||
<p><strong>In such a policy attributes from pull and push sources are treated
|
||||
differently.</strong></p>
|
||||
<p><strong>For pull sources, a list of attributes is indicated. Either an attribute is
|
||||
searched in all the pull sources and whatever attribute value found is
|
||||
returned. Or each attribute is indicated with a source. With each attribute is
|
||||
indicated the output format and namespace.</strong></p>
|
||||
<p><strong>The policy may also indicate that all the attributes in the Django session
|
||||
must be forwarded. Then, no AttributeSource instance is required. All the
|
||||
attributes are then forwarded without treating input namespace considerations.
|
||||
When an AttributeSource instance is found, the input namespace of this source
|
||||
is considered. An option can then be set to tell that the output format and
|
||||
namespace must be taken. A list of attribute can also be given.
|
||||
This list can be use to filter attributes to forward without or without taking
|
||||
care of the source. The output namespace and format can also be trated per
|
||||
attribute.</strong></p>
|
||||
<p>If the namespace is default, the attribute names will be taken from the
|
||||
system namespace. In BASIC the name will be the definition name. In URI, the
|
||||
Name will be the OID in urn format and the friendly name will be the
|
||||
definition name. If a namespace is given, the first identifier of this
|
||||
attribute is taken as Name in BASIC. In URI, the same and the first friendly
|
||||
name is taken.</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">LibertyServiceProvider</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
|
||||
<span class="o">...</span>
|
||||
<span class="n">attribute_policy</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="n">AttributePolicy</span><span class="p">,</span>
|
||||
<span class="n">verbose_name</span><span class="o">=</span><span class="n">_</span><span class="p">(</span><span class="s">"Attribute policy"</span><span class="p">),</span> <span class="n">null</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">blank</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">AttributePolicy</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
|
||||
<span class="c"># List of attributes to provide from pull sources at SSO Login.</span>
|
||||
<span class="c"># If an attribute is indicate without a source, from any source.</span>
|
||||
<span class="c"># The output format and namespace is given by each attribute.</span>
|
||||
<span class="n">attribute_list_for_sso_from_pull_sources</span> <span class="o">=</span> \
|
||||
<span class="n">models</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="n">LibertyAttributeMap</span><span class="p">,</span>
|
||||
<span class="n">related_name</span> <span class="o">=</span> <span class="s">"attributes of pull sources"</span><span class="p">,</span>
|
||||
<span class="n">blank</span> <span class="o">=</span> <span class="bp">True</span><span class="p">,</span> <span class="n">null</span> <span class="o">=</span> <span class="bp">True</span><span class="p">)</span>
|
||||
|
||||
<span class="c"># Set to true for proxying attributes from pull sources at SSO Login.</span>
|
||||
<span class="c"># Attributes are in session.</span>
|
||||
<span class="c"># All attributes are forwarded as is except if the parameter</span>
|
||||
<span class="c"># 'attribute_list_for_sso_from_push_sources' is initialized</span>
|
||||
<span class="n">forward_attributes_from_pull_sources</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">BooleanField</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
|
||||
|
||||
<span class="c"># Map attributes in session</span>
|
||||
<span class="c"># forward_attributes_in_session must be true</span>
|
||||
<span class="c"># At False, all attributes are forwarded as is</span>
|
||||
<span class="c"># At true, look for the namespace of the source for input, If not found,</span>
|
||||
<span class="c"># system namespace. Look for the options attribute_name_format and</span>
|
||||
<span class="c"># output_namespace of the attribute policy for output.</span>
|
||||
<span class="n">map_attributes_from_pull_sources</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">BooleanField</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
|
||||
|
||||
<span class="c"># ATTRIBUTE_VALUE_FORMATS[0] =></span>
|
||||
<span class="c"># (lasso.SAML2_ATTRIBUTE_NAME_FORMAT_BASIC, 'SAMLv2 BASIC')</span>
|
||||
<span class="n">output_name_format</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span> <span class="o">=</span> <span class="mi">100</span><span class="p">,</span>
|
||||
<span class="n">choices</span> <span class="o">=</span> <span class="n">ATTRIBUTE_VALUE_FORMATS</span><span class="p">,</span>
|
||||
<span class="n">default</span> <span class="o">=</span> <span class="n">ATTRIBUTE_VALUE_FORMATS</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
|
||||
|
||||
<span class="c">#ATTRIBUTES_NS[0] => ('Default', 'Default')</span>
|
||||
<span class="n">output_namespace</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span> <span class="o">=</span> <span class="mi">100</span><span class="p">,</span>
|
||||
<span class="n">choices</span> <span class="o">=</span> <span class="n">ATTRIBUTES_NS</span><span class="p">,</span> <span class="n">default</span> <span class="o">=</span> <span class="n">ATTRIBUTES_NS</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
|
||||
|
||||
<span class="c"># Filter attributes pushed from source.</span>
|
||||
<span class="n">source_filter_for_sso_from_push_sources</span> <span class="o">=</span> \
|
||||
<span class="n">models</span><span class="o">.</span><span class="n">ManyToManyField</span><span class="p">(</span><span class="n">AttributeSource</span><span class="p">,</span>
|
||||
<span class="n">related_name</span> <span class="o">=</span> <span class="s">"attributes of pull sources"</span><span class="p">,</span>
|
||||
<span class="n">blank</span> <span class="o">=</span> <span class="bp">True</span><span class="p">,</span> <span class="n">null</span> <span class="o">=</span> <span class="bp">True</span><span class="p">)</span>
|
||||
|
||||
<span class="c"># List of attributes to filter from pull sources at SSO Login.</span>
|
||||
<span class="n">attribute_filter_for_sso_from_push_sources</span> <span class="o">=</span> \
|
||||
<span class="n">models</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="n">LibertyAttributeMap</span><span class="p">,</span>
|
||||
<span class="n">related_name</span> <span class="o">=</span> <span class="s">"attributes of pull sources"</span><span class="p">,</span>
|
||||
<span class="n">blank</span> <span class="o">=</span> <span class="bp">True</span><span class="p">,</span> <span class="n">null</span> <span class="o">=</span> <span class="bp">True</span><span class="p">)</span>
|
||||
|
||||
<span class="c"># The sources of attributes of the previous list are considered.</span>
|
||||
<span class="c"># May be used conjointly with 'source_filter_for_sso_from_push_sources'</span>
|
||||
<span class="n">filter_source_of_filtered_attributes</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">BooleanField</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
|
||||
|
||||
<span class="c"># To map the attributes of forwarded attributes with the defaut output</span>
|
||||
<span class="c"># format and namespace, use 'map_attributes_from_pull_sources'</span>
|
||||
<span class="c"># Use the following option to use the output format and namespace</span>
|
||||
<span class="c"># indicated for each attribute.</span>
|
||||
<span class="n">map_attributes_of_filtered_attributes</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">BooleanField</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
|
||||
|
||||
|
||||
<span class="c"># Set to true to take in account missing required attributes</span>
|
||||
<span class="n">send_error_and_no_attrs_if_missing_required_attrs</span> <span class="o">=</span> \
|
||||
<span class="n">models</span><span class="o">.</span><span class="n">BooleanField</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
|
||||
<span class="n">verbose_name</span> <span class="o">=</span> <span class="n">_</span><span class="p">(</span><span class="s">'attribute options policy'</span><span class="p">)</span>
|
||||
<span class="n">verbose_name_plural</span> <span class="o">=</span> <span class="n">_</span><span class="p">(</span><span class="s">'attribute options policies'</span><span class="p">)</span>
|
||||
|
||||
|
||||
<span class="k">class</span> <span class="nc">AttributeList</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
|
||||
<span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span> <span class="o">=</span> <span class="mi">40</span><span class="p">,</span> <span class="n">unique</span> <span class="o">=</span> <span class="bp">True</span><span class="p">)</span>
|
||||
<span class="n">attributes</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">ManyToManyField</span><span class="p">(</span><span class="n">AttributeItem</span><span class="p">,</span>
|
||||
<span class="n">related_name</span> <span class="o">=</span> <span class="s">"attributes of the list"</span><span class="p">,</span>
|
||||
<span class="n">blank</span> <span class="o">=</span> <span class="bp">True</span><span class="p">,</span> <span class="n">null</span> <span class="o">=</span> <span class="bp">True</span><span class="p">)</span>
|
||||
|
||||
|
||||
<span class="k">class</span> <span class="nc">AttributeItem</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
|
||||
<span class="n">attribute_name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span> <span class="o">=</span> <span class="mi">100</span><span class="p">,</span> <span class="n">choices</span> <span class="o">=</span> <span class="n">ATTRIBUTES</span><span class="p">,</span>
|
||||
<span class="n">default</span> <span class="o">=</span> <span class="n">ATTRIBUTES</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
|
||||
<span class="c"># ATTRIBUTE_VALUE_FORMATS[0] =></span>
|
||||
<span class="c"># (lasso.SAML2_ATTRIBUTE_NAME_FORMAT_BASIC, 'SAMLv2 BASIC')</span>
|
||||
<span class="n">output_attribute_name_format</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span> <span class="o">=</span> <span class="mi">100</span><span class="p">,</span>
|
||||
<span class="n">choices</span> <span class="o">=</span> <span class="n">ATTRIBUTE_VALUE_FORMATS</span><span class="p">,</span>
|
||||
<span class="n">default</span> <span class="o">=</span> <span class="n">ATTRIBUTE_VALUE_FORMATS</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
|
||||
<span class="c">#ATTRIBUTES_NS[0] => ('Default', 'Default')</span>
|
||||
<span class="n">output_namespace</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span> <span class="o">=</span> <span class="mi">100</span><span class="p">,</span>
|
||||
<span class="n">choices</span> <span class="o">=</span> <span class="n">ATTRIBUTES_NS</span><span class="p">,</span> <span class="n">default</span> <span class="o">=</span> <span class="n">ATTRIBUTES_NS</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
|
||||
<span class="n">required</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">BooleanField</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
|
||||
<span class="n">source</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">ForeignKey</span><span class="p">(</span><span class="n">AttributeSource</span><span class="p">,</span> <span class="n">blank</span> <span class="o">=</span> <span class="bp">True</span><span class="p">,</span> <span class="n">null</span> <span class="o">=</span> <span class="bp">True</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>A list of attributes can also be taken from the service provider metadata and
|
||||
added to ‘attribute_list_for_sso_from_pull_sources’. The namespace may be
|
||||
extracted from the metadata. This namespace is then used to look for the
|
||||
corresponding definition and then to provide the attribute in the right
|
||||
namespace. Read attributes from metadata is not yet supported.</p>
|
||||
<p>For the attributes of pull sources, once the list of attributes is defined,
|
||||
They are loaded in the user profile.</p>
|
||||
<p>As explained before the attribute_aggregator loading function send signals to
|
||||
grab dictionnary of attributes. Up to know, only the ldap loading function are
|
||||
connected to these signals. The namespace of LDAP sources is assumed to be
|
||||
the same as the system namespace. There is here then no mapping needed. Other
|
||||
kind of sources than LDAP can be defined in attribute aggregator.</p>
|
||||
<p>To grab attributes from a LDAP the user dn in the LDAP or at least a local
|
||||
identifier in the LDAP is required. For this purpose, each user has alias
|
||||
associated with LDAP source. These aliases must their DN in the LDAP. When
|
||||
the authentication LDAP backend will be taken in account, the dn will be taken
|
||||
direclty from the user Model instance.</p>
|
||||
<p>Each LDAP sources are declared with the binding parameters. The LDAP namespace
|
||||
is always ‘Default’.</p>
|
||||
<p>If an attribute to load is not found and is required the answer should report
|
||||
an error (Not yet implemented).</p>
|
||||
<p>Attributes in response can also be provided with other means than from an LDAP
|
||||
source. Attributes can be put in the user Django session and then loaded in
|
||||
the profile. An option of the service provier indicate if attributes in the
|
||||
session must be provided to the service provider.</p>
|
||||
<p>To have the attribute loaded from the session, they must be provided in the
|
||||
session as follows:
|
||||
request.session[‘attributes’][source_name] = list()</p>
|
||||
<p>The source_name must be the name of an existing instance of an
|
||||
‘AttributeSource’. Such an instance contains a field namespace indicating the
|
||||
namespace of attributes.</p>
|
||||
<p>This is currently implemented only for the SAML2 service provider module of
|
||||
authentic2. Authsaml2, the SP module, parse the assertion and put the
|
||||
attributes in the session.</p>
|
||||
<div class="highlight-python"><pre>if not 'multisource_attributes' in request.session:
|
||||
request.session['attributes'] = dict{}
|
||||
request.session['multisource_attributes'] \
|
||||
[login.assertion.issuer.content] = attributes</pre>
|
||||
</div>
|
||||
<p>Then, Authentic2 can be used as a SAML2 proxy forwarding attributes in
|
||||
assertion, eventually doing a namespace mapping. For this, the option
|
||||
forward attributes in sesion must be set (by default False).</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h3><a href="index.html">Table Of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Attribute Management in Authentic2</a><ul>
|
||||
<li><a class="reference internal" href="#summary">Summary</a></li>
|
||||
<li><a class="reference internal" href="#configuration">Configuration</a><ul>
|
||||
<li><a class="reference internal" href="#configure-sources-of-attributes">Configure sources of attributes</a><ul>
|
||||
<li><a class="reference internal" href="#declare-the-django-profile-source">Declare the Django profile source</a></li>
|
||||
<li><a class="reference internal" href="#add-an-ldap-source">Add an LDAP Source</a></li>
|
||||
<li><a class="reference internal" href="#manage-user-distinguished-names-in-ldap-directories">Manage user distinguished names in LDAP directories</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#configure-attributes-pushed-to-saml2-service-providers-in-sso-response">Configure attributes pushed to SAML2 service providers in SSO response</a><ul>
|
||||
<li><a class="reference internal" href="#create-attribute-items">Create attribute items</a></li>
|
||||
<li><a class="reference internal" href="#create-a-named-list-of-attribute-items">Create a named list of attribute items</a></li>
|
||||
<li><a class="reference internal" href="#create-or-modify-an-attribute-policy">Create or modify an attribute policy</a></li>
|
||||
<li><a class="reference internal" href="#associate-the-policy-to-a-service-provider">Associate the policy to a service provider</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#handle-attributes-provided-by-other-identity-providers-proxy-attributes">Handle attributes provided by other Identity providers, proxy attributes</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#modifying-supported-namespaces-and-attribute-name-mappings">Modifying supported namespaces and attribute name mappings</a></li>
|
||||
<li><a class="reference internal" href="#explanation-draft">Explanation (Draft)</a><ul>
|
||||
<li><a class="reference internal" href="#attribute-aggegrator-module">Attribute aggegrator module</a><ul>
|
||||
<li><a class="reference internal" href="#intro">Intro</a></li>
|
||||
<li><a class="reference internal" href="#faq">FAQ</a></li>
|
||||
<li><a class="reference internal" href="#quick-explanation">Quick explanation</a></li>
|
||||
<li><a class="reference internal" href="#the-model-userattributeprofile">The model <em>UserAttributeProfile</em></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#saml2-attribute-representation-in-assertions">SAML2 attribute representation in assertions</a><ul>
|
||||
<li><a class="reference internal" href="#basic">BASIC</a></li>
|
||||
<li><a class="reference internal" href="#x-500-ldap-attribute-profile-uri">X.500/LDAP Attribute Profile (URI)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#when-authentic-2-deals-with-attributes-and-needs-mapping">When authentic 2 deals with attributes and needs mapping?</a><ul>
|
||||
<li><a class="reference internal" href="#when-authentic2-behaves-as-an-attribute-provider-at-sso-login">When Authentic2 behaves as an attribute provider at SSO login</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="config_cas_idp.html"
|
||||
title="previous chapter">Configure Authentic2 as a CAS client</a></p>
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/attribute_management.txt"
|
||||
rel="nofollow">Show Source</a></li>
|
||||
</ul>
|
||||
<div id="searchbox" style="display: none">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<input type="text" name="q" />
|
||||
<input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
<p class="searchtip" style="font-size: 90%">
|
||||
Enter search terms or a module, class or function name.
|
||||
</p>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="config_cas_idp.html" title="Configure Authentic2 as a CAS client"
|
||||
>previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2011, Mikaël Ates.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,178 @@
|
|||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Authentication with an existing LDAP directory — Authentic2 1.9.2 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '',
|
||||
VERSION: '1.9.2',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="top" title="Authentic2 1.9.2 documentation" href="index.html" />
|
||||
<link rel="next" title="Authentication on Authentic2 with PAM" href="auth_pam.html" />
|
||||
<link rel="prev" title="Installation" href="installation.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="auth_pam.html" title="Authentication on Authentic2 with PAM"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="installation.html" title="Installation"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
<div class="section" id="authentication-with-an-existing-ldap-directory">
|
||||
<span id="auth-ldap"></span><h1>Authentication with an existing LDAP directory<a class="headerlink" href="#authentication-with-an-existing-ldap-directory" title="Permalink to this headline">¶</a></h1>
|
||||
<p>Authentic use the module django_auth_ldap to synchronize the Django user tables
|
||||
with an LDAP. For complex use case, we will refer you to the django_auth_ldap
|
||||
documentation, see <a class="reference external" href="http://packages.python.org/django-auth-ldap/">http://packages.python.org/django-auth-ldap/</a>.</p>
|
||||
<div class="section" id="how-to-authenticate-users-against-an-ldap-server-with-anonymous-binding">
|
||||
<h2>How to authenticate users against an LDAP server with anonymous binding ?<a class="headerlink" href="#how-to-authenticate-users-against-an-ldap-server-with-anonymous-binding" title="Permalink to this headline">¶</a></h2>
|
||||
<ol class="arabic">
|
||||
<li><p class="first">Install the django_auth_ldap module for Django:</p>
|
||||
<div class="highlight-python"><pre>pip install django_auth_ldap</pre>
|
||||
</div>
|
||||
</li>
|
||||
<li><p class="first">Configure your local_settings.py file for authenticating against LDAP.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>The next lines must be added:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre><span class="n">AUTHENTICATION_BACKENDS</span> <span class="o">+=</span> <span class="p">(</span> <span class="s">'django_auth_ldap.backend.LDAPBackend'</span><span class="p">,</span> <span class="p">)</span>
|
||||
|
||||
<span class="kn">import</span> <span class="nn">ldap</span>
|
||||
<span class="kn">from</span> <span class="nn">django_auth_ldap.config</span> <span class="kn">import</span> <span class="n">LDAPSearch</span>
|
||||
|
||||
<span class="c"># Here put the LDAP URL of your server</span>
|
||||
<span class="n">AUTH_LDAP_SERVER_URI</span> <span class="o">=</span> <span class="s">'ldap://ldap.example.com'</span>
|
||||
<span class="c"># Let the bind DN and bind password blank for anonymous binding</span>
|
||||
<span class="n">AUTH_LDAP_BIND_DN</span> <span class="o">=</span> <span class="s">""</span>
|
||||
<span class="n">AUTH_LDAP_BIND_PASSWORD</span> <span class="o">=</span> <span class="s">""</span>
|
||||
<span class="c"># Lookup user under the branch o=base and by mathcing their uid against the</span>
|
||||
<span class="c"># received login name</span>
|
||||
<span class="n">AUTH_LDAP_USER_SEARCH</span> <span class="o">=</span> <span class="n">LDAPSearch</span><span class="p">(</span><span class="s">"o=base"</span><span class="p">,</span>
|
||||
<span class="n">ldap</span><span class="o">.</span><span class="n">SCOPE_SUBTREE</span><span class="p">,</span> <span class="s">"(uid=</span><span class="si">%(user)s</span><span class="s">)"</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="how-to-allow-members-of-an-ldap-group-to-manage-authentic">
|
||||
<h2>How to allow members of an LDAP group to manage Authentic ?<a class="headerlink" href="#how-to-allow-members-of-an-ldap-group-to-manage-authentic" title="Permalink to this headline">¶</a></h2>
|
||||
<ol class="arabic">
|
||||
<li><p class="first">First you must know the objectClass of groups in your LDAP schema, this FAQ
|
||||
will show you the configuration for two usual classes: groupOfNames and
|
||||
groupOfUniqueNames.</p>
|
||||
</li>
|
||||
<li><p class="first">Find the relevant groupname. We will say it is: cn=admin,o=mycompany</p>
|
||||
</li>
|
||||
<li><p class="first">Add the following lines:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">django_auth_ldap.config</span> <span class="kn">import</span> <span class="n">GroupOfNamesType</span>
|
||||
<span class="n">AUTH_LDAP_GROUP_TYPE</span> <span class="o">=</span> <span class="n">GroupOfNamesType</span><span class="p">()</span>
|
||||
<span class="n">AUTH_LDAP_GROUP_SEARCH</span> <span class="o">=</span> <span class="n">LDAPSearch</span><span class="p">(</span><span class="s">"o=mycompany"</span><span class="p">,</span>
|
||||
<span class="n">ldap</span><span class="o">.</span><span class="n">SCOPE_SUBTREE</span><span class="p">,</span> <span class="s">"(objectClass=groupOfNames)"</span><span class="p">)</span>
|
||||
<span class="n">AUTH_LDAP_USER_FLAGS_BY_GROUP</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s">"is_staff"</span><span class="p">:</span> <span class="s">"cn=admin,o=mycompany"</span>
|
||||
<span class="p">}</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
<p>For an objectClass of groupOfUniqueNames you would change the string
|
||||
GroupOfNamesType to GroupOfUniqueNamesType and grouOfNames to
|
||||
groupOfUniqueNames. For more complex cases see the django_auth_ldap
|
||||
documentation.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h3><a href="index.html">Table Of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Authentication with an existing LDAP directory</a><ul>
|
||||
<li><a class="reference internal" href="#how-to-authenticate-users-against-an-ldap-server-with-anonymous-binding">How to authenticate users against an LDAP server with anonymous binding ?</a></li>
|
||||
<li><a class="reference internal" href="#how-to-allow-members-of-an-ldap-group-to-manage-authentic">How to allow members of an LDAP group to manage Authentic ?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="installation.html"
|
||||
title="previous chapter">Installation</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="auth_pam.html"
|
||||
title="next chapter">Authentication on Authentic2 with PAM</a></p>
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/auth_ldap.txt"
|
||||
rel="nofollow">Show Source</a></li>
|
||||
</ul>
|
||||
<div id="searchbox" style="display: none">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<input type="text" name="q" />
|
||||
<input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
<p class="searchtip" style="font-size: 90%">
|
||||
Enter search terms or a module, class or function name.
|
||||
</p>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="auth_pam.html" title="Authentication on Authentic2 with PAM"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="installation.html" title="Installation"
|
||||
>previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2011, Mikaël Ates.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,129 @@
|
|||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Authentication on Authentic2 with PAM — Authentic2 1.9.2 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '',
|
||||
VERSION: '1.9.2',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="top" title="Authentic2 1.9.2 documentation" href="index.html" />
|
||||
<link rel="next" title="Configure SAML 2.0 service providers" href="config_saml2_sp.html" />
|
||||
<link rel="prev" title="Authentication with an existing LDAP directory" href="auth_ldap.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="config_saml2_sp.html" title="Configure SAML 2.0 service providers"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="auth_ldap.html" title="Authentication with an existing LDAP directory"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
<div class="section" id="authentication-on-authentic2-with-pam">
|
||||
<span id="auth-pam"></span><h1>Authentication on Authentic2 with PAM<a class="headerlink" href="#authentication-on-authentic2-with-pam" title="Permalink to this headline">¶</a></h1>
|
||||
<p>This module is copied from <a class="reference external" href="https://bitbucket.org/wnielson/django-pam/">https://bitbucket.org/wnielson/django-pam/</a> by Weston
|
||||
Nielson and the pam ctype module by Chris Atlee <a class="reference external" href="http://atlee.ca/software/pam/">http://atlee.ca/software/pam/</a>.</p>
|
||||
<p>Add ‘authentic2.vendor.dpam.backends.PAMBackend’ to your
|
||||
<tt class="docutils literal"><span class="pre">settings.py</span></tt>:</p>
|
||||
<div class="highlight-python"><pre>AUTHENTICATION_BACKENDS = (
|
||||
...
|
||||
'authentic2.vendor.dpam.backends.PAMBackend',
|
||||
...
|
||||
)</pre>
|
||||
</div>
|
||||
<p>Now you can login via the system-login credentials. If the user is
|
||||
successfully authenticated but has never logged-in before, a new <tt class="docutils literal"><span class="pre">User</span></tt>
|
||||
object is created. By default this new <tt class="docutils literal"><span class="pre">User</span></tt> has both <tt class="docutils literal"><span class="pre">is_staff</span></tt> and
|
||||
<tt class="docutils literal"><span class="pre">is_superuser</span></tt> set to <tt class="docutils literal"><span class="pre">False</span></tt>. You can change this behavior by adding
|
||||
<tt class="docutils literal"><span class="pre">PAM_IS_STAFF=True</span></tt> and <tt class="docutils literal"><span class="pre">PAM_IS_SUPERUSER</span></tt> in your <tt class="docutils literal"><span class="pre">settings.py</span></tt> file.</p>
|
||||
<p>The default PAM service used is <tt class="docutils literal"><span class="pre">login</span></tt> but you can change it by setting the
|
||||
<tt class="docutils literal"><span class="pre">PAM_SERVICE</span></tt> variable in your <tt class="docutils literal"><span class="pre">settings.py</span></tt> file.</p>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="auth_ldap.html"
|
||||
title="previous chapter">Authentication with an existing LDAP directory</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="config_saml2_sp.html"
|
||||
title="next chapter">Configure SAML 2.0 service providers</a></p>
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/auth_pam.txt"
|
||||
rel="nofollow">Show Source</a></li>
|
||||
</ul>
|
||||
<div id="searchbox" style="display: none">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<input type="text" name="q" />
|
||||
<input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
<p class="searchtip" style="font-size: 90%">
|
||||
Enter search terms or a module, class or function name.
|
||||
</p>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="config_saml2_sp.html" title="Configure SAML 2.0 service providers"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="auth_ldap.html" title="Authentication with an existing LDAP directory"
|
||||
>previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2011, Mikaël Ates.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,112 @@
|
|||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Configure Authentic2 as a CAS client — Authentic2 1.9.2 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '',
|
||||
VERSION: '1.9.2',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="top" title="Authentic2 1.9.2 documentation" href="index.html" />
|
||||
<link rel="next" title="Attribute Management in Authentic2" href="attribute_management.html" />
|
||||
<link rel="prev" title="Configure Authentic2 as a CAS server" href="config_cas_sp.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="attribute_management.html" title="Attribute Management in Authentic2"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="config_cas_sp.html" title="Configure Authentic2 as a CAS server"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
<div class="section" id="configure-authentic2-as-a-cas-client">
|
||||
<span id="config-cas-idp"></span><h1>Configure Authentic2 as a CAS client<a class="headerlink" href="#configure-authentic2-as-a-cas-client" title="Permalink to this headline">¶</a></h1>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="config_cas_sp.html"
|
||||
title="previous chapter">Configure Authentic2 as a CAS server</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="attribute_management.html"
|
||||
title="next chapter">Attribute Management in Authentic2</a></p>
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/config_cas_idp.txt"
|
||||
rel="nofollow">Show Source</a></li>
|
||||
</ul>
|
||||
<div id="searchbox" style="display: none">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<input type="text" name="q" />
|
||||
<input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
<p class="searchtip" style="font-size: 90%">
|
||||
Enter search terms or a module, class or function name.
|
||||
</p>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="attribute_management.html" title="Attribute Management in Authentic2"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="config_cas_sp.html" title="Configure Authentic2 as a CAS server"
|
||||
>previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2011, Mikaël Ates.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,147 @@
|
|||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Configure Authentic2 as a CAS server — Authentic2 1.9.2 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '',
|
||||
VERSION: '1.9.2',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="top" title="Authentic2 1.9.2 documentation" href="index.html" />
|
||||
<link rel="next" title="Configure Authentic2 as a CAS client" href="config_cas_idp.html" />
|
||||
<link rel="prev" title="Configure Authentic2 as a SAML2 service provider" href="config_saml2_idp.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="config_cas_idp.html" title="Configure Authentic2 as a CAS client"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="config_saml2_idp.html" title="Configure Authentic2 as a SAML2 service provider"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
<div class="section" id="configure-authentic2-as-a-cas-server">
|
||||
<span id="config-cas-sp"></span><h1>Configure Authentic2 as a CAS server<a class="headerlink" href="#configure-authentic2-as-a-cas-server" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="section" id="how-to-use-authentic2-as-a-cas-1-0-or-cas-2-0-identity-provider">
|
||||
<h2>How to use Authentic2 as a CAS 1.0 or CAS 2.0 identity provider ?<a class="headerlink" href="#how-to-use-authentic2-as-a-cas-1-0-or-cas-2-0-identity-provider" title="Permalink to this headline">¶</a></h2>
|
||||
<ol class="arabic">
|
||||
<li><p class="first">Activate CAS IdP support in settings.py:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre><span class="n">IDP_CAS</span> <span class="o">=</span> <span class="bp">True</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</li>
|
||||
<li><p class="first">Then create the database table to hold CAS service tickets:</p>
|
||||
<div class="highlight-python"><pre>python authentic2/manage.py syncdb --migrate</pre>
|
||||
</div>
|
||||
</li>
|
||||
<li><p class="first">Also configure authentic2 to authenticate against your LDAP directory (see
|
||||
above) if your want your user attributes to be accessible from your service,
|
||||
if it is not necessary you can use the normal relational database storage
|
||||
for you users.</p>
|
||||
</li>
|
||||
<li><p class="first">Finally configure your service to point to the CAS endpoint at:</p>
|
||||
<blockquote>
|
||||
<div><p>http[s]://your.domain.com/idp/cas/</p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><p class="first">If needed configure your service to resolve authenticated user with your
|
||||
LDAP directory (if user attributes are needed for your service)</p>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h3><a href="index.html">Table Of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Configure Authentic2 as a CAS server</a><ul>
|
||||
<li><a class="reference internal" href="#how-to-use-authentic2-as-a-cas-1-0-or-cas-2-0-identity-provider">How to use Authentic2 as a CAS 1.0 or CAS 2.0 identity provider ?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="config_saml2_idp.html"
|
||||
title="previous chapter">Configure Authentic2 as a SAML2 service provider</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="config_cas_idp.html"
|
||||
title="next chapter">Configure Authentic2 as a CAS client</a></p>
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/config_cas_sp.txt"
|
||||
rel="nofollow">Show Source</a></li>
|
||||
</ul>
|
||||
<div id="searchbox" style="display: none">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<input type="text" name="q" />
|
||||
<input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
<p class="searchtip" style="font-size: 90%">
|
||||
Enter search terms or a module, class or function name.
|
||||
</p>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="config_cas_idp.html" title="Configure Authentic2 as a CAS client"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="config_saml2_idp.html" title="Configure Authentic2 as a SAML2 service provider"
|
||||
>previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2011, Mikaël Ates.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,117 @@
|
|||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Configure Authentic2 as a SAML2 service provider or a SAML2 proxy — Authentic2 1.9.2 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '',
|
||||
VERSION: '1.9.2',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="top" title="Authentic2 1.9.2 documentation" href="index.html" />
|
||||
<link rel="next" title="Configure Authentic2 as a CAS server" href="config_cas_sp.html" />
|
||||
<link rel="prev" title="Configure SAML 2.0 service providers" href="config_saml2_sp.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="config_cas_sp.html" title="Configure Authentic2 as a CAS server"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="config_saml2_sp.html" title="Configure SAML 2.0 service providers"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
<div class="section" id="configure-authentic2-as-a-saml2-service-provider-or-a-saml2-proxy">
|
||||
<span id="config-saml2-idp"></span><h1>Configure Authentic2 as a SAML2 service provider or a SAML2 proxy<a class="headerlink" href="#configure-authentic2-as-a-saml2-service-provider-or-a-saml2-proxy" title="Permalink to this headline">¶</a></h1>
|
||||
<ol class="arabic simple">
|
||||
<li>Create instance of SP settings</li>
|
||||
<li>Declare IdP</li>
|
||||
<li>Get authsaml2 md</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="config_saml2_sp.html"
|
||||
title="previous chapter">Configure SAML 2.0 service providers</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="config_cas_sp.html"
|
||||
title="next chapter">Configure Authentic2 as a CAS server</a></p>
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/config_saml2_idp.txt"
|
||||
rel="nofollow">Show Source</a></li>
|
||||
</ul>
|
||||
<div id="searchbox" style="display: none">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<input type="text" name="q" />
|
||||
<input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
<p class="searchtip" style="font-size: 90%">
|
||||
Enter search terms or a module, class or function name.
|
||||
</p>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="config_cas_sp.html" title="Configure Authentic2 as a CAS server"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="config_saml2_sp.html" title="Configure SAML 2.0 service providers"
|
||||
>previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2011, Mikaël Ates.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,140 @@
|
|||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Configure SAML 2.0 service providers — Authentic2 1.9.2 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '',
|
||||
VERSION: '1.9.2',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="top" title="Authentic2 1.9.2 documentation" href="index.html" />
|
||||
<link rel="next" title="Configure Authentic2 as a SAML2 service provider" href="config_saml2_idp.html" />
|
||||
<link rel="prev" title="Authentication on Authentic2 with PAM" href="auth_pam.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="config_saml2_idp.html" title="Configure Authentic2 as a SAML2 service provider"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="auth_pam.html" title="Authentication on Authentic2 with PAM"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
<div class="section" id="configure-saml-2-0-service-providers">
|
||||
<span id="config-saml2-sp"></span><h1>Configure SAML 2.0 service providers<a class="headerlink" href="#configure-saml-2-0-service-providers" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="section" id="how-to-i-authenticate-against-authentic2-with-a-samlv2-service-provider">
|
||||
<h2>How to I authenticate against Authentic2 with a SAMLv2 service provider ?<a class="headerlink" href="#how-to-i-authenticate-against-authentic2-with-a-samlv2-service-provider" title="Permalink to this headline">¶</a></h2>
|
||||
<ol class="arabic">
|
||||
<li><p class="first">Grab the SAML2 IdP metadata:</p>
|
||||
<blockquote>
|
||||
<div><p>http[s]://your.domain.com/idp/saml2/metadata</p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><p class="first">And configure your service provider with it.</p>
|
||||
</li>
|
||||
<li><p class="first">Go to the providers admin panel on:</p>
|
||||
<blockquote>
|
||||
<div><p>http[s]://your.domain.com/admin/saml/libertyprovider/add/</p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
</ol>
|
||||
<p>There create a new provider using the service provider metadata and enable it
|
||||
as a service provider, you can customize some behaviours like the preferred
|
||||
assertion consumer or encryption for the NameID or the Assertion element.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h3><a href="index.html">Table Of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Configure SAML 2.0 service providers</a><ul>
|
||||
<li><a class="reference internal" href="#how-to-i-authenticate-against-authentic2-with-a-samlv2-service-provider">How to I authenticate against Authentic2 with a SAMLv2 service provider ?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="auth_pam.html"
|
||||
title="previous chapter">Authentication on Authentic2 with PAM</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="config_saml2_idp.html"
|
||||
title="next chapter">Configure Authentic2 as a SAML2 service provider</a></p>
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/config_saml2_sp.txt"
|
||||
rel="nofollow">Show Source</a></li>
|
||||
</ul>
|
||||
<div id="searchbox" style="display: none">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<input type="text" name="q" />
|
||||
<input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
<p class="searchtip" style="font-size: 90%">
|
||||
Enter search terms or a module, class or function name.
|
||||
</p>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="config_saml2_idp.html" title="Configure Authentic2 as a SAML2 service provider"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="auth_pam.html" title="Authentication on Authentic2 with PAM"
|
||||
>previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2011, Mikaël Ates.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,117 @@
|
|||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Download — Authentic2 1.9.2 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '',
|
||||
VERSION: '1.9.2',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="top" title="Authentic2 1.9.2 documentation" href="index.html" />
|
||||
<link rel="next" title="Installation" href="installation.html" />
|
||||
<link rel="prev" title="Features" href="features.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="installation.html" title="Installation"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="features.html" title="Features"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
<div class="section" id="download">
|
||||
<span id="id1"></span><h1>Download<a class="headerlink" href="#download" title="Permalink to this headline">¶</a></h1>
|
||||
<ol class="arabic simple">
|
||||
<li>Pypi: <a class="reference external" href="http://pypi.python.org/pypi/authentic2/1.9.0">http://pypi.python.org/pypi/authentic2/1.9.0</a></li>
|
||||
<li>Git repository: <a class="reference external" href="http://repos.entrouvert.org/authentic.git">http://repos.entrouvert.org/authentic.git</a></li>
|
||||
<li><a class="reference external" href="http://dev.entrouvert.org/projects/authentic/repository">Browse source</a></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="features.html"
|
||||
title="previous chapter">Features</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="installation.html"
|
||||
title="next chapter">Installation</a></p>
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/download.txt"
|
||||
rel="nofollow">Show Source</a></li>
|
||||
</ul>
|
||||
<div id="searchbox" style="display: none">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<input type="text" name="q" />
|
||||
<input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
<p class="searchtip" style="font-size: 90%">
|
||||
Enter search terms or a module, class or function name.
|
||||
</p>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="installation.html" title="Installation"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="features.html" title="Features"
|
||||
>previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2011, Mikaël Ates.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,128 @@
|
|||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Features — Authentic2 1.9.2 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '',
|
||||
VERSION: '1.9.2',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="top" title="Authentic2 1.9.2 documentation" href="index.html" />
|
||||
<link rel="next" title="Installation" href="installation.html" />
|
||||
<link rel="prev" title="Authentic2’s documentation" href="index.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="installation.html" title="Installation"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="index.html" title="Authentic2’s documentation"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
<div class="section" id="features">
|
||||
<span id="id1"></span><h1>Features<a class="headerlink" href="#features" title="Permalink to this headline">¶</a></h1>
|
||||
<p>Authentic can authenticate users against:</p>
|
||||
<ul class="simple">
|
||||
<li>an LDAP directory,</li>
|
||||
<li>a SAML 2.0 identity provider,</li>
|
||||
<li>an OpenID identity provider,</li>
|
||||
<li>with an X509 certificate.</li>
|
||||
</ul>
|
||||
<p>Authentic can provide authentication to web applications using the following
|
||||
protocols:</p>
|
||||
<ul class="simple">
|
||||
<li>OpenID,</li>
|
||||
<li>SAML 2.0,</li>
|
||||
<li>CAS 1.0 & CAS 2.0.</li>
|
||||
</ul>
|
||||
<p>Authentic can proxy authentication between any two different protocols it
|
||||
support.</p>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="index.html"
|
||||
title="previous chapter">Authentic2’s documentation</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="installation.html"
|
||||
title="next chapter">Installation</a></p>
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/features.txt"
|
||||
rel="nofollow">Show Source</a></li>
|
||||
</ul>
|
||||
<div id="searchbox" style="display: none">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<input type="text" name="q" />
|
||||
<input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
<p class="searchtip" style="font-size: 90%">
|
||||
Enter search terms or a module, class or function name.
|
||||
</p>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="installation.html" title="Installation"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="index.html" title="Authentic2’s documentation"
|
||||
>previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2011, Mikaël Ates.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,95 @@
|
|||
|
||||
|
||||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Index — Authentic2 1.9.2 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '',
|
||||
VERSION: '1.9.2',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="top" title="Authentic2 1.9.2 documentation" href="index.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="#" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
|
||||
<h1 id="index">Index</h1>
|
||||
|
||||
<div class="genindex-jumpbox">
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
|
||||
|
||||
|
||||
<div id="searchbox" style="display: none">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<input type="text" name="q" />
|
||||
<input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
<p class="searchtip" style="font-size: 90%">
|
||||
Enter search terms or a module, class or function name.
|
||||
</p>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="#" title="General Index"
|
||||
>index</a></li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2011, Mikaël Ates.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,176 @@
|
|||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Authentic2’s documentation — Authentic2 1.9.2 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '',
|
||||
VERSION: '1.9.2',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="top" title="Authentic2 1.9.2 documentation" href="#" />
|
||||
<link rel="next" title="Features" href="features.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="features.html" title="Features"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li><a href="#">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
<div class="section" id="authentic2-s-documentation">
|
||||
<h1>Authentic2’s documentation<a class="headerlink" href="#authentic2-s-documentation" title="Permalink to this headline">¶</a></h1>
|
||||
<p>Authentic2 is a versatile identity provider addressing a broad
|
||||
range of needs, from simple to advanced setups, around web authentication,
|
||||
attribute sharing, namespace mapping and authorization management.</p>
|
||||
<p>Authentic2 supports many protocols and standards, including SAML2, CAS, OpenID,
|
||||
LDAP, X509, OATH, and can bridge between them.</p>
|
||||
<p>Authentic2 is under the GNU AGPL version 3 licence.</p>
|
||||
<p>It has support for SAMLv2 thanks to <a class="reference external" href="http://lasso.entrouvert.org">Lasso</a>,
|
||||
a free (GNU GPL) implementation of the Liberty Alliance and OASIS
|
||||
specifications of SAML2, ID-FF1.2 and ID-WSF2.</p>
|
||||
<ul class="simple">
|
||||
<li><a class="reference external" href="http://dev.entrouvert.org/projects/authentic">Authentic2 project site</a></li>
|
||||
<li><a class="reference external" href="http://dev.entrouvert.org/projects/authentic/roadmap">Authentic2 roadmap</a></li>
|
||||
</ul>
|
||||
<div class="section" id="documentation-content">
|
||||
<h2>Documentation content<a class="headerlink" href="#documentation-content" title="Permalink to this headline">¶</a></h2>
|
||||
<div class="toctree-wrapper compound">
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="features.html">Features</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="download.html">Download</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="installation.html#dependencies">Dependencies</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="installation.html#quick-start">Quick Start</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="installation.html#specifying-a-different-database">Specifying a different database</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="installation.html#how-to-upgrade-to-a-new-version-of-authentic">How to upgrade to a new version of authentic ?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="auth_ldap.html">Authentication with an existing LDAP directory</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="auth_ldap.html#how-to-authenticate-users-against-an-ldap-server-with-anonymous-binding">How to authenticate users against an LDAP server with anonymous binding ?</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="auth_ldap.html#how-to-allow-members-of-an-ldap-group-to-manage-authentic">How to allow members of an LDAP group to manage Authentic ?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="auth_pam.html">Authentication on Authentic2 with PAM</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="config_saml2_sp.html">Configure SAML 2.0 service providers</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="config_saml2_sp.html#how-to-i-authenticate-against-authentic2-with-a-samlv2-service-provider">How to I authenticate against Authentic2 with a SAMLv2 service provider ?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="config_saml2_idp.html">Configure Authentic2 as a SAML2 service provider or a SAML2 proxy</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="config_cas_sp.html">Configure Authentic2 as a CAS server</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="config_cas_sp.html#how-to-use-authentic2-as-a-cas-1-0-or-cas-2-0-identity-provider">How to use Authentic2 as a CAS 1.0 or CAS 2.0 identity provider ?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="config_cas_idp.html">Configure Authentic2 as a CAS client</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="attribute_management.html">Attribute Management in Authentic2</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="attribute_management.html#summary">Summary</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="attribute_management.html#configuration">Configuration</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="attribute_management.html#modifying-supported-namespaces-and-attribute-name-mappings">Modifying supported namespaces and attribute name mappings</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="attribute_management.html#explanation-draft">Explanation (Draft)</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="copyright">
|
||||
<h2>Copyright<a class="headerlink" href="#copyright" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Authentic is copyrighted by Entr’ouvert and is licensed through the GNU General
|
||||
Public Licence, version 2 or later. A copy of the whole license text is
|
||||
available in the COPYING file.</p>
|
||||
<p>The OpenID IdP originates in the project django_openid_provider by Roman
|
||||
Barczy¿ski, which is under the Apache 2.0 licence. This imply that you must
|
||||
distribute authentic2 under the AGPL3 licence when distributing this part of the
|
||||
project which is the only AGPL licence version compatible with the Apache 2.0
|
||||
licence.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h3><a href="#">Table Of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Authentic2’s documentation</a><ul>
|
||||
<li><a class="reference internal" href="#documentation-content">Documentation content</a><ul>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="features.html"
|
||||
title="next chapter">Features</a></p>
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/index.txt"
|
||||
rel="nofollow">Show Source</a></li>
|
||||
</ul>
|
||||
<div id="searchbox" style="display: none">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<input type="text" name="q" />
|
||||
<input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
<p class="searchtip" style="font-size: 90%">
|
||||
Enter search terms or a module, class or function name.
|
||||
</p>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="features.html" title="Features"
|
||||
>next</a> |</li>
|
||||
<li><a href="#">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2011, Mikaël Ates.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,217 @@
|
|||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Installation — Authentic2 1.9.2 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '',
|
||||
VERSION: '1.9.2',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="top" title="Authentic2 1.9.2 documentation" href="index.html" />
|
||||
<link rel="next" title="Attribute Management in Authentic2" href="attribute_management.html" />
|
||||
<link rel="prev" title="Features" href="features.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="attribute_management.html" title="Attribute Management in Authentic2"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="features.html" title="Features"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
<div class="section" id="installation">
|
||||
<span id="id1"></span><h1>Installation<a class="headerlink" href="#installation" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="section" id="dependencies">
|
||||
<h2>Dependencies<a class="headerlink" href="#dependencies" title="Permalink to this headline">¶</a></h2>
|
||||
<p>You must install the following packages to use Authentic</p>
|
||||
<ul>
|
||||
<li><p class="first">Python Lasso binding 2.3.5:</p>
|
||||
<blockquote>
|
||||
<div><p>From sources: <a class="reference external" href="http://lasso.entrouvert.org/download">http://lasso.entrouvert.org/download</a>
|
||||
Debian based distribution: apt-get install python-lasso</p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><p class="first">Django 1.3:</p>
|
||||
<blockquote>
|
||||
<div><p>From sources: <a class="reference external" href="http://www.djangoproject.com/download/1.3/tarball/">http://www.djangoproject.com/download/1.3/tarball/</a></p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><p class="first">Django-registration 0.8-alpha-1:</p>
|
||||
<blockquote>
|
||||
<div><p>From sources: <a class="reference external" href="http://bitbucket.org/ubernostrum/django-registration/downloads">http://bitbucket.org/ubernostrum/django-registration/downloads</a>
|
||||
Debian based distribution: apt-get install python-django-registration</p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><p class="first">Django-authopenid 0.9.6:</p>
|
||||
<blockquote>
|
||||
<div><p>From sources: <a class="reference external" href="http://bitbucket.org/benoitc/django-authopenid/downloads">http://bitbucket.org/benoitc/django-authopenid/downloads</a></p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><p class="first">Django-south 0.7.3:</p>
|
||||
<blockquote>
|
||||
<div><p>From sources:: <a class="reference external" href="http://south.aeracode.org/docs/installation.html">http://south.aeracode.org/docs/installation.html</a></p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
<li><p class="first">Django-profiles 0.2:</p>
|
||||
<blockquote>
|
||||
<div><p>From sources:: <a class="reference external" href="http://pypi.python.org/pypi/django-profiles">http://pypi.python.org/pypi/django-profiles</a></p>
|
||||
</div></blockquote>
|
||||
</li>
|
||||
</ul>
|
||||
<p>You install all the django libraries quickly using pip:</p>
|
||||
<div class="highlight-python"><pre>pip install django django-profiles django-registration \
|
||||
django-debug-toolbar django-authopenid south</pre>
|
||||
</div>
|
||||
<p>or easy_install:</p>
|
||||
<div class="highlight-python"><pre>easy_install django django-profiles django-registration \
|
||||
django-debug-toolbar django-authopenid south</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="quick-start">
|
||||
<h2>Quick Start<a class="headerlink" href="#quick-start" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Then launch the following commands:</p>
|
||||
<div class="highlight-python"><pre>python manage.py syncdb --migrate
|
||||
python manage.py runserver</pre>
|
||||
</div>
|
||||
<p>You should see the following output:</p>
|
||||
<div class="highlight-python"><pre>Validating models...
|
||||
0 errors found
|
||||
|
||||
Django version 1.2, using settings 'authentic.settings'
|
||||
Development server is running at http://127.0.0.1:8000/
|
||||
Quit the server with CONTROL-C.
|
||||
|
||||
You can access the running application on http://127.0.0.1:8000/</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="specifying-a-different-database">
|
||||
<h2>Specifying a different database<a class="headerlink" href="#specifying-a-different-database" title="Permalink to this headline">¶</a></h2>
|
||||
<p>This is done by modifying the DATABASES dictionary in your local_settings.py file
|
||||
(create it in Authentic project directory); for example:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre><span class="n">DATABASES</span><span class="p">[</span><span class="s">'default'</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
|
||||
<span class="s">'ENGINE'</span><span class="p">:</span> <span class="s">'django.db.backends.postgresql'</span><span class="p">,</span>
|
||||
<span class="s">'NAME'</span><span class="p">:</span> <span class="s">'authentic'</span><span class="p">,</span>
|
||||
<span class="s">'USER'</span><span class="p">:</span> <span class="s">'admindb'</span><span class="p">,</span>
|
||||
<span class="s">'PASSWORD'</span><span class="p">:</span> <span class="s">'foobar'</span><span class="p">,</span>
|
||||
<span class="s">'HOST'</span><span class="p">:</span> <span class="s">'db.example.com'</span><span class="p">,</span>
|
||||
<span class="s">'PORT'</span><span class="p">:</span> <span class="s">''</span><span class="p">,</span> <span class="c"># empty string means default value</span>
|
||||
<span class="p">}</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You should refer to the Django documentation on databases settings at
|
||||
<a class="reference external" href="http://docs.djangoproject.com/en/dev/ref/settings/#databases">http://docs.djangoproject.com/en/dev/ref/settings/#databases</a> for all
|
||||
the details.</p>
|
||||
</div>
|
||||
<div class="section" id="how-to-upgrade-to-a-new-version-of-authentic">
|
||||
<h2>How to upgrade to a new version of authentic ?<a class="headerlink" href="#how-to-upgrade-to-a-new-version-of-authentic" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Authentic store all its data in a relational database as specified in its
|
||||
settings.py or local_settings.py file. So in order to upgrade to a new version
|
||||
of authentic you have to update your database schema using the
|
||||
migration command — you will need to have installed the dependency django-south,
|
||||
see the beginning of this README file.:</p>
|
||||
<div class="highlight-python"><pre>python ./manage.py migrate</pre>
|
||||
</div>
|
||||
<p>Then you will need to create new tables if there are.:</p>
|
||||
<div class="highlight-python"><pre>python ./manage.py syncdb</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h3><a href="index.html">Table Of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Installation</a><ul>
|
||||
<li><a class="reference internal" href="#dependencies">Dependencies</a></li>
|
||||
<li><a class="reference internal" href="#quick-start">Quick Start</a></li>
|
||||
<li><a class="reference internal" href="#specifying-a-different-database">Specifying a different database</a></li>
|
||||
<li><a class="reference internal" href="#how-to-upgrade-to-a-new-version-of-authentic">How to upgrade to a new version of authentic ?</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="features.html"
|
||||
title="previous chapter">Features</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="attribute_management.html"
|
||||
title="next chapter">Attribute Management in Authentic2</a></p>
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/installation.txt"
|
||||
rel="nofollow">Show Source</a></li>
|
||||
</ul>
|
||||
<div id="searchbox" style="display: none">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<input type="text" name="q" />
|
||||
<input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
<p class="searchtip" style="font-size: 90%">
|
||||
Enter search terms or a module, class or function name.
|
||||
</p>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li class="right" >
|
||||
<a href="attribute_management.html" title="Attribute Management in Authentic2"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="features.html" title="Features"
|
||||
>previous</a> |</li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2011, Mikaël Ates.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,99 @@
|
|||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Search — Authentic2 1.9.2 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '',
|
||||
VERSION: '1.9.2',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<script type="text/javascript" src="_static/searchtools.js"></script>
|
||||
<link rel="top" title="Authentic2 1.9.2 documentation" href="index.html" />
|
||||
<script type="text/javascript">
|
||||
jQuery(function() { Search.loadIndex("searchindex.js"); });
|
||||
</script>
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
<h1 id="search-documentation">Search</h1>
|
||||
<div id="fallback" class="admonition warning">
|
||||
<script type="text/javascript">$('#fallback').hide();</script>
|
||||
<p>
|
||||
Please activate JavaScript to enable the search
|
||||
functionality.
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
From here you can search these documents. Enter your search
|
||||
words into the box below and click "search". Note that the search
|
||||
function will automatically search for all of the words. Pages
|
||||
containing fewer words won't appear in the result list.
|
||||
</p>
|
||||
<form action="" method="get">
|
||||
<input type="text" name="q" value="" />
|
||||
<input type="submit" value="search" />
|
||||
<span id="search-progress" style="padding-left: 10px"></span>
|
||||
</form>
|
||||
|
||||
<div id="search-results">
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
>index</a></li>
|
||||
<li><a href="index.html">Authentic2 1.9.2 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2011, Mikaël Ates.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,653 @@
|
|||
.. _attribute_management:
|
||||
|
||||
==================================
|
||||
Attribute Management in Authentic2
|
||||
==================================
|
||||
|
||||
Summary
|
||||
=======
|
||||
|
||||
Attribute management currently allows to configure attribute policies
|
||||
associated with SAML2 service providers to define attributes that are
|
||||
pushed in SAML2 successful authentication response delivered by Authentic2.
|
||||
|
||||
User attributes can be taken from LDAP directories, the user Django
|
||||
profile or taken from the user Django session if Authentic2 is also configured
|
||||
as a SAML2 service provider.
|
||||
|
||||
Indeed, when Authentic2 acts also as a SAML2 service provider,
|
||||
attributes contained in the SAML2 assertion received from third IdP are put in
|
||||
the user session.
|
||||
|
||||
Attributes can thus be proxyfied during SSO with Authentic2
|
||||
configured as a SAML2 proxy.
|
||||
|
||||
The namespace of attributes received from another SAML2 IdP or pushed in the
|
||||
assertion given in to service providers can be configured per attribute or per
|
||||
service provider.
|
||||
|
||||
By default, the namespace and format of attributes in assertion is conformant
|
||||
to the SAMLV2.0 X500/LDAP Attribute profile::
|
||||
|
||||
<saml:Attribute
|
||||
xmlns:x500="urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500"
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
|
||||
Name="urn:oid:2.5.4.42" FriendlyName="givenName">
|
||||
<saml:AttributeValue xsi:type="xs:string"
|
||||
x500:Encoding="LDAP">Mikaël</saml:AttributeValue>
|
||||
</saml:Attribute>
|
||||
|
||||
But the http://schemas.xmlsoap.org/ws/2005/05/identity/claims from the ISI
|
||||
profile can also be used, for instance::
|
||||
|
||||
<saml:Attribute
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
|
||||
Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"
|
||||
FriendlyName="First Name">
|
||||
<saml:AttributeValue>Mikaël</saml:AttributeValue>
|
||||
</saml:Attribute>
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Configure sources of attributes
|
||||
-------------------------------
|
||||
|
||||
The source of attributes for authentic2 are of two kinds. The LDAP sources and
|
||||
the user django profile.
|
||||
|
||||
Declare the Django profile source
|
||||
_________________________________
|
||||
|
||||
Add an attribute source named USER_PROFILE with namespace 'Default'.
|
||||
|
||||
Then, it is necessary that users create their profile.
|
||||
|
||||
Add an LDAP Source
|
||||
__________________
|
||||
|
||||
For LDAP sources, objects of type 'LDAPSource' must be created.
|
||||
|
||||
**Even if the authentication is based on LDAP authentification, thus that a
|
||||
server is configured in settings.py, it is
|
||||
necessary to create a corresponding 'LDAPSource' to use it as a source of
|
||||
attribute.**
|
||||
|
||||
1. Go to http[s]://your.domain.com/admin/attribute_aggregator/ldapsource/add/
|
||||
|
||||
2. Fill form fields
|
||||
|
||||
Only the field Name, Server, User, Password, Base and Port are used for now.
|
||||
**The namespace of LDAP source must be kept to 'Default', since the system
|
||||
namespace is based on LDAP.**
|
||||
|
||||
.. image:: pictures/ldapsource.png
|
||||
:width: 800 px
|
||||
|
||||
3. Save
|
||||
|
||||
.. image:: pictures/ldapsource_saved.png
|
||||
:width: 800 px
|
||||
|
||||
Manage user distinguished names in LDAP directories
|
||||
___________________________________________________
|
||||
|
||||
To find the user in a LDAP directory, authentic2 must know its distinguished
|
||||
name (DN). If this LDAP has been used when the user has authenticated,
|
||||
Authentic2 learn the user DN. Nothing has to be done from this point of view.
|
||||
|
||||
However, if it is expected that user attributes be taken in a directory that
|
||||
is not used by the user for authentication, it is necessary to manually
|
||||
indicate to Authentic2 what is the user DN in the directory. For this, a
|
||||
user alias in source is created for the user:
|
||||
|
||||
1. Go to http[s]://your.domain.com/admin/attribute_aggregator/useraliasinsource/add/
|
||||
|
||||
2. Fill form fields
|
||||
|
||||
.. image:: pictures/alias_in_source.png
|
||||
:width: 800 px
|
||||
|
||||
3. Save
|
||||
|
||||
.. image:: pictures/alias_in_source_saved.png
|
||||
:width: 800 px
|
||||
|
||||
Configure attributes pushed to SAML2 service providers in SSO response
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Reminder:
|
||||
|
||||
- The default name format in SAML2 assertions is URI
|
||||
- The default namespace called 'Default' is LDAP
|
||||
|
||||
In summary:
|
||||
|
||||
1. Create attribute items indicating an attribute name, a source, the name format expected and the namespace expected for the attribute name and friendly name if any.
|
||||
|
||||
2. Create a named list of attribute items.
|
||||
|
||||
3. Create an attribute policy and associate the previous list or associate the previous list to a existing attribute policy.
|
||||
|
||||
4. Associate the policy to a service provider.
|
||||
|
||||
Create attribute items
|
||||
______________________
|
||||
|
||||
1. Go to http[s]://your.domain.com/admin/idp/attributeitem/add/
|
||||
|
||||
2. Fill form fields
|
||||
|
||||
.. image:: pictures/attribute_item.png
|
||||
:width: 800 px
|
||||
|
||||
3. Save
|
||||
|
||||
.. image:: pictures/attribute_item_saved.png
|
||||
:width: 800 px
|
||||
|
||||
Create a named list of attribute items
|
||||
______________________________________
|
||||
|
||||
1. Go to http[s]://your.domain.com/admin/idp/attributelist/add/
|
||||
|
||||
2. Name the list and add items to list
|
||||
|
||||
.. image:: pictures/attribute_list.png
|
||||
:width: 800 px
|
||||
|
||||
3. Save
|
||||
|
||||
.. image:: pictures/attribute_list_saved.png
|
||||
:width: 800 px
|
||||
|
||||
Create or modify an attribute policy
|
||||
____________________________________
|
||||
|
||||
1. Go to http[s]://your.domain.com/admin/idp/attributepolicy/add/
|
||||
|
||||
2. Add list to the policy
|
||||
|
||||
.. image:: pictures/policy_pull.png
|
||||
:width: 800 px
|
||||
|
||||
3. Save
|
||||
|
||||
.. image:: pictures/policy_pull_saved.png
|
||||
:width: 800 px
|
||||
|
||||
Associate the policy to a service provider
|
||||
__________________________________________
|
||||
|
||||
1. Go to http[s]://your.domain.com/admin/saml/libertyprovider/1/
|
||||
|
||||
2. Add policy to the service provider
|
||||
|
||||
.. image:: pictures/sp_policy_pull.png
|
||||
:width: 800 px
|
||||
|
||||
3. Save
|
||||
|
||||
.. image:: pictures/sp_policy_pull_saved.png
|
||||
:width: 800 px
|
||||
|
||||
4. The display name of the policy has changed
|
||||
|
||||
.. image:: pictures/policy_pull_renamed.png
|
||||
:width: 800 px
|
||||
|
||||
Handle attributes provided by other Identity providers, proxy attributes
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Link to configure first Authentic as a sp to have attributes in session
|
||||
|
||||
Add a source if mapping set to true
|
||||
|
||||
Modifying supported namespaces and attribute name mappings
|
||||
==========================================================
|
||||
|
||||
TBD
|
||||
|
||||
Explanation (Draft)
|
||||
===================
|
||||
|
||||
Attribute aggegrator module
|
||||
---------------------------
|
||||
|
||||
The core attribute management is based on the attribute aggregator module.
|
||||
|
||||
Intro
|
||||
_____
|
||||
|
||||
Attribute aggregator provides a main Model class called UserAttributeProfile,
|
||||
functions to load attributes and extract attributes.
|
||||
|
||||
The mapping between attribute namespaces is built-in and depends on a unique
|
||||
file (mapping.py).
|
||||
|
||||
A main schema is defined and is based on LDAP/X500 for naming. The support
|
||||
of http://schemas.xmlsoap.org/ws/2005/05/identity/claims is partly complete.
|
||||
|
||||
Source of attributes are connected with attribute loading functions using
|
||||
signals.
|
||||
|
||||
FAQ
|
||||
___
|
||||
|
||||
Why not use the Django User profile?
|
||||
|
||||
The django user profile needs to define attributes as class attributes and
|
||||
then support form filling or mapping with LDAP.
|
||||
|
||||
That is useful and may be used, especially because the profile can be used as
|
||||
a source of attribute to load in the attribute_aggregator profile.
|
||||
|
||||
The attribute_aggregator profile allow to load multivalued attributes from any
|
||||
source supported (LDAP, Django profile and attributes in Django session for
|
||||
now) from any namespace defined in mapping.py (LDAP/X500 and claims for now).
|
||||
|
||||
The profile can be loaded giving a source or a list of attribute, or can be
|
||||
from any known source, or with a dictionnary.
|
||||
|
||||
Attributes can be extracted with many functions in any namespace supported.
|
||||
|
||||
Quick explanation
|
||||
_________________
|
||||
|
||||
The schema is defined in mapping.py and is made of definitions like this::
|
||||
|
||||
"sn": {
|
||||
"oid": "2.5.4.4",
|
||||
"display_name": _("sn surname"),
|
||||
"alias": ['surname'],
|
||||
"profile_field_name": 'last_name',
|
||||
"type": "http://www.w3.org/2001/XMLSchema#string",
|
||||
"namespaces": {
|
||||
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims": {
|
||||
"identifiers":
|
||||
[
|
||||
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname",
|
||||
],
|
||||
"friendly_names":
|
||||
[
|
||||
"Last Name",
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
The profile store all the data in a text field taht contains a cPickle list of
|
||||
instances of the class AttributeData.
|
||||
|
||||
The profile is attached to a user and then can be created or loaded with::
|
||||
|
||||
profile = load_or_create_user_profile(user=user)
|
||||
|
||||
User may be None to create a temporary profile for an anonymous user. But
|
||||
that need a DB cleaning function not implemented.
|
||||
|
||||
The model *UserAttributeProfile*
|
||||
________________________________
|
||||
|
||||
The model 'UserAttributeProfile' can be attached to a user and then persist
|
||||
(as a Model).
|
||||
|
||||
When the profile is loaded, all data stored are removed expect if the
|
||||
the data has an expiration date later.
|
||||
|
||||
The profile provide several methods to store and extract attributes.
|
||||
|
||||
All the methods to add attributes are based on a main one accepting a
|
||||
dictionnary of attribute is parameters 'load_by_dic()'. The other methods
|
||||
('load_listed_attributes()', 'load_greedy()') send a signal with a list of
|
||||
attributes (listed_attributes_call) or not (any_attributes_call) to grab a
|
||||
dictionnary. The list is given with the definition name, oid or friendly name
|
||||
of the attribute in the system namespace.
|
||||
|
||||
Into the dictionnary, attributes are given with their name, oid or friendly
|
||||
name in the default namespace or with their name in a namepsace. An expiration
|
||||
date can also be given (ISO8601 format), if none, attribute will be deleted at
|
||||
next profile loading. The dictionnary format is as follows::
|
||||
|
||||
attributes = dict()
|
||||
data_from_source = list()
|
||||
a1 = dict()
|
||||
a1['oid'] = definition_name
|
||||
Or
|
||||
a1['definition'] = definition_name
|
||||
definition may be the definition name like 'gn'
|
||||
or an alias like 'givenName'
|
||||
Or
|
||||
a1['name'] = attribute_name_in_ns
|
||||
a1['namespace'] = ns_name
|
||||
a1['expiration_date'] = date
|
||||
a1['values'] = list_of_values
|
||||
data_from_source.append(a1)
|
||||
...
|
||||
data_from_source.append(a2)
|
||||
attributes[source_name] = data_from_source
|
||||
|
||||
Getters are defined to extract data from a profile. Only AttributeData
|
||||
instances are extracted that assume that any attribute namespace can be used.
|
||||
|
||||
* get_data_of_definition(definition)
|
||||
|
||||
Return a list of AttributeData instances corresponding to the definition
|
||||
given.
|
||||
|
||||
* get_freshest_data_of_definition(definition)
|
||||
|
||||
Return the freshest AttributeData instance. If multiple with no or same exp
|
||||
date, random. Should use the creation date soon.
|
||||
|
||||
* get_data_of_source
|
||||
|
||||
Return a list of AttributeData instances corresponding to the source given.
|
||||
|
||||
* get_data_of_source_by_name
|
||||
|
||||
Idem but source name is given, not a Source instance.
|
||||
|
||||
* get_data_of_definition_and_source
|
||||
|
||||
Return a list of AttributeData instances corresponding to the definition and
|
||||
source given.
|
||||
|
||||
* get_data_of_definition_and_source_by_name
|
||||
|
||||
Idem but source name is given, not a Source instance.
|
||||
|
||||
SAML2 attribute representation in assertions
|
||||
--------------------------------------------
|
||||
|
||||
SAML2 attribute profile (saml-profiles-2.0-os - Section 8) defines two kind of
|
||||
attribute element syntax in the attribute statement of assertions, also
|
||||
called *name format*:
|
||||
|
||||
- BASIC::
|
||||
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
|
||||
|
||||
- URI::
|
||||
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
|
||||
|
||||
*URI should be used when attributes have "universally" known unique names
|
||||
like OID.*
|
||||
|
||||
Example::
|
||||
|
||||
<saml:Attribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"
|
||||
Name="FirstName">
|
||||
<saml:AttributeValue xsi:type="xs:string">By-Tor</saml:AttributeValue>
|
||||
</saml:Attribute>
|
||||
|
||||
<saml:Attribute
|
||||
xmlns:x500="urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500"
|
||||
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
|
||||
Name="urn:oid:2.5.4.42" FriendlyName="givenName">
|
||||
<saml:AttributeValue xsi:type="xs:string"
|
||||
x500:Encoding="LDAP">Steven</saml:AttributeValue>
|
||||
</saml:Attribute>
|
||||
|
||||
|
||||
BASIC
|
||||
_____
|
||||
|
||||
|
||||
Two <Attribute> elements refer to the same SAML attribute if and only if the
|
||||
values of their Name XML attributes are equal in the sense of Section 3.3.6 of
|
||||
[Schema2].
|
||||
|
||||
No additional XML attributes are defined for use with the <Attribute> element.
|
||||
|
||||
The schema type of the contents of the <AttributeValue> element MUST be drawn
|
||||
from one of the types defined in Section 3.3 of [Schema2]. The xsi:type
|
||||
attribute MUST be present and be given the appropriate value.
|
||||
|
||||
X.500/LDAP Attribute Profile (URI)
|
||||
__________________________________
|
||||
|
||||
**Extracted from the SAML2 core specifications**
|
||||
|
||||
Two <Attribute> elements refer to the same SAML attribute if and only if their
|
||||
Name XML attribute values are equal in the sense of [RFC3061]. The
|
||||
FriendlyName attribute plays no role in the comparison.
|
||||
|
||||
Directory attribute type definitions for use in native X.500 directories
|
||||
specify the syntax of the attribute using ASN.1 [ASN.1]. For use in LDAP,
|
||||
directory attribute definitions additionally include an LDAP syntax which
|
||||
specifies how attribute or assertion values conforming to the syntax are to be
|
||||
represented when transferred in the LDAP protocol (known as an LDAP-specific
|
||||
encoding). The LDAP-specific encoding commonly produces Unicode characters in
|
||||
UTF-8 form. This SAML attribute profile specifies the form of SAML attribute
|
||||
values only for those directory attributes which have LDAP syntaxes. Future
|
||||
extensions to this profile may define attribute value formats for directory
|
||||
attributes whose syntaxes specify other encodings.
|
||||
|
||||
To represent the encoding rules in use for a particular attribute value, the
|
||||
<AttributeValue> element MUST contain an XML attribute named Encoding defined
|
||||
in the XML namespace urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500.
|
||||
|
||||
For any directory attribute with a syntax whose LDAP-specific encoding
|
||||
exclusively produces UTF-8 character strings as values, the SAML attribute
|
||||
value is encoded as simply the UTF-8 string itself, as the content of the
|
||||
<AttributeValue> element, with no additional whitespace.
|
||||
In such cases, the xsi:type XML attribute MUST be set to xs:string.
|
||||
The profile-specific Encoding XML attribute is provided, with a value of LDAP.
|
||||
|
||||
The AttributeData instances have a field expiration_data. It the profile
|
||||
exists, obsolete data are removed at loading.
|
||||
|
||||
|
||||
When authentic 2 deals with attributes and needs mapping?
|
||||
---------------------------------------------------------
|
||||
|
||||
Authentic2 behaves as an attribute provider:
|
||||
* At the SSO login
|
||||
* When an attribute request is received
|
||||
|
||||
Authentic requests (e.g. by soap) are not yet supported.
|
||||
|
||||
When Authentic2 behaves as an attribute provider at SSO login
|
||||
_____________________________________________________________
|
||||
|
||||
At a SSO request, just before responding to the service provider, the saml2
|
||||
idp module sends the signal 'add_attributes_to_response' giving the SP entity
|
||||
ID.
|
||||
|
||||
The signal is connected to the function 'provide_attributes_at_sso()' in
|
||||
charge of providing the attributes at the SSO for this SP.
|
||||
|
||||
**Attributes sources are of two kinds. The first ones are the sources that can
|
||||
be requested by the IdP with a syncrhonous binding without user intercations.
|
||||
These sources are called pull sources. They are for now limited to LDAP
|
||||
sources. The other ones are sources are asyncrhonous bindings, usually
|
||||
requiring user interactions. These sources are called push sources. They are
|
||||
now limited to the attributes provided at SSO requests when the IdP acts as a
|
||||
SAML2 SP. There attributes are put/found in the Django session.**
|
||||
|
||||
Each source in the system is declared with an instance of the AttributeSource
|
||||
model. We'll see later that to forward attributes of push sources it is not
|
||||
necessary that a source is declared in some circumstances.
|
||||
|
||||
To manage these sources an attribute policy is attached to services providers.
|
||||
Then the service provider model must be extended with a attribute
|
||||
attributes_at_sso_policy. The service provider must send the signal
|
||||
'add_attributes_to_response'.
|
||||
|
||||
The implementation is actually done for SAML2 providers.
|
||||
|
||||
**In such a policy attributes from pull and push sources are treated
|
||||
differently.**
|
||||
|
||||
**For pull sources, a list of attributes is indicated. Either an attribute is
|
||||
searched in all the pull sources and whatever attribute value found is
|
||||
returned. Or each attribute is indicated with a source. With each attribute is
|
||||
indicated the output format and namespace.**
|
||||
|
||||
**The policy may also indicate that all the attributes in the Django session
|
||||
must be forwarded. Then, no AttributeSource instance is required. All the
|
||||
attributes are then forwarded without treating input namespace considerations.
|
||||
When an AttributeSource instance is found, the input namespace of this source
|
||||
is considered. An option can then be set to tell that the output format and
|
||||
namespace must be taken. A list of attribute can also be given.
|
||||
This list can be use to filter attributes to forward without or without taking
|
||||
care of the source. The output namespace and format can also be trated per
|
||||
attribute.**
|
||||
|
||||
If the namespace is default, the attribute names will be taken from the
|
||||
system namespace. In BASIC the name will be the definition name. In URI, the
|
||||
Name will be the OID in urn format and the friendly name will be the
|
||||
definition name. If a namespace is given, the first identifier of this
|
||||
attribute is taken as Name in BASIC. In URI, the same and the first friendly
|
||||
name is taken.
|
||||
|
||||
::
|
||||
|
||||
class LibertyServiceProvider(models.Model):
|
||||
...
|
||||
attribute_policy = models.ForeignKey(AttributePolicy,
|
||||
verbose_name=_("Attribute policy"), null=True, blank=True)
|
||||
|
||||
class AttributePolicy(models.Model):
|
||||
# List of attributes to provide from pull sources at SSO Login.
|
||||
# If an attribute is indicate without a source, from any source.
|
||||
# The output format and namespace is given by each attribute.
|
||||
attribute_list_for_sso_from_pull_sources = \
|
||||
models.ForeignKey(LibertyAttributeMap,
|
||||
related_name = "attributes of pull sources",
|
||||
blank = True, null = True)
|
||||
|
||||
# Set to true for proxying attributes from pull sources at SSO Login.
|
||||
# Attributes are in session.
|
||||
# All attributes are forwarded as is except if the parameter
|
||||
# 'attribute_list_for_sso_from_push_sources' is initialized
|
||||
forward_attributes_from_pull_sources = models.BooleanField(default=False)
|
||||
|
||||
# Map attributes in session
|
||||
# forward_attributes_in_session must be true
|
||||
# At False, all attributes are forwarded as is
|
||||
# At true, look for the namespace of the source for input, If not found,
|
||||
# system namespace. Look for the options attribute_name_format and
|
||||
# output_namespace of the attribute policy for output.
|
||||
map_attributes_from_pull_sources = models.BooleanField(default=False)
|
||||
|
||||
# ATTRIBUTE_VALUE_FORMATS[0] =>
|
||||
# (lasso.SAML2_ATTRIBUTE_NAME_FORMAT_BASIC, 'SAMLv2 BASIC')
|
||||
output_name_format = models.CharField(max_length = 100,
|
||||
choices = ATTRIBUTE_VALUE_FORMATS,
|
||||
default = ATTRIBUTE_VALUE_FORMATS[0])
|
||||
|
||||
#ATTRIBUTES_NS[0] => ('Default', 'Default')
|
||||
output_namespace = models.CharField(max_length = 100,
|
||||
choices = ATTRIBUTES_NS, default = ATTRIBUTES_NS[0])
|
||||
|
||||
# Filter attributes pushed from source.
|
||||
source_filter_for_sso_from_push_sources = \
|
||||
models.ManyToManyField(AttributeSource,
|
||||
related_name = "attributes of pull sources",
|
||||
blank = True, null = True)
|
||||
|
||||
# List of attributes to filter from pull sources at SSO Login.
|
||||
attribute_filter_for_sso_from_push_sources = \
|
||||
models.ForeignKey(LibertyAttributeMap,
|
||||
related_name = "attributes of pull sources",
|
||||
blank = True, null = True)
|
||||
|
||||
# The sources of attributes of the previous list are considered.
|
||||
# May be used conjointly with 'source_filter_for_sso_from_push_sources'
|
||||
filter_source_of_filtered_attributes = models.BooleanField(default=False)
|
||||
|
||||
# To map the attributes of forwarded attributes with the defaut output
|
||||
# format and namespace, use 'map_attributes_from_pull_sources'
|
||||
# Use the following option to use the output format and namespace
|
||||
# indicated for each attribute.
|
||||
map_attributes_of_filtered_attributes = models.BooleanField(default=False)
|
||||
|
||||
|
||||
# Set to true to take in account missing required attributes
|
||||
send_error_and_no_attrs_if_missing_required_attrs = \
|
||||
models.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('attribute options policy')
|
||||
verbose_name_plural = _('attribute options policies')
|
||||
|
||||
|
||||
class AttributeList(models.Model):
|
||||
name = models.CharField(max_length = 40, unique = True)
|
||||
attributes = models.ManyToManyField(AttributeItem,
|
||||
related_name = "attributes of the list",
|
||||
blank = True, null = True)
|
||||
|
||||
|
||||
class AttributeItem(models.Model):
|
||||
attribute_name = models.CharField(max_length = 100, choices = ATTRIBUTES,
|
||||
default = ATTRIBUTES[0])
|
||||
# ATTRIBUTE_VALUE_FORMATS[0] =>
|
||||
# (lasso.SAML2_ATTRIBUTE_NAME_FORMAT_BASIC, 'SAMLv2 BASIC')
|
||||
output_attribute_name_format = models.CharField(max_length = 100,
|
||||
choices = ATTRIBUTE_VALUE_FORMATS,
|
||||
default = ATTRIBUTE_VALUE_FORMATS[0])
|
||||
#ATTRIBUTES_NS[0] => ('Default', 'Default')
|
||||
output_namespace = models.CharField(max_length = 100,
|
||||
choices = ATTRIBUTES_NS, default = ATTRIBUTES_NS[0])
|
||||
required = models.BooleanField(default=False)
|
||||
source = models.ForeignKey(AttributeSource, blank = True, null = True)
|
||||
|
||||
|
||||
A list of attributes can also be taken from the service provider metadata and
|
||||
added to 'attribute_list_for_sso_from_pull_sources'. The namespace may be
|
||||
extracted from the metadata. This namespace is then used to look for the
|
||||
corresponding definition and then to provide the attribute in the right
|
||||
namespace. Read attributes from metadata is not yet supported.
|
||||
|
||||
For the attributes of pull sources, once the list of attributes is defined,
|
||||
They are loaded in the user profile.
|
||||
|
||||
As explained before the attribute_aggregator loading function send signals to
|
||||
grab dictionnary of attributes. Up to know, only the ldap loading function are
|
||||
connected to these signals. The namespace of LDAP sources is assumed to be
|
||||
the same as the system namespace. There is here then no mapping needed. Other
|
||||
kind of sources than LDAP can be defined in attribute aggregator.
|
||||
|
||||
To grab attributes from a LDAP the user dn in the LDAP or at least a local
|
||||
identifier in the LDAP is required. For this purpose, each user has alias
|
||||
associated with LDAP source. These aliases must their DN in the LDAP. When
|
||||
the authentication LDAP backend will be taken in account, the dn will be taken
|
||||
direclty from the user Model instance.
|
||||
|
||||
Each LDAP sources are declared with the binding parameters. The LDAP namespace
|
||||
is always 'Default'.
|
||||
|
||||
If an attribute to load is not found and is required the answer should report
|
||||
an error (Not yet implemented).
|
||||
|
||||
Attributes in response can also be provided with other means than from an LDAP
|
||||
source. Attributes can be put in the user Django session and then loaded in
|
||||
the profile. An option of the service provier indicate if attributes in the
|
||||
session must be provided to the service provider.
|
||||
|
||||
To have the attribute loaded from the session, they must be provided in the
|
||||
session as follows:
|
||||
request.session['attributes'][source_name] = list()
|
||||
|
||||
The source_name must be the name of an existing instance of an
|
||||
'AttributeSource'. Such an instance contains a field namespace indicating the
|
||||
namespace of attributes.
|
||||
|
||||
This is currently implemented only for the SAML2 service provider module of
|
||||
authentic2. Authsaml2, the SP module, parse the assertion and put the
|
||||
attributes in the session.
|
||||
|
||||
::
|
||||
|
||||
if not 'multisource_attributes' in request.session:
|
||||
request.session['attributes'] = dict{}
|
||||
request.session['multisource_attributes'] \
|
||||
[login.assertion.issuer.content] = attributes
|
||||
|
||||
Then, Authentic2 can be used as a SAML2 proxy forwarding attributes in
|
||||
assertion, eventually doing a namespace mapping. For this, the option
|
||||
forward attributes in sesion must be set (by default False).
|
|
@ -0,0 +1,61 @@
|
|||
.. _auth_ldap:
|
||||
|
||||
==============================================
|
||||
Authentication with an existing LDAP directory
|
||||
==============================================
|
||||
|
||||
Authentic use the module django_auth_ldap to synchronize the Django user tables
|
||||
with an LDAP. For complex use case, we will refer you to the django_auth_ldap
|
||||
documentation, see http://packages.python.org/django-auth-ldap/.
|
||||
|
||||
How to authenticate users against an LDAP server with anonymous binding ?
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
1. Install the django_auth_ldap module for Django::
|
||||
|
||||
pip install django_auth_ldap
|
||||
|
||||
|
||||
2. Configure your local_settings.py file for authenticating against LDAP.
|
||||
|
||||
The next lines must be added::
|
||||
|
||||
AUTHENTICATION_BACKENDS += ( 'django_auth_ldap.backend.LDAPBackend', )
|
||||
|
||||
import ldap
|
||||
from django_auth_ldap.config import LDAPSearch
|
||||
|
||||
# Here put the LDAP URL of your server
|
||||
AUTH_LDAP_SERVER_URI = 'ldap://ldap.example.com'
|
||||
# Let the bind DN and bind password blank for anonymous binding
|
||||
AUTH_LDAP_BIND_DN = ""
|
||||
AUTH_LDAP_BIND_PASSWORD = ""
|
||||
# Lookup user under the branch o=base and by mathcing their uid against the
|
||||
# received login name
|
||||
AUTH_LDAP_USER_SEARCH = LDAPSearch("o=base",
|
||||
ldap.SCOPE_SUBTREE, "(uid=%(user)s)")
|
||||
|
||||
How to allow members of an LDAP group to manage Authentic ?
|
||||
-----------------------------------------------------------
|
||||
|
||||
1. First you must know the objectClass of groups in your LDAP schema, this FAQ
|
||||
will show you the configuration for two usual classes: groupOfNames and
|
||||
groupOfUniqueNames.
|
||||
|
||||
2. Find the relevant groupname. We will say it is: cn=admin,o=mycompany
|
||||
|
||||
3. Add the following lines::
|
||||
|
||||
from django_auth_ldap.config import GroupOfNamesType
|
||||
AUTH_LDAP_GROUP_TYPE = GroupOfNamesType()
|
||||
AUTH_LDAP_GROUP_SEARCH = LDAPSearch("o=mycompany",
|
||||
ldap.SCOPE_SUBTREE, "(objectClass=groupOfNames)")
|
||||
AUTH_LDAP_USER_FLAGS_BY_GROUP = {
|
||||
"is_staff": "cn=admin,o=mycompany"
|
||||
}
|
||||
|
||||
For an objectClass of groupOfUniqueNames you would change the string
|
||||
GroupOfNamesType to GroupOfUniqueNamesType and grouOfNames to
|
||||
groupOfUniqueNames. For more complex cases see the django_auth_ldap
|
||||
documentation.
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
.. _auth_pam:
|
||||
|
||||
=====================================
|
||||
Authentication on Authentic2 with PAM
|
||||
=====================================
|
||||
|
||||
This module is copied from https://bitbucket.org/wnielson/django-pam/ by Weston
|
||||
Nielson and the pam ctype module by Chris Atlee http://atlee.ca/software/pam/.
|
||||
|
||||
Add 'authentic2.vendor.dpam.backends.PAMBackend' to your
|
||||
``settings.py``::
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
...
|
||||
'authentic2.vendor.dpam.backends.PAMBackend',
|
||||
...
|
||||
)
|
||||
|
||||
Now you can login via the system-login credentials. If the user is
|
||||
successfully authenticated but has never logged-in before, a new ``User``
|
||||
object is created. By default this new ``User`` has both ``is_staff`` and
|
||||
``is_superuser`` set to ``False``. You can change this behavior by adding
|
||||
``PAM_IS_STAFF=True`` and ``PAM_IS_SUPERUSER`` in your ``settings.py`` file.
|
||||
|
||||
The default PAM service used is ``login`` but you can change it by setting the
|
||||
``PAM_SERVICE`` variable in your ``settings.py`` file.
|
|
@ -0,0 +1,245 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Authentic2 documentation build configuration file, created by
|
||||
# sphinx-quickstart on Thu Oct 13 14:38:32 2011.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.pngmath', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Authentic2'
|
||||
copyright = u'2011, Mikaël Ates'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '1.9.2'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'default'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'Authentic2doc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'Authentic2.tex', u'Authentic2 Documentation',
|
||||
u'Mikaël Ates', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output --------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'authentic2', u'Authentic2 Documentation',
|
||||
[u'Mikaël Ates'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output ------------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'Authentic2', u'Authentic2 Documentation', u'Mikaël Ates',
|
||||
'Authentic2', 'One line description of project.', 'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
||||
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {'http://docs.python.org/': None}
|
|
@ -0,0 +1,5 @@
|
|||
.. _config_cas_idp:
|
||||
|
||||
====================================
|
||||
Configure Authentic2 as a CAS client
|
||||
====================================
|
|
@ -0,0 +1,28 @@
|
|||
.. _config_cas_sp:
|
||||
|
||||
====================================
|
||||
Configure Authentic2 as a CAS server
|
||||
====================================
|
||||
|
||||
How to use Authentic2 as a CAS 1.0 or CAS 2.0 identity provider ?
|
||||
-----------------------------------------------------------------
|
||||
|
||||
1. Activate CAS IdP support in settings.py::
|
||||
|
||||
IDP_CAS = True
|
||||
|
||||
2. Then create the database table to hold CAS service tickets::
|
||||
|
||||
python authentic2/manage.py syncdb --migrate
|
||||
|
||||
3. Also configure authentic2 to authenticate against your LDAP directory (see
|
||||
above) if your want your user attributes to be accessible from your service,
|
||||
if it is not necessary you can use the normal relational database storage
|
||||
for you users.
|
||||
|
||||
4. Finally configure your service to point to the CAS endpoint at:
|
||||
|
||||
http[s]://your.domain.com/idp/cas/
|
||||
|
||||
5. If needed configure your service to resolve authenticated user with your
|
||||
LDAP directory (if user attributes are needed for your service)
|
|
@ -0,0 +1,12 @@
|
|||
.. _config_saml2_idp:
|
||||
|
||||
=================================================================
|
||||
Configure Authentic2 as a SAML2 service provider or a SAML2 proxy
|
||||
=================================================================
|
||||
|
||||
1. Create instance of SP settings
|
||||
|
||||
2. Declare IdP
|
||||
|
||||
3. Get authsaml2 md
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _config_saml2_sp:
|
||||
|
||||
====================================
|
||||
Configure SAML 2.0 service providers
|
||||
====================================
|
||||
|
||||
How to I authenticate against Authentic2 with a SAMLv2 service provider ?
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
1. Grab the SAML2 IdP metadata:
|
||||
|
||||
http[s]://your.domain.com/idp/saml2/metadata
|
||||
|
||||
2. And configure your service provider with it.
|
||||
|
||||
3. Go to the providers admin panel on:
|
||||
|
||||
http[s]://your.domain.com/admin/saml/libertyprovider/add/
|
||||
|
||||
There create a new provider using the service provider metadata and enable it
|
||||
as a service provider, you can customize some behaviours like the preferred
|
||||
assertion consumer or encryption for the NameID or the Assertion element.
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
.. _download:
|
||||
|
||||
========
|
||||
Download
|
||||
========
|
||||
|
||||
1. Pypi: http://pypi.python.org/pypi/authentic2/1.9.0
|
||||
|
||||
2. Git repository: http://repos.entrouvert.org/authentic.git
|
||||
|
||||
3. `Browse source <http://dev.entrouvert.org/projects/authentic/repository>`_
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _features:
|
||||
|
||||
========
|
||||
Features
|
||||
========
|
||||
|
||||
Authentic can authenticate users against:
|
||||
|
||||
- an LDAP directory,
|
||||
- a SAML 2.0 identity provider,
|
||||
- an OpenID identity provider,
|
||||
- with an X509 certificate.
|
||||
|
||||
Authentic can provide authentication to web applications using the following
|
||||
protocols:
|
||||
|
||||
- OpenID,
|
||||
- SAML 2.0,
|
||||
- CAS 1.0 & CAS 2.0.
|
||||
|
||||
Authentic can proxy authentication between any two different protocols it
|
||||
support.
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
.. Authentic2 documentation master file, created by
|
||||
sphinx-quickstart on Thu Oct 13 09:53:03 2011.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
==========================
|
||||
Authentic2's documentation
|
||||
==========================
|
||||
|
||||
Authentic2 is a versatile identity provider addressing a broad
|
||||
range of needs, from simple to advanced setups, around web authentication,
|
||||
attribute sharing, namespace mapping and authorization management.
|
||||
|
||||
Authentic2 supports many protocols and standards, including SAML2, CAS, OpenID,
|
||||
LDAP, X509, OATH, and can bridge between them.
|
||||
|
||||
Authentic2 is under the GNU AGPL version 3 licence.
|
||||
|
||||
It has support for SAMLv2 thanks to `Lasso <http://lasso.entrouvert.org>`_,
|
||||
a free (GNU GPL) implementation of the Liberty Alliance and OASIS
|
||||
specifications of SAML2, ID-FF1.2 and ID-WSF2.
|
||||
|
||||
- `Authentic2 project site <http://dev.entrouvert.org/projects/authentic>`_
|
||||
- `Authentic2 roadmap <http://dev.entrouvert.org/projects/authentic/roadmap>`_
|
||||
|
||||
Documentation content
|
||||
=====================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
features
|
||||
|
||||
download
|
||||
|
||||
installation
|
||||
|
||||
auth_ldap
|
||||
|
||||
auth_pam
|
||||
|
||||
config_saml2_sp
|
||||
|
||||
config_saml2_idp
|
||||
|
||||
config_cas_sp
|
||||
|
||||
config_cas_idp
|
||||
|
||||
attribute_management
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
Authentic is copyrighted by Entr'ouvert and is licensed through the GNU General
|
||||
Public Licence, version 2 or later. A copy of the whole license text is
|
||||
available in the COPYING file.
|
||||
|
||||
The OpenID IdP originates in the project django_openid_provider by Roman
|
||||
Barczy¿ski, which is under the Apache 2.0 licence. This imply that you must
|
||||
distribute authentic2 under the AGPL3 licence when distributing this part of the
|
||||
project which is the only AGPL licence version compatible with the Apache 2.0
|
||||
licence.
|
||||
|
||||
.. Indices and tables
|
||||
.. ==================
|
||||
|
||||
.. * :ref:`genindex`
|
||||
.. * :ref:`modindex`
|
||||
.. * :ref:`search`
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
.. _installation:
|
||||
|
||||
============
|
||||
Installation
|
||||
============
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
You must install the following packages to use Authentic
|
||||
|
||||
- Python Lasso binding 2.3.5:
|
||||
|
||||
From sources: http://lasso.entrouvert.org/download
|
||||
Debian based distribution: apt-get install python-lasso
|
||||
|
||||
- Django 1.3:
|
||||
|
||||
From sources: http://www.djangoproject.com/download/1.3/tarball/
|
||||
|
||||
- Django-registration 0.8-alpha-1:
|
||||
|
||||
From sources: http://bitbucket.org/ubernostrum/django-registration/downloads
|
||||
Debian based distribution: apt-get install python-django-registration
|
||||
|
||||
- Django-authopenid 0.9.6:
|
||||
|
||||
From sources: http://bitbucket.org/benoitc/django-authopenid/downloads
|
||||
|
||||
- Django-south 0.7.3:
|
||||
|
||||
From sources:: http://south.aeracode.org/docs/installation.html
|
||||
|
||||
- Django-profiles 0.2:
|
||||
|
||||
From sources:: http://pypi.python.org/pypi/django-profiles
|
||||
|
||||
You install all the django libraries quickly using pip::
|
||||
|
||||
pip install django django-profiles django-registration \
|
||||
django-debug-toolbar django-authopenid south
|
||||
|
||||
or easy_install::
|
||||
|
||||
easy_install django django-profiles django-registration \
|
||||
django-debug-toolbar django-authopenid south
|
||||
|
||||
Quick Start
|
||||
-----------
|
||||
|
||||
Then launch the following commands::
|
||||
|
||||
python manage.py syncdb --migrate
|
||||
python manage.py runserver
|
||||
|
||||
You should see the following output::
|
||||
|
||||
Validating models...
|
||||
0 errors found
|
||||
|
||||
Django version 1.2, using settings 'authentic.settings'
|
||||
Development server is running at http://127.0.0.1:8000/
|
||||
Quit the server with CONTROL-C.
|
||||
|
||||
You can access the running application on http://127.0.0.1:8000/
|
||||
|
||||
|
||||
Specifying a different database
|
||||
-------------------------------
|
||||
|
||||
This is done by modifying the DATABASES dictionary in your local_settings.py file
|
||||
(create it in Authentic project directory); for example::
|
||||
|
||||
DATABASES['default'] = {
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
'NAME': 'authentic',
|
||||
'USER': 'admindb',
|
||||
'PASSWORD': 'foobar',
|
||||
'HOST': 'db.example.com',
|
||||
'PORT': '', # empty string means default value
|
||||
}
|
||||
|
||||
You should refer to the Django documentation on databases settings at
|
||||
http://docs.djangoproject.com/en/dev/ref/settings/#databases for all
|
||||
the details.
|
||||
|
||||
How to upgrade to a new version of authentic ?
|
||||
----------------------------------------------
|
||||
|
||||
Authentic store all its data in a relational database as specified in its
|
||||
settings.py or local_settings.py file. So in order to upgrade to a new version
|
||||
of authentic you have to update your database schema using the
|
||||
migration command — you will need to have installed the dependency django-south,
|
||||
see the beginning of this README file.::
|
||||
|
||||
python ./manage.py migrate
|
||||
|
||||
Then you will need to create new tables if there are.::
|
||||
|
||||
python ./manage.py syncdb
|
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 32 KiB |