Skip to content
Snippets Groups Projects
README.md 6.68 KiB
Newer Older
  • Learn to ignore specific revisions
  • Art's avatar
    Art committed
    
    
    #### Minimal Intro:
    - [SSO](https://lmddgtfy.net/?q=SSO): Single Sign On
    
    Art's avatar
    Art committed
    - SLO: Single Log Out
    - SP: Service Provider (your web app)
    - IdP: Identity Provider (server with some dreadful software like Shibboleth)
    - Metadata/Meta: an XML that describes SP or IdP (or some other entity)
    - SAML/SAML2: It's just another XML-based enterprise-grade standard that will make you cry blood and shit bricks
    
    Art's avatar
    Art committed
    
    
    
    Art's avatar
    Art committed
    #### Necessary Stuff
    
    Art's avatar
    Art committed
    - Binary dependencies: `sudo apt install libxml2-dev libxslt1-dev xmlsec1 libxmlsec1-dev pkg-config`
    - Python dependencies: see `requirements.txt` or `setup.py`
    - Add the app into `INSTALLED_APPS`
    
    Art's avatar
    Art committed
    - Include the app's `urls.py` into the project `urls.py` `urlpatterns`, preferably without a prefix
    
    
    
    Art's avatar
    Art committed
    #### Development Setup
    
    Art's avatar
    Art committed
    - Simply use `localhost:8000/dev/` _(might change depending on your urlconf)_ for all the logging-in-out-users-groups stuff
    - To make your life easier, add the following to your project settings: 
    ```python
    LOGIN_URL = urls.reverse_lazy("sso-dev")
    ```
    
    - If you want to debug `ssoauth` or need a fully functional SSO during development for some other reason, an example is below. For additional info see production setup chapter and `ssoauth.app_settings.defaults`. If you also want a working SLO during development you will need SSL for your localhost, `nginx` will be your best friend.
    
    Art's avatar
    Art committed
    ```python
    """ settings/dev.py """
    
    Art's avatar
    Art committed
    import os, socket
    
    Art's avatar
    Art committed
    
    
    IDP_META_URL = "https://idp-test.it.hs-hannover.de/idp/shibboleth"
    
    Art's avatar
    Art committed
    IDP_LOGOUT_URL = "https://idp-test.it.hs-hannover.de/idp/profile/Logout"
    
    Art's avatar
    Art committed
    
    
    SP_KEY = "{project_settings}/cert/sp.key"
    SP_CERT = "{project_settings}/cert/sp.pem"
    
    Art's avatar
    Art committed
    
    
    SP_HOST = "localhost"
    SP_PORT = 8000
    SP_SSL = False
    
    Art's avatar
    Art committed
    
    
    Art's avatar
    Art committed
    SP_FORCE_ENTITY_ID = "dev-id-{0}-{1}".format(socket.gethostname(), os.path.dirname(os.path.dirname(__file__)))  # too many localhosts around
    
    
    LOGIN_URL = urls.reverse_lazy("sso-dev")  # it's "sso-login" for prod
    
    #### Overriding Log In Pages of Other Apps
    There are some apps like `django.contrib.admin` or `wagtail` that will simply ignore `LOGIN_URL` and use their own log in page. If this behavior is undesirable and you would prefer using `ssoauth` instead, add the following into your `urls.py` (_before_ including URLs of that other app):
    ```python3
    re_path(r"^(?:\w+/)?login/?$", ssoauth_views.LogInView.as_view(already_authenticated_403=True)),
    ```
      - Adjust the path if required
      - Optional argument `already_authenticated_403=True` is used to avoid redirect loops (e.g. caused by `django.contrib.admin`). You can also use `already_authenticated_redirect="url-name"`.
    
    Art's avatar
    Art committed
    
    #### Regarding Logging Out
    
    After logging out locally, user will be redirected to one of the following (with this priority):
      - `?next=` GET value (Note: instead of `next` use use `django.contrib.auth.REDIRECT_FIELD_NAME`)  
      - `LOGOUT_REDIRECT_URL` (vanilla django setting), recommended values are:
        - `LOGOUT_REDIRECT_URL = None` for the default behavior
        - `LOGOUT_REDIRECT_URL = urls.reverse_lazy("sso-logout-idp")` if you want to instantly initiate IDP log out, possibly triggering SLO
        - `LOGOUT_REDIRECT_URL = urls.reverse_lazy("sso-logged-out-locally")`
      - Default: redirect to the view that asks users what to do next. Don't forget to override template `ssoauth/logged_out_locally.html`
    
    **SLO**: SLO/SLS is disabled by default. If you want to try your luck with you should add to your project settings `SP_SLS_ENABLED = True`
    
    Currently only IdP-initiated SLO is supported by this app. The only supported binding type is HTTP-Redirect due to the limitations of the underlying library used.
    
    
    For SLO with HTTP-Redirect to work, the SLS page must be included as `<iframe>`. Your server and/or browser might restrict such behavior. Start with setting `SP_SLS_X_FRAME_OPTIONS` (check `ssoauth.app_settings.defaults`). 
    
    Art's avatar
    Art committed
    If you have `nginx` serving pages to users, you might need to configure `x-frame-options` for the SLS view (Only the SLS view, nowhere else!). Additionally you might need to configure CSP on the web server on the IdP side. Anyways it will most likely be a lot of [fun](https://duckduckgo.com/?q=dwarf+fortress+fun) for you.
    
    
    #### Groups and Permissions
    To receive groups over SSO you need a group mapping (and of course a properly configured IdP). You can manage group mapping with `group_mapping` management command. Example:
    
        
        group_mapping add myproject_superusers "CN=MyProjectSuperusers,OU=Foo,OU=Bar,DC=fh-h,DC=de"
    
    
    Art's avatar
    Art committed
    *Groups are not mapped automatically. The reason is that automatic mapping can pose security risks. Imagine auto-mapping that expects group with name "Superusers"; an intruder could create new group with this name under any path they own and/or create an alias/reference and receive superuser permissions in your project.* 
    
    Art's avatar
    Art committed
    #### Production Settings
    
    This example might be incomplete. See `ssoauth.app_settings.defaults` for additional info
    
    Art's avatar
    Art committed
    
    ```python
    """ settings/prod.py """
    
    Art's avatar
    Art committed
    
    
    Art's avatar
    Art committed
    SP_HOST = "foobar.it.hs-hannover.de"  # FQDN of your SP
    
    Art's avatar
    Art committed
    IDP_META_URL = "https://idp.hs-hannover.de/idp/shibboleth"  # production
    
    Art's avatar
    Art committed
    IDP_LOGOUT_URL = "https://idp.it.hs-hannover.de/idp/profile/Logout"  # web page for IdP logout (might initiate SLO)
    
    Art's avatar
    Art committed
    
    
    Art's avatar
    Art committed
    LOGIN_URL = urls.reverse_lazy("sso-login")  # django setting
    
    Art's avatar
    Art committed
    ```
    
    Art's avatar
    Art committed
    
    #### Certs
    
    - if you don't need your cert to be signed you can use `openssl req -new -x509 -days 3650 -nodes -out sp.pem -keyout sp.key`
    - create `cert` directory:
      - inside of project `settings` directory if it's a package
      - next to project `settings.py` file if it's a module
    - `./cert/sp.key` put the private key here
    - `./cert/sp.pem` put the certificate here, signing is optional
    - if you prefer your keys somewhere else, set `SP_KEY` and `SP_CERT` settings
    
    
    #### Add an SP to an IdP
    
    - Ask somebody who knows more. Seriously.
    - Try on the test IdP before you brick the production!
    - Grab your meta
      - Run your project.
      - Find meta of your SP (relative path `/saml2/meta`)
      - Use Ctrl+U ("view source") to get the actual XML, otherwise your browser could mess it up
    - IdP:
      - `SSH` to the IdP and locate the Shibboleth directory, most likely `/opt/shibboleth-idp/`
      - put your meta into a new file in `./metadata/` and give it a nice verbose name
      - edit `./conf/metadata-providers.xml`
        - create a new `<MetadataProvider .../>` element, your best guess is to copy an existing line that belongs to some existing Django project
        - `id` should be unique
        - `metadataFile` should point on your new metadata
       - edit `./conf/attribute-filter.xml`
         - add a new `<Rule .../>` element inside of `<AttributeFilterPolicy id="releaseToDjango">`
         - `value` (looks like URI) should be the `entityID` of your SP (you find it in your meta)
      - `systemctl restart tomcat8` (you now have some time to grab you a coffee)
      - ensure the IdP works after restarting!
    
    Art's avatar
    Art committed