Skip to content
Snippets Groups Projects
README.md 7.36 KiB
Newer Older
  • Learn to ignore specific revisions
  • Art's avatar
    Art committed
    #### ssoauth:
    - Manages users and permissions
    - Authenticates users using SAML2 (Shibboleth)
    - Authorizes users based on groups:
      - Groups are normally received via SAML2
      - Groups can also be fetched locally: this is enabled automatically if the `hsh` app is available, skipping multiple software layers for additional security
    - Suppresses direct permission assignment (`django.contrib.auth` `User`<->`Permission`), use groups instead
    
    
    #### Minimal SSO Intro:
    
    Art's avatar
    Art committed
    - [SSO](https://lmddgtfy.net/?q=SSO): Single Sign On
    
    Art's avatar
    Art committed
    - SLO: Single Log Out
    - SP: Service Provider (your web app)
    
    Art's avatar
    Art committed
    - IDP: Identity Provider (e.g. Shibboleth)
    - Metadata: an XML that describes SP or IDP (or some other relying party)
    - SAML/SAML2: yet another XML-based enterprise-grade standard that will make you cry blood
    
    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`
    
    - Include the `ssoauth` `urls.py` into the project `urls.py` `urlpatterns`:
       - Without a path/prefix: youre done.
       - With a path/prefix:
         - Reconsider it. It's highly recommended to include `ssoauth` **without** a prefix/path to avoid issues with apps like `contrib.admin` and `wagtail` that provide their own log in pages.
         - If you really need to use a path/prefix, make sure to set a setting `LOGIN_URL = urls.reverse_lazy("sso-login")`
    
    Art's avatar
    Art committed
    
    
    
    Art's avatar
    Art committed
    #### Development Setup
    
    - Should work with zero additional configuration.
    
    Art's avatar
    Art committed
    - You can use `localhost:8000/dev/` _(might change depending on your url configuration)_ to access the dev view with all its helper features.
    
    If you are not sure whether you need it: you don't need it.
    Use this only if you want an actual SSO with SAML2. For extra details see the default settings in `ssoauth.app_settings.defaults`. You might also need SSL, try using `nginx` for it.
    
    Art's avatar
    Art committed
    ```python
    
    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
    
    #### 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)),
    ```
    
    Art's avatar
    Art committed
      - Adjust the path if needed
      - Optional argument `already_authenticated_403=True` is used to avoid redirect loops (e.g. those caused by `django.contrib.admin`). You can also use `already_authenticated_redirect="url-name"`.
    
    Art's avatar
    Art committed
    
    #### Regarding Logging Out
    
    
    Art's avatar
    Art committed
    After logging out locally, user will be redirected to one of the following (in this order):
    
    Art's avatar
    Art committed
      - `?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")`
    
    Art's avatar
    Art committed
      - Default: redirect to the view that asks users what to do next. You might want to override template `ssoauth/logged_out_locally.html`
    
    #### Advanced Logging Out and SLO
    
    If unsure, ignore this section.
    
    **SLO**: SLO/SLS is disabled by default. If you want to try your luck with SLO you should add to your project settings `SP_SLS_ENABLED = True`
    
    Art's avatar
    Art committed
    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.
    
    Art's avatar
    Art committed
    
    
    #### Groups and Permissions
    
    Art's avatar
    Art committed
    With `ssoauth` the only way to assign permissions is with groups:
    
    Art's avatar
    Art committed
      - when user logs in, `ssoauth` receives group names from the IDP, or fetches them from the `hsh` app if it's present
      - if your project has local groups (`django.contrib.auth` `Group`) with exactly the same names, these groups are assigned to the user
      - all other groups and permissions are removed from the user for security reasons (better don't workaround this behaviour in your project)
    
    Art's avatar
    Art committed
    
    You can predefine some groups in  project settings (see `ssoauth` default config for details). These predefined groups will be created automatically (when migrating). For example, a superuser group:
    
    ```python
    PREDEFINED_GROUPS = {
        "my_project_superusers": [ssoauth.SUPERUSER_PERM_CODENAME],
    }
    ```
    
    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
    
    Art's avatar
    Art committed
    """ settings.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
    
    
    Art's avatar
    Art committed
    - if you don't have a cert yet you can create one (and it doesn't need to be signed to use for SAML2 encryption):
    ```bash
    openssl req -newkey rsa:16384 -x509 -days 3650 -nodes -out sp.pem -keyout sp.key
    ```
    
    Art's avatar
    Art committed
    - 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
    
    
    
    Art's avatar
    Art committed
    #### Add your SP to an IDP
    
    Art's avatar
    Art committed
    - Ask for assistance if not sure
    - The project should be running with production settings
    - Take the metadata of your project:
      - Run your project
      - The metadata of your SP is generated and shown on a web page, default path is `/saml2/meta`
    
    Art's avatar
    Art committed
      - Use Ctrl+U ("view source") to get the actual XML, otherwise your browser could mess it up
    
    Art's avatar
    Art committed
    - Shibboleth IDP:
      - `SSH` to the IDP and locate the Shibboleth directory, e.g. `/opt/shibboleth-idp/`
    
    Art's avatar
    Art committed
      - 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`
      - `systemctl restart tomcat8`
    
    Art's avatar
    Art committed
      - make sure the IDP still works, it can be super picky regarding its config files