diff --git a/README.rst b/README.rst index 46886541..a60c841d 100644 --- a/README.rst +++ b/README.rst @@ -29,6 +29,8 @@ openwisp-radius :target: https://pypi.org/project/black/ :alt: code style: black +--- + **Need a quick overview?** `Try the OpenWISP Demo `_. **OpenWISP RADIUS** provides a web interface to a `freeradius `_ database, @@ -45,11 +47,35 @@ It can be used as a standalone application or integrated with the rest of `OpenW It can also be used as a `base system or framework on top of which custom tailored solutions can be built `_. -- `Documentation `_ -- `Change log `_ -- `Support `_ -- `Issue Tracker `_ -- `License `_ +Documentation +------------- + +- `Usage documentation `_ +- `Developer documentation + `_ + +Contributing +------------ + +Please refer to the `OpenWISP contributing guidelines +`_. + +Changelog +--------- + +See `CHANGES +`_. + +License +------- + +See `LICENSE +`_. + +Support +------- + +See `OpenWISP Support Channels `_. .. image:: https://raw.githubusercontent.com/openwisp/openwisp2-docs/master/assets/design/openwisp-logo-black.svg :target: http://openwisp.org diff --git a/docs/deploy/freeradius.rst b/docs/deploy/freeradius.rst new file mode 100644 index 00000000..cf58560a --- /dev/null +++ b/docs/deploy/freeradius.rst @@ -0,0 +1,515 @@ +Freeradius Setup for Captive Portal authentication +================================================== + +This guide explains how to install and configure `freeradius 3 +`_ in order to make it work with OpenWISP RADIUS +for Captive Portal authentication. + +The guide is written for debian based systems, other linux distributions +can work as well but the name of packages and files may be different. + +Widely used solutions used with OpenWISP RADIUS are PfSense and +Coova-Chilli, but other solutions can be used as well. + +.. note:: + + Before users can authenticate through a captive portal, they will most + likely need to sign up through a web page, or alternatively, they will + need to perform social login or some other kind of Single Sign On + (SSO). + + The :doc:`OpenWISP WiFi Login Pages ` web app + is an open source solution which integrates with OpenWISP RADIUS to + provide features like self user registration, social login, SSO/SAML + login, SMS verification, simple username & password login using the + :ref:`radius_user_token` method. + + For more information see: :doc:`OpenWISP WiFi Login Pages + ` + +How to install freeradius 3 +--------------------------- + +First of all, become root: + +.. code-block:: shell + + sudo -s + +In order to **install a recent version of FreeRADIUS**, we recommend using +the `freeradius packages provided by NetworkRADIUS +`_. + +After having updated the APT sources list to pull the NetworkRADIUS +packages, let's proceed to update the list of available packages: + +.. code-block:: shell + + apt update + +These packages are always needed: + +.. code-block:: shell + + apt install freeradius freeradius-rest + +If you use MySQL: + +.. code-block:: shell + + apt install freeradius-mysql + +If you use PostgreSQL: + +.. code-block:: shell + + apt install freeradius-postgresql + +.. warning:: + + You have to install and configure an SQL database like PostgreSQL, + MySQL (SQLite can also work, but we won't treat it here) and make sure + both OpenWISP RADIUS and Freeradius point to it. + + The steps outlined above may not be sufficient to get the DB of your + choice to run, please consult the documentation of your database of + choice for more information on how to get it to run properly. + + In the rest of this document we will mention PostgreSQL often because + that is the database generally preferred by the Django community. + +Configuring Freeradius 3 +------------------------ + +For a complete reference on how to configure freeradius please read the +`Freeradius wiki, configuration files +`_ and their +`configuration tutorial `_. + +.. note:: + + The path to freeradius configuration could be different on your + system. This article use the ``/etc/freeradius/`` directory that ships + with recent debian distributions and its derivatives + +Refer to the `mods-available documentation +`_ +for the available configuration values. + +Enable the configured modules +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +First of all enable the ``rest`` and optionally the ``sql`` module: + +.. code-block:: shell + + ln -s /etc/freeradius/mods-available/rest /etc/freeradius/mods-enabled/rest + # optional + ln -s /etc/freeradius/mods-available/sql /etc/freeradius/mods-enabled/sql + +.. _radius_configure_rest_module: + +Configure the REST module +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Configure the rest module by editing the file +``/etc/freeradius/mods-enabled/rest``, substituting ```` with your +django project's URL, (for example, if you are testing a development +environment, the URL could be ``http://127.0.0.1:8000``, otherwise in +production could be something like ``https://openwisp2.mydomain.org``)- + +.. warning:: + + Remember you need to add your freeradius server IP address in + `openwisp freeradius allowed hosts settings + <../user/settings.html#openwisp-radius-freeradius-allowed-hosts>`_. If + the freeradius server IP is not in allowed hosts, all requests to + openwisp radius API will return ``403``. + +Refer to the `rest module documentation +`_ +for the available configuration values. + +.. code-block:: ini + + # /etc/freeradius/mods-enabled/rest + + connect_uri = "" + + authorize { + uri = "${..connect_uri}/api/v1/freeradius/authorize/" + method = 'post' + body = 'json' + data = '{"username": "%{User-Name}", "password": "%{User-Password}"}' + tls = ${..tls} + } + + # this section can be left empty + authenticate {} + + post-auth { + uri = "${..connect_uri}/api/v1/freeradius/postauth/" + method = 'post' + body = 'json' + data = '{"username": "%{User-Name}", "password": "%{User-Password}", "reply": "%{reply:Packet-Type}", "called_station_id": "%{Called-Station-ID}", "calling_station_id": "%{Calling-Station-ID}"}' + tls = ${..tls} + } + + accounting { + uri = "${..connect_uri}/api/v1/freeradius/accounting/" + method = 'post' + body = 'json' + data = '{"status_type": "%{Acct-Status-Type}", "session_id": "%{Acct-Session-Id}", "unique_id": "%{Acct-Unique-Session-Id}", "username": "%{User-Name}", "realm": "%{Realm}", "nas_ip_address": "%{NAS-IP-Address}", "nas_port_id": "%{NAS-Port}", "nas_port_type": "%{NAS-Port-Type}", "session_time": "%{Acct-Session-Time}", "authentication": "%{Acct-Authentic}", "input_octets": "%{Acct-Input-Octets}", "output_octets": "%{Acct-Output-Octets}", "called_station_id": "%{Called-Station-Id}", "calling_station_id": "%{Calling-Station-Id}", "terminate_cause": "%{Acct-Terminate-Cause}", "service_type": "%{Service-Type}", "framed_protocol": "%{Framed-Protocol}", "framed_ip_address": "%{Framed-IP-Address}"}' + tls = ${..tls} + } + +Configure the SQL module +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. note:: + + The ``sql`` module is not extremely needed but we treat it here since + it can be useful to implement custom behavior, moreover we treat it in + this document also to show that OpenWISP RADIUS can integrate itself + with other widely used FreeRADIUS modules. + +Once you have configured properly an SQL server, e.g. PostgreSQL:, and you +can connect with a username and password edit the file +``/etc/freeradius/mods-available/sql`` to configure Freeradius to use the +relational database. + +Change the configuration for ``driver``, ``dialect``, ``server``, +``port``, ``login``, ``password``, ``radius_db`` as you need to fit your +SQL server configuration. + +Refer to the `sql module documentation +`_ for +the available configuration values. + +Example configuration using the PostgreSQL database: + +.. code-block:: ini + + # /etc/freeradius/mods-available/sql + + driver = "rlm_sql_postgresql" + dialect = "postgresql" + + # Connection info: + server = "localhost" + port = 5432 + login = "" + password = "" + radius_db = "radius" + +.. _freeradius_site: + +Configure the site +~~~~~~~~~~~~~~~~~~ + +This section explains how to configure the FreeRADIUS site. + +Please refer to :ref:`radius_freeradius_api_authentication` to understand +the different possibilities with which FreeRADIUS can authenticate +requests going to OpenWISP RADIUS so that OpenWISP RADIUS knows to which +organization each request belongs. + +If you are **not** using the method described in :ref:`radius_user_token`, +you have to do the following: + +- create one FreeRADIUS site for each organization +- uncomment the line which starts with ``# api_token_header`` +- substitute the occurrences of ```` and + ```` with the UUID & RADIUS API token of each + organization, refer to the section :ref:`radius_organization_uuid_token` + for finding these values. + +If you are deploying a captive portal setup and can use the RADIUS User +Token method, you can get away with having only one freeradius site for +all the organizations and can simply copy the configuration shown below. + +.. code-block:: ini + + # /etc/freeradius/sites-enabled/default + # Remove `#` symbol from the line to uncomment it + + server default { + # if you are not using Radius Token authentication method, please uncomment + # and set the values for & + # api_token_header = "Authorization: Bearer " + + authorize { + # if you are not using Radius Token authentication method, please uncomment the following + # update control { &REST-HTTP-Header += "${...api_token_header}" } + rest + } + + # this section can be left empty + authenticate {} + + post-auth { + # if you are not using Radius Token authentication method, please uncomment the following + # update control { &REST-HTTP-Header += "${...api_token_header}" } + rest + + Post-Auth-Type REJECT { + # if you are not using Radius Token authentication method, please uncomment the following + # update control { &REST-HTTP-Header += "${....api_token_header}" } + rest + } + } + + accounting { + # if you are not using Radius Token authentication method, please uncomment the following + # update control { &REST-HTTP-Header += "${...api_token_header}" } + rest + } + } + +Please also ensure that ``acct_unique`` is present in the +``pre-accounting`` section: + +.. code-block:: ini + + preacct { + # ... + acct_unique + # ... + } + +.. _radius_restart_freeradius: + +Restart freeradius to make the configuration effective +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Restart freeradius to load the new configuration: + +.. code-block:: shell + + service freeradius restart + # alternatively if you are using systemd + systemctl restart freeradius + +In case of errors you can run `freeradius in debug mode +`_ by running ``freeradius +-X`` in order to find out the reason of the failure. + +**A common problem, especially during development and testing, is that the +openwisp-radius application may not be running**, in that case you can +find out how to run the django development server in the +:doc:`../developer/installation` section. + +Also make sure that this server runs on the port specified in +``/etc/freeradius/mods-enabled/rest``. + +You may also want to take a look at the `Freeradius documentation +`_ for further information that is +freeradius specific. + +Reconfigure the development environment using PostgreSQL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You'll have to reconfigure the development environment as well before +being able to use openwisp-radius for managing the freeradius databases. + +If you have installed for development, create a file +``tests/local_settings.py`` and add the following code to configure the +database: + +.. code-block:: python + + # openwisp-radius/tests/local_settings.py + DATABASES = { + "default": { + "ENGINE": "django.db.backends.postgresql_psycopg2", + "NAME": "", + "USER": "", + "PASSWORD": "", + "HOST": "127.0.0.1", + "PORT": "5432", + }, + } + +Make sure the database by the name ```` is created and also the +role ```` with ```` as password. + +Using Radius Checks for Authorization Information +------------------------------------------------- + +Traditionally, when using an SQL backend with Freeradius, user +authorization information such as User-Name and `"known good" +`_ password can be stored +using the *radcheck* table provided by Freeradius' default SQL schema. + +OpenWISP RADIUS instead uses the FreeRADIUS `rlm_rest +`_ +module in order to take advantage of the built in user management and +authentication capabilities of Django (for more information about these +topics see :ref:`radius_configure_rest_module` and `User authentication in +Django `_). + +When migrating from existing FreeRADIUS deployments or in cases where it +is preferred to use the FreeRADIUS *radcheck* table for storing user +credentials it is possible to utilize `rlm_sql +`_ in parallel with (or +instead of) `rlm_rest +`_ +for authorization. + +.. note:: + + Bypassing the REST API of openwisp-radius means that you will have to + manually create the radius check entries for each user you want to + authenticate with FreeRADIUS. + +Configuration +~~~~~~~~~~~~~ + +To configure support for accessing user credentials with Radius Checks +ensure the ``authorize`` section of your site as follows contains the +``sql`` module: + +.. code-block:: ini + + # /etc/freeradius/sites-available/default + + authorize { + # ... + sql # <-- the sql module + # ... + } + +.. _radius_debugging: + +Debugging +--------- + +In this section we will explain how to debug your freeradius instance. + +Start freeradius in debug mode +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When debugging we suggest you to open up a dedicated terminal window to +run freeradius in debug mode: + +.. code-block:: shell + + # we need to stop the main freeradius process first + service freeradius stop + # alternatively if you are using systemd + systemctl stop freeradius + # launch freeradius in debug mode + freeradius -X + +Testing authentication and authorization +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can do this with ``radtest``: + +.. code-block:: shell + + # radtest 10 + radtest admin admin localhost 10 testing123 + +A successful authentication will return similar output: + +.. code-block:: text + + Sent Access-Request Id 215 from 0.0.0.0:34869 to 127.0.0.1:1812 length 75 + User-Name = "admin" + User-Password = "admin" + NAS-IP-Address = 127.0.0.1 + NAS-Port = 10 + Message-Authenticator = 0x00 + Cleartext-Password = "admin" + Received Access-Accept Id 215 from 127.0.0.1:1812 to 0.0.0.0:0 length 20 + +While an unsuccessful one will look like the following: + +.. code-block:: text + + Sent Access-Request Id 85 from 0.0.0.0:51665 to 127.0.0.1:1812 length 73 + User-Name = "foo" + User-Password = "bar" + NAS-IP-Address = 127.0.0.1 + NAS-Port = 10 + Message-Authenticator = 0x00 + Cleartext-Password = "bar" + Received Access-Reject Id 85 from 127.0.0.1:1812 to 0.0.0.0:0 length 20 + (0) -: Expected Access-Accept got Access-Reject + +Alternatively, you can use ``radclient`` which allows more complex tests; +in the following example we show how to test an authentication request +which includes ``Called-Station-ID`` and ``Calling-Station-ID``: + +.. code-block:: shell + + user="foo" + pass="bar" + called="00-11-22-33-44-55:localhost" + calling="00:11:22:33:44:55" + request="User-Name=$user,User-Password=$pass,Called-Station-ID=$called,Calling-Station-ID=$calling" + echo $request | radclient localhost auth testing123 + +Testing accounting +~~~~~~~~~~~~~~~~~~ + +You can do this with ``radclient``, but first of all you will have to +create a text file like the following one: + +.. code-block:: text + + # /tmp/accounting.txt + + Acct-Session-Id = "35000006" + User-Name = "jim" + NAS-IP-Address = 172.16.64.91 + NAS-Port = 1 + NAS-Port-Type = Async + Acct-Status-Type = Interim-Update + Acct-Authentic = RADIUS + Service-Type = Login-User + Login-Service = Telnet + Login-IP-Host = 172.16.64.25 + Acct-Delay-Time = 0 + Acct-Session-Time = 261 + Acct-Input-Octets = 9900909 + Acct-Output-Octets = 10101010101 + Called-Station-Id = 00-27-22-F3-FA-F1:hostname + Calling-Station-Id = 5c:7d:c1:72:a7:3b + +Then you can call ``radclient``: + +.. code-block:: shell + + radclient -f /tmp/accounting.txt -x 127.0.0.1 acct testing123 + +You should get the following output: + +.. code-block:: text + + Sent Accounting-Request Id 83 from 0.0.0.0:51698 to 127.0.0.1:1813 length 154 + Acct-Session-Id = "35000006" + User-Name = "jim" + NAS-IP-Address = 172.16.64.91 + NAS-Port = 1 + NAS-Port-Type = Async + Acct-Status-Type = Interim-Update + Acct-Authentic = RADIUS + Service-Type = Login-User + Login-Service = Telnet + Login-IP-Host = 172.16.64.25 + Acct-Delay-Time = 0 + Acct-Session-Time = 261 + Acct-Input-Octets = 9900909 + Acct-Output-Octets = 1511075509 + Called-Station-Id = "00-27-22-F3-FA-F1:hostname" + Calling-Station-Id = "5c:7d:c1:72:a7:3b" + Received Accounting-Response Id 83 from 127.0.0.1:1813 to 0.0.0.0:0 length 20 + +Customizing your configuration +------------------------------ + +You can further customize your freeradius configuration and exploit the +many features of freeradius but you will need to test how your +configuration plays with *openwisp-radius*. diff --git a/docs/deploy/freeradius_wpa_enterprise.rst b/docs/deploy/freeradius_wpa_enterprise.rst new file mode 100644 index 00000000..2bde4aaf --- /dev/null +++ b/docs/deploy/freeradius_wpa_enterprise.rst @@ -0,0 +1,274 @@ +Freeradius Setup for WPA Enterprise (EAP-TTLS-PAP) authentication +================================================================= + +This guide explains how to install and configure `freeradius 3 +`_ in order to make it work with OpenWISP RADIUS +for WPA Enterprise EAP-TTLS-PAP authentication. + +The setup will allow users to authenticate via WiFi WPA Enterprise +networks using their personal username and password of their django user +accounts. Users can either be created manually via the admin interface, +:doc:`generated <../user/generating_users>`, :doc:`imported from CSV +<../user/importing_users>`, or can self register through a web page which +makes use of the :ref:`registration REST API ` +(like :doc:`OpenWISP WiFi Login Pages `). + +Prerequisites +------------- + +Execute the steps explained in the following sections of the +:doc:`freeradius guide for captive portal authentication `: + + - How to install freeradius 3 + - Enable the configured modules + - Configure the REST module + +Then proceed with the rest of the document. + +Freeradius configuration +------------------------ + +.. _radius_freeradius_site_wpa_enterprise: + +Configure the sites +~~~~~~~~~~~~~~~~~~~ + +Main sites +++++++++++ + +In this scenario it is necessary to set up one FreeRADIUS site for each +organization you want to support, each FreeRADIUS instance will therefore +need two dedicated ports, one for authentication and one for accounting +and a related inner tunnel configuration. + +Let's create the site for an hypotethical organization called org-A. + +Don't forget to substitute the occurrences of ```` and +```` with the UUID & Radius API token of each +organization, refer to the section :ref:`radius_organization_uuid_token` +for finding these values. + +.. code-block:: ini + + # /etc/freeradius/sites-enabled/org_a + + server org_a { + listen { + type = auth + ipaddr = * + # ensure each org has its own port + port = 1812 + # adjust these as needed + limit { + max_connections = 16 + lifetime = 0 + idle_timeout = 30 + } + } + + listen { + ipaddr = * + # ensure each org has its own port + port = 1813 + type = acct + limit {} + } + + # IPv6 configuration skipped for brevity + # consult the freeradius default configuration if you need + # to add the IPv6 configuration + + # Substitute the following variables with + # the organization UUID and RADIUS API Token + api_token_header = "Authorization: Bearer " + + authorize { + eap-org_a { + ok = return + } + + update control { &REST-HTTP-Header += "${...api_token_header}" } + rest + } + + authenticate { + Auth-Type eap-org_a { + eap-org_a + } + } + + post-auth { + update control { &REST-HTTP-Header += "${...api_token_header}" } + rest + + Post-Auth-Type REJECT { + update control { &REST-HTTP-Header += "${....api_token_header}" } + rest + } + } + + accounting { + update control { &REST-HTTP-Header += "${...api_token_header}" } + rest + } + } + +Please also ensure that ``acct_unique`` is present in the +``pre-accounting`` section: + +.. code-block:: ini + + preacct { + # ... + acct_unique + # ... + } + +Inner tunnels ++++++++++++++ + +You will need to set up one inner tunnel for each organization too. + +Following the example for a hypothetical organization named org-A: + +.. code-block:: ini + + # /etc/freeradius/sites-enabled/inner-tunnel + + server inner-tunnel_org_a { + listen { + ipaddr = 127.0.0.1 + # each org will need a dedicated port for their inner tunnel + port = 18120 + type = auth + } + + api_token_header = "Authorization: Bearer " + + authorize { + filter_username + update control { &REST-HTTP-Header += "${...api_token_header}" } + rest + + eap-org_a { + ok = return + } + + expiration + logintime + + pap + } + + authenticate { + Auth-Type PAP { + pap + } + + Auth-Type CHAP { + chap + } + + Auth-Type MS-CHAP { + mschap + } + eap-org_a + } + + session {} + + post-auth { + } + + pre-proxy {} + post-proxy { + eap-org_a + } + } + +Configure the EAP modules +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. note:: + + Keep in mind these are basic sample configurations, once you get it + working feel free to tweak it to make it more secure and fully + featured. + +You will need to set up one EAP module instance for each organization too. + +Following the example for a hypotetical organization named org-A: + +.. code-block:: ini + + eap eap-org_a { + default_eap_type = ttls + timer_expire = 60 + ignore_unknown_eap_types = no + cisco_accounting_username_bug = no + max_sessions = ${max_requests} + + tls-config tls-common { + # make sure to have a valid SSL certificate for production usage + private_key_password = whatever + private_key_file = /etc/ssl/private/ssl-cert-snakeoil.key + certificate_file = /etc/ssl/certs/ssl-cert-snakeoil.pem + ca_file = /etc/ssl/certs/ca-certificates.crt + dh_file = ${certdir}/dh + ca_path = ${cadir} + cipher_list = "DEFAULT" + cipher_server_preference = no + ecdh_curve = "prime256v1" + + cache { + enable = no + } + + ocsp { + enable = no + override_cert_url = yes + url = "http://127.0.0.1/ocsp/" + } + } + + ttls { + tls = tls-common + default_eap_type = pap + copy_request_to_tunnel = yes + use_tunneled_reply = yes + virtual_server = "inner-tunnel_org_a" + } + } + +Repeating the steps for more organizations +------------------------------------------ + +Let's say you don't have only the hypotetical org-A in your system but +more organizations, in that case you simply have to repeat the steps +explained in the previous sections, substituting the occurrences of org-A +with the names of the other organizations. + +So if you have an organization named ACME Systems, copy the files and +substitute the occurrences ``org_a`` with ``acme_systems``. + +Final steps +----------- + +Once the configurations are ready, you should :ref:`restart freeradius +` and :ref:`then test/troubleshoot/debug your +setup `. + +Implementing other EAP scenarios +-------------------------------- + +Implementing other setups like EAP-TLS requires additional development +effort. + +`OpenWISP Controller `_ +already supports x509 certificates, so it would be a matter of integrating +the `django-x509 `_ module into +OpenWISP RADIUS and then implement mechanisms for the users to securely +download their certificates. + +If you're interested in this feature, let us know via the `support +channels `_. diff --git a/docs/developer/extending.rst b/docs/developer/extending.rst new file mode 100644 index 00000000..cc10ad7f --- /dev/null +++ b/docs/developer/extending.rst @@ -0,0 +1,547 @@ +Extending OpenWISP RADIUS +========================= + +.. include:: ../partials/developer-docs.rst + +One of the core values of the OpenWISP project is `Software Reusability +`_, +for this reason *openwisp-radius* provides a set of base classes which can +be imported, extended and reused to create derivative apps. + +In order to implement your custom version of *openwisp-radius*, you need +to perform the steps described in this section. + +When in doubt, the code in the `test project +`_ +and the `sample app +`_ +will serve you as source of truth: just replicate and adapt that code to +get a basic derivative of *openwisp-radius* working. + +If you want to add new users fields, please follow the :doc:`tutorial to +extend the openwisp-users `. As an example, we +have extended *openwisp-users* to *sample_users* app and added a field +``social_security_number`` in the `sample_users/models.py +`_. + +.. important:: + + If you plan on using a customized version of this module, we suggest + to start with it since the beginning, because migrating your data from + the default module to your extended version may be time consuming. + +1. Initialize your custom module +-------------------------------- + +The first thing you need to do is to create a new django app which will +contain your custom version of *openwisp-radius*. + +A django app is nothing more than a `python package +`_ (a directory +of python scripts), in the following examples we'll call this django app +``myradius``, but you can name it how you want: + +.. code-block:: shell + + django-admin startapp myradius + +Keep in mind that the command mentioned above must be called from a +directory which is available in your `PYTHON_PATH +`_ so that +you can then import the result into your project. + +2. Install ``openwisp-radius`` +------------------------------ + +Install (and add to the requirement of your project) openwisp-radius: + +.. code-block:: + + pip install openwisp-radius + +3. Add your App to ``INSTALLED_APPS`` +------------------------------------- + +Now you need to add ``myradius`` to ``INSTALLED_APPS`` in your +``settings.py``, ensuring also that ``openwisp_radius`` has been removed: + +.. code-block:: python + + import os + + INSTALLED_APPS = [ + # ... other apps ... + # openwisp admin theme + "openwisp_utils.admin_theme", + # all-auth + "django.contrib.sites", + "allauth", + "allauth.account", + "allauth.socialaccount", + # admin + "django.contrib.admin", + # rest framework + "rest_framework", + "django_filters", + # registration + "rest_framework.authtoken", + "dj_rest_auth", + "dj_rest_auth.registration", + # social login + "allauth.socialaccount.providers.facebook", # optional, can be removed if social login is not needed + "allauth.socialaccount.providers.google", # optional, can be removed if social login is not needed + # SAML login + "djangosaml2", # optional, can be removed if SAML login is not needed + # openwisp + # 'myradius', <-- replace with your app-name here + "openwisp_users", + "private_storage", + "drf_yasg", + ] + + SITE_ID = 1 + MEDIA_ROOT = os.path.join(BASE_DIR, "media") + PRIVATE_STORAGE_ROOT = os.path.join(MEDIA_ROOT, "private") + + AUTHENTICATION_BACKENDS = ( + "openwisp_users.backends.UsersAuthenticationBackend", + "openwisp_radius.saml.backends.OpenwispRadiusSaml2Backend", # optional, can be removed if SAML login is not needed + ) + +4. Add ``EXTENDED_APPS`` +------------------------ + +Add the following to your ``settings.py``: + +.. code-block:: python + + EXTENDED_APPS = ("openwisp_radius",) + +5. Add ``openwisp_utils.staticfiles.DependencyFinder`` +------------------------------------------------------ + +Add ``openwisp_utils.staticfiles.DependencyFinder`` to +``STATICFILES_FINDERS`` in your ``settings.py``: + +.. code-block:: python + + STATICFILES_FINDERS = [ + "django.contrib.staticfiles.finders.FileSystemFinder", + "django.contrib.staticfiles.finders.AppDirectoriesFinder", + "openwisp_utils.staticfiles.DependencyFinder", + ] + +6. Add ``openwisp_utils.loaders.DependencyLoader`` +-------------------------------------------------- + +Add ``openwisp_utils.loaders.DependencyLoader`` to ``TEMPLATES`` in your +``settings.py``, but ensure it comes before +``django.template.loaders.app_directories.Loader``: + +.. code-block:: python + + TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "OPTIONS": { + "loaders": [ + "django.template.loaders.filesystem.Loader", + "openwisp_utils.loaders.DependencyLoader", + "django.template.loaders.app_directories.Loader", + ], + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + }, + } + ] + +7. Inherit the AppConfig class +------------------------------ + +Refer to the `sample_radius/apps.py +`_ +file in the sample app of the test project. + +You have to replicate and adapt that code in your project. + +For more information regarding the concept of ``AppConfig`` please refer +to the `"Applications" section in the django documentation +`_. + +8. Create your custom models +---------------------------- + +For the purpose of showing an example, we added a simple ``details`` field +to the `models of the sample app in the test project +`_. + +You can add fields in a similar way in your ``models.py`` file. + +For doubts regarding how to use, extend or develop models please refer to +the `"Models" section in the django documentation +`_. + +9. Add swapper configurations +----------------------------- + +Once you have created the models, add the following to your +``settings.py``: + +.. code-block:: python + + # Setting models for swapper module + OPENWISP_RADIUS_RADIUSREPLY_MODEL = "myradius.RadiusReply" + OPENWISP_RADIUS_RADIUSGROUPREPLY_MODEL = "myradius.RadiusGroupReply" + OPENWISP_RADIUS_RADIUSCHECK_MODEL = "myradius.RadiusCheck" + OPENWISP_RADIUS_RADIUSGROUPCHECK_MODEL = "myradius.RadiusGroupCheck" + OPENWISP_RADIUS_RADIUSACCOUNTING_MODEL = "myradius.RadiusAccounting" + OPENWISP_RADIUS_NAS_MODEL = "myradius.Nas" + OPENWISP_RADIUS_RADIUSUSERGROUP_MODEL = "myradius.RadiusUserGroup" + OPENWISP_RADIUS_RADIUSPOSTAUTH_MODEL = "myradius.RadiusPostAuth" + OPENWISP_RADIUS_RADIUSBATCH_MODEL = "myradius.RadiusBatch" + OPENWISP_RADIUS_RADIUSGROUP_MODEL = "myradius.RadiusGroup" + OPENWISP_RADIUS_RADIUSTOKEN_MODEL = "myradius.RadiusToken" + OPENWISP_RADIUS_PHONETOKEN_MODEL = "myradius.PhoneToken" + OPENWISP_RADIUS_ORGANIZATIONRADIUSSETTINGS_MODEL = ( + "myradius.OrganizationRadiusSettings" + ) + OPENWISP_RADIUS_REGISTEREDUSER_MODEL = "myradius.RegisteredUser" + + # You will need to change AUTH_USER_MODEL if you are extending openwisp_users + AUTH_USER_MODEL = "openwisp_users.User" + +Substitute ``myradius`` with the name you chose in step 1. + +10. Create database migrations +------------------------------ + +Copy the `migration files from the sample_radius's migration folder +`_. + +Now, create database migrations as per your custom application's +requirements: + +.. code-block:: shell + + ./manage.py makemigrations + +If you are starting with a fresh database, you can apply the migrations: + +.. code-block:: shell + + ./manage.py migrate + +However, if you want :ref:`migrate an existing freeradius database please +read the guide in the setup `. + +For more information, refer to the `"Migrations" section in the django +documentation +`_. + +11. Create the admin +-------------------- + +Refer to the `admin.py file of the sample app +`_. + +To introduce changes to the admin, you can do it in two main ways which +are described below. + +For more information regarding how the django admin works, or how it can +be customized, please refer to `"The django admin site" section in the +django documentation +`_. + +1. Monkey patching +~~~~~~~~~~~~~~~~~~ + +If the changes you need to add are relatively small, you can resort to +monkey patching. + +For example: + +.. code-block:: python + + from openwisp_radius.admin import ( + RadiusCheckAdmin, + RadiusReplyAdmin, + RadiusAccountingAdmin, + NasAdmin, + RadiusGroupAdmin, + RadiusUserGroupAdmin, + RadiusGroupCheckAdmin, + RadiusGroupReplyAdmin, + RadiusPostAuthAdmin, + RadiusBatchAdmin, + ) + + # NasAdmin.fields += ['example_field'] <-- Monkey patching changes example + +2. Inheriting admin classes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you need to introduce significant changes and/or you don't want to +resort to monkey patching, you can proceed as follows: + +.. code-block:: python + + from django.contrib import admin + from openwisp_radius.admin import ( + RadiusCheckAdmin as BaseRadiusCheckAdmin, + RadiusReplyAdmin as BaseRadiusReplyAdmin, + RadiusAccountingAdmin as BaseRadiusAccountingAdmin, + NasAdmin as BaseNasAdmin, + RadiusGroupAdmin as BaseRadiusGroupAdmin, + RadiusUserGroupAdmin as BaseRadiusUserGroupAdmin, + RadiusGroupCheckAdmin as BaseRadiusGroupCheckAdmin, + RadiusGroupReplyAdmin as BaseRadiusGroupReplyAdmin, + RadiusPostAuthAdmin as BaseRadiusPostAuthAdmin, + RadiusBatchAdmin as BaseRadiusBatchAdmin, + ) + from swapper import load_model + + Nas = load_model("openwisp_radius", "Nas") + RadiusAccounting = load_model("openwisp_radius", "RadiusAccounting") + RadiusBatch = load_model("openwisp_radius", "RadiusBatch") + RadiusCheck = load_model("openwisp_radius", "RadiusCheck") + RadiusGroup = load_model("openwisp_radius", "RadiusGroup") + RadiusPostAuth = load_model("openwisp_radius", "RadiusPostAuth") + RadiusReply = load_model("openwisp_radius", "RadiusReply") + PhoneToken = load_model("openwisp_radius", "PhoneToken") + RadiusGroupCheck = load_model("openwisp_radius", "RadiusGroupCheck") + RadiusGroupReply = load_model("openwisp_radius", "RadiusGroupReply") + RadiusUserGroup = load_model("openwisp_radius", "RadiusUserGroup") + OrganizationRadiusSettings = load_model( + "openwisp_radius", "OrganizationRadiusSettings" + ) + User = get_user_model() + + admin.site.unregister(RadiusCheck) + admin.site.unregister(RadiusReply) + admin.site.unregister(RadiusAccounting) + admin.site.unregister(Nas) + admin.site.unregister(RadiusGroup) + admin.site.unregister(RadiusUserGroup) + admin.site.unregister(RadiusGroupCheck) + admin.site.unregister(RadiusGroupReply) + admin.site.unregister(RadiusPostAuth) + admin.site.unregister(RadiusBatch) + + + @admin.register(RadiusCheck) + class RadiusCheckAdmin(BaseRadiusCheckAdmin): + pass + # add your changes here + + + @admin.register(RadiusReply) + class RadiusReplyAdmin(BaseRadiusReplyAdmin): + pass + # add your changes here + + + @admin.register(RadiusAccounting) + class RadiusAccountingAdmin(BaseRadiusAccountingAdmin): + pass + # add your changes here + + + @admin.register(Nas) + class NasAdmin(BaseNasAdmin): + pass + # add your changes here + + + @admin.register(RadiusGroup) + class RadiusGroupAdmin(BaseRadiusGroupAdmin): + pass + # add your changes here + + + @admin.register(RadiusUserGroup) + class RadiusUserGroupAdmin(BaseRadiusUserGroupAdmin): + pass + # add your changes here + + + @admin.register(RadiusGroupCheck) + class RadiusGroupCheckAdmin(BaseRadiusGroupCheckAdmin): + pass + # add your changes here + + + @admin.register(RadiusGroupReply) + class RadiusGroupReplyAdmin(BaseRadiusGroupReplyAdmin): + pass + # add your changes here + + + @admin.register(RadiusPostAuth) + class RadiusPostAuthAdmin(BaseRadiusPostAuthAdmin): + pass + # add your changes here + + + @admin.register(RadiusBatch) + class RadiusBatchAdmin(BaseRadiusBatchAdmin): + pass + # add your changes here + +12. Setup Freeradius API Allowed Hosts +-------------------------------------- + +Add allowed freeradius hosts in ``settings.py``: + +.. code-block:: python + + OPENWISP_RADIUS_FREERADIUS_ALLOWED_HOSTS = ["127.0.0.1"] + +Read more about :ref:`freeradius allowed hosts in settings page +`. + +13. Setup Periodic tasks +------------------------ + +Some periodic commands are required in production environments to enable +certain features and facilitate database cleanup: + +1. You need to create a `celery configuration file as it's created in + example file + `_. + +2. In the settings.py, `configure the CELERY_BEAT_SCHEDULE +`_. +Some celery tasks take an argument, for instance ``365`` is given here for +``delete_old_radacct`` in the example settings. These arguments are passed +to their respective management commands. More information about these +parameters can be found at the :doc:`management commands page +<../user/management_commands>`. + +3. Add the following in your settings.py file: + +.. code-block:: python + + CELERY_IMPORTS = ("openwisp_radius.tasks",) + +For more information about the usage of celery in django, please refer to +the `"First steps with Django" section in the celery documentation +`_. + +14. Create root URL configuration +--------------------------------- + +The root ``url.py`` file should have the following paths (please read the +comments): + +.. code-block:: python + + from openwisp_radius.urls import get_urls + + # Only imported when views are extended. + # from myradius.api.views import views as api_views + # from myradius.social.views import views as social_views + # from myradius.saml.views import views as saml_views + + urlpatterns = [ + # ... other urls in your project ... + path("admin/", admin.site.urls), + # openwisp-radius urls + path("accounts/", include("openwisp_users.accounts.urls")), + path("api/v1/", include("openwisp_utils.api.urls")), + # Use only when extending views (discussed below) + # path('', include((get_urls(api_views, social_views, saml_views), 'radius'), namespace='radius')), + # Remove when extending views + path("", include("openwisp_radius.urls", namespace="radius")), + ] + +For more information about URL configuration in django, please refer to +the `"URL dispatcher" section in the django documentation +`_. + +15. Import the automated tests +------------------------------ + +When developing a custom application based on this module, it's a good +idea to import and run the base tests too, so that you can be sure the +changes you're introducing are not breaking some of the existing features +of *openwisp-radius*. + +In case you need to add breaking changes, you can overwrite the tests +defined in the base classes to test your own behavior. + +See the `tests of the sample app +`_ +to find out how to do this. + +You can then run tests with: + +.. code-block:: shell + + # the --parallel flag is optional + ./manage.py test --parallel myradius + +Substitute ``myradius`` with the name you chose in step 1. + +Other base classes that can be inherited and extended +----------------------------------------------------- + +The following steps are not required and are intended for more advanced +customization. + +1. Extending the API Views +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The API view classes can be extended into other django applications as +well. Note that it is not required for extending *openwisp-radius* to your +app and this change is required only if you plan to make changes to the +API views. + +Create a view file as done in `API views.py +`_. + +Remember to use these views in root URL configurations in point 14. If you +want only extend the API views and not social views, you can use +``get_urls(api_views, None)`` to get social_views from *openwisp_radius*. + +For more information about django views, please refer to the `views +section in the django documentation +`_. + +2. Extending the Social Views +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The social view classes can be extended into other django applications as +well. Note that it is not required for extending *openwisp-radius* to your +app and this change is required only if you plan to make changes to the +social views. + +Create a view file as done in `social views.py +`_. + +Remember to use these views in root URL configurations in point 14. If you +want only extend the API views and not social views, you can use +``get_urls(api_views, None)`` to get social_views from *openwisp_radius*. + +3. Extending the SAML Views +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The SAML view classes can be extended into other django applications as +well. Note that it is not required for extending *openwisp-radius* to your +app and this change is required only if you plan to make changes to the +SAML views. + +Create a view file as done in `saml views.py +`_. + +Remember to use these views in root URL configurations in point 14. If you +want only extend the API views and social view but not SAML views, you can +use ``get_urls(api_views, social_views, None)`` to get saml_views from +*openwisp_radius*. + +For more information about django views, please refer to the `views +section in the django documentation +`_. diff --git a/docs/developer/index.rst b/docs/developer/index.rst new file mode 100644 index 00000000..99915fc9 --- /dev/null +++ b/docs/developer/index.rst @@ -0,0 +1,16 @@ +Developer Docs +============== + +.. include:: ../partials/developer-docs.rst + +.. toctree:: + :maxdepth: 2 + + ./installation.rst + ./utils.rst + ./extending.rst + +Other useful resources: + + - :doc:`../user/rest-api` + - :doc:`../user/settings` diff --git a/docs/developer/installation.rst b/docs/developer/installation.rst new file mode 100644 index 00000000..e8aaf526 --- /dev/null +++ b/docs/developer/installation.rst @@ -0,0 +1,161 @@ +Developer Installation Instructions +=================================== + +.. include:: ../partials/developer-docs.rst + +.. contents:: **Table of Contents**: + :depth: 2 + :local: + +Dependencies +------------ + +- Python >= 3.8 + +Installing for Development +-------------------------- + +Install the system dependencies: + +.. code-block:: shell + + sudo apt update + sudo apt install -y sqlite3 libsqlite3-dev libpq-dev + sudo apt install -y xmlsec1 + sudo apt install -y chromium-browser + +Fork and clone the forked repository: + +.. code-block:: shell + + git clone git://github.com//openwisp-radius + +Navigate into the cloned repository: + +.. code-block:: shell + + cd openwisp-radius/ + +Launch Redis: + +.. code-block:: shell + + docker-compose up -d redis + +Setup and activate a virtual-environment (we'll be using `virtualenv +`_): + +.. code-block:: shell + + python -m virtualenv env + source env/bin/activate + +Make sure that your base python packages are up to date before moving to +the next step: + +.. code-block:: shell + + pip install -U pip wheel setuptools + +Install development dependencies: + +.. code-block:: shell + + pip install -e .[saml,openvpn_status] + pip install -r requirements-test.txt + sudo npm install -g jshint stylelint + +Install WebDriver for Chromium for your browser version from +https://chromedriver.chromium.org/home and Extract ``chromedriver`` to one +of directories from your ``$PATH`` (example: ``~/.local/bin/``). + +Create database: + +.. code-block:: shell + + cd tests/ + ./manage.py migrate + ./manage.py createsuperuser + +Launch celery worker (for background jobs): + +.. code-block:: shell + + celery -A openwisp2 worker -l info + +Launch development server: + +.. code-block:: shell + + ./manage.py runserver + +You can access the admin interface at ``http://127.0.0.1:8000/admin/``. + +Run tests with: + +.. code-block:: shell + + ./runtests.py --parallel + +Run quality assurance tests with: + +.. code-block:: shell + + ./run-qa-checks + +Alternative Sources +------------------- + +Pypi +~~~~ + +To install the latest Pypi: + +.. code-block:: shell + + pip install openwisp-radius + +Github +~~~~~~ + +To install the latest development version tarball via HTTPs: + +.. code-block:: shell + + pip install https://github.com/openwisp/openwisp-radius/tarball/master + +Alternatively you can use the git protocol: + +.. code-block:: shell + + pip install -e git+git://github.com/openwisp/openwisp-radius#egg=openwisp_radius[saml,openvpn_status] + +.. _radius_migrate_existing_freeradius_db: + +Migrating an existing freeradius database +----------------------------------------- + +If you already have a freeradius 3 database with the default schema, you +should be able to use it with openwisp-radius (and extended apps) easily: + +1. first of all, back up your existing database; +2. configure django to connect to your existing database; +3. fake the first migration (which only replicates the default freeradius + schema) and then launch the rest of migrations normally, see the + examples below to see how to do this. + +.. code-block:: shell + + ./manage.py migrate --fake openwisp-radius 0001_initial_freeradius + ./manage.py migrate + +Troubleshooting Steps for Common Installation Issues +---------------------------------------------------- + +If you encounter any issue during installation, run: + +.. code-block:: shell + + pip install -e .[saml] -r requirements-test.txt + +instead of ``pip install -r requirements-test.txt`` diff --git a/docs/developer/utils.rst b/docs/developer/utils.rst new file mode 100644 index 00000000..8fd5de04 --- /dev/null +++ b/docs/developer/utils.rst @@ -0,0 +1,66 @@ +Code Utilities +============== + +.. include:: ../partials/developer-docs.rst + +.. contents:: **Table of Contents**: + :depth: 2 + :local: + +Signals +------- + +``radius_accounting_success`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Path**: ``openwisp_radius.signals.radius_accounting_success`` + +**Arguments**: + +- ``sender`` : ``AccountingView`` +- ``accounting_data`` (``dict``): accounting information +- ``view``: instance of ``AccountingView`` + +This signal is emitted every time the accounting REST API endpoint +completes successfully, just before the response is returned. + +The ``view`` argument can also be used to access the ``request`` object +i.e. ``view.request``. + +Captive portal mock views +------------------------- + +The development environment of openwisp-radius provides two URLs that mock +the behavior of a captive portal, these URLs can be used when testing +frontend applications like :doc:`OpenWISP WiFi Login Pages +` during development. + +.. note:: + + These views are meant to be used just for development and testing. + +Captive Portal Login Mock View +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- **URL**: ``http://localhost:8000/captive-portal-mock/login/``. +- **POST fields**: ``auth_pass`` or ``password``. + +This view handles the captive portal login process by first checking for +either an ``auth_pass`` or ``password`` in the POST request data. It then +attempts to find a corresponding ``RadiusToken`` instance where the key +matches the provided value. If a matching token is found and there are no +active sessions (i.e., no open ``RadiusAccounting`` records), then it +creates a new radius session for the user. If successful, the user is +considered logged in. + +Captive Portal Logout Mock View +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- **URL**: ``http://localhost:8000/captive-portal-mock/logout/``. +- **POST fields**: ``logout_id``. + +This view looks for an entry in the ``radacct`` table where ``session_id`` +matches the value passed in the ``logout_id`` POST field. If such an entry +is found, the view makes a ``POST`` request to the accounting view to mark +the session as terminated, using ``User-Request`` as the termination +cause. diff --git a/docs/images/add_users_csv.gif b/docs/images/add_users_csv.gif new file mode 100644 index 00000000..af7caf5e Binary files /dev/null and b/docs/images/add_users_csv.gif differ diff --git a/docs/images/add_users_prefix.gif b/docs/images/add_users_prefix.gif new file mode 100644 index 00000000..40385bbc Binary files /dev/null and b/docs/images/add_users_prefix.gif differ diff --git a/docs/images/download_user_credentials_button.png b/docs/images/download_user_credentials_button.png new file mode 100644 index 00000000..61b241fc Binary files /dev/null and b/docs/images/download_user_credentials_button.png differ diff --git a/docs/images/drf_api_interface.png b/docs/images/drf_api_interface.png new file mode 100644 index 00000000..9079fbdb Binary files /dev/null and b/docs/images/drf_api_interface.png differ diff --git a/docs/images/freeradius_allowed_hosts.png b/docs/images/freeradius_allowed_hosts.png new file mode 100644 index 00000000..958a9933 Binary files /dev/null and b/docs/images/freeradius_allowed_hosts.png differ diff --git a/docs/images/mac-address-roaming.png b/docs/images/mac-address-roaming.png new file mode 100644 index 00000000..5c847df1 Binary files /dev/null and b/docs/images/mac-address-roaming.png differ diff --git a/docs/images/optional_fields.png b/docs/images/optional_fields.png new file mode 100644 index 00000000..6779d459 Binary files /dev/null and b/docs/images/optional_fields.png differ diff --git a/docs/images/org_uuid.png b/docs/images/org_uuid.png new file mode 100644 index 00000000..ee7ce591 Binary files /dev/null and b/docs/images/org_uuid.png differ diff --git a/docs/images/organization_coa_enabled.png b/docs/images/organization_coa_enabled.png new file mode 100644 index 00000000..067f110b Binary files /dev/null and b/docs/images/organization_coa_enabled.png differ diff --git a/docs/images/organization_registration_setting.png b/docs/images/organization_registration_setting.png new file mode 100644 index 00000000..fa319c0a Binary files /dev/null and b/docs/images/organization_registration_setting.png differ diff --git a/docs/images/organization_saml_setting.png b/docs/images/organization_saml_setting.png new file mode 100644 index 00000000..e4fa7922 Binary files /dev/null and b/docs/images/organization_saml_setting.png differ diff --git a/docs/images/organization_sms_verification_setting.png b/docs/images/organization_sms_verification_setting.png new file mode 100644 index 00000000..1b19bf57 Binary files /dev/null and b/docs/images/organization_sms_verification_setting.png differ diff --git a/docs/images/organization_social_login_setting.png b/docs/images/organization_social_login_setting.png new file mode 100644 index 00000000..44f3d448 Binary files /dev/null and b/docs/images/organization_social_login_setting.png differ diff --git a/docs/images/pdf_of_user_list.png b/docs/images/pdf_of_user_list.png new file mode 100644 index 00000000..648afbcb Binary files /dev/null and b/docs/images/pdf_of_user_list.png differ diff --git a/docs/images/swagger_api.png b/docs/images/swagger_api.png new file mode 100644 index 00000000..b40dc65b Binary files /dev/null and b/docs/images/swagger_api.png differ diff --git a/docs/images/token.png b/docs/images/token.png new file mode 100644 index 00000000..60fe9a0c Binary files /dev/null and b/docs/images/token.png differ diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..73d02ecc --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,70 @@ +RADIUS +====== + +.. seealso:: + + **Source code**: `github.com/openwisp/openwisp-radius + `_. + +OpenWISP RADIUS is available since OpenWISP 22.05 and provides many +features aimed at public WiFi services. + +For a full introduction please refer to :doc:`user/intro`. + +The following diagram illustrates the role of the RADIUS module within the +OpenWISP architecture. + +.. figure:: /images/architecture/v2/architecture-v2-openwisp-radius.png + :target: ../_images/architecture-v2-openwisp-radius.png + :align: center + :alt: OpenWISP Architecture: Radius module + + **OpenWISP Architecture: highlighted radius module** + +.. important:: + + For an enhanced viewing experience, open the image above in a new + browser tab. + + Refer to :doc:`/general/architecture` for more information. + +.. toctree:: + :caption: RADIUS Module Usage Docs + :maxdepth: 1 + + user/intro.rst + user/registration.rst + user/generating_users.rst + user/importing_users.rst + user/social_login.rst + user/saml.rst + user/enforcing_limits.rst + user/change_of_authorization.rst + user/management_commands.rst + user/rest-api.rst + user/settings.rst + +.. toctree:: + :caption: RADIUS Module Developer Docs + :maxdepth: 2 + + Developer Docs Index + +Deploy instructions +------------------- + +See :ref:`Enabling the RADIUS module on the OpenWISP ansible role +documentation `. + +Alternatively you can set it up manually by following these guides: + +.. toctree:: + :maxdepth: 1 + + deploy/freeradius + deploy/freeradius_wpa_enterprise + +This module is also available in :doc:`docker-openwisp ` +although its usage is not recommended for production usage yet, unless the +reader is willing to invest effort in adapting the docker images and +configurations to overcome any roadblocks encountered. diff --git a/docs/partials/developer-docs.rst b/docs/partials/developer-docs.rst new file mode 100644 index 00000000..39d7fc24 --- /dev/null +++ b/docs/partials/developer-docs.rst @@ -0,0 +1,9 @@ +.. note:: + + This page is for developers who want to customize or extend OpenWISP + RADIUS, whether for bug fixes, new features, or contributions. + + For user guides and general information, please see: + + - :doc:`General OpenWISP Quickstart ` + - :doc:`OpenWISP RADIUS User Docs ` diff --git a/docs/user/change_of_authorization.rst b/docs/user/change_of_authorization.rst new file mode 100644 index 00000000..771a6f81 --- /dev/null +++ b/docs/user/change_of_authorization.rst @@ -0,0 +1,56 @@ +.. _change_of_authorization: + +Change of Authorization (CoA) +============================= + +.. important:: + + The *Change of Authorization (CoA)* is disabled by default. + + In order to enable this feature you have it enable it via :ref:`global + setting or from the admin interface `. + +The openwisp-radius module supports the Change of Authorization (CoA) +specification of the RADIUS protocol described in `RFC 5176 +`_. + +Whenever the *RADIUS Group* of a user is changed, openwisp-radius updates +the NAS with the user's latest RADIUS Attributes. This is achieved by +sending CoA RADIUS packet to NAS for all open RADIUS sessions of the user. +This allows enforcing RADIUS limits without requiring the user to +re-authenticate with the NAS. + +The CoA RADIUS packet contains the RADIUS Attributes defined in the new +*RADIUS Group* of the user. If the new *RADIUS Group* does not specify any +attributes, the CoA RADIUS packet will unset the attributes set by the +previous *RADIUS Group*. + +Consider the following example with two *RADIUS Groups*: + +===================== =================================================== +**RADIUS Group Name** **RADIUS Group Checks** +**users** ============================= ================ + **Attribute** **Value** + ``Max-Daily-Session-Traffic`` ``:=3000000000`` + ``Max-Daily-Session`` ``:=10800`` + ============================= ================ +**power-users** *Note: This group intentionally does not define any + limits.* +===================== =================================================== + +A user, Jane is assigned ``users`` *RADIUS Group* and is currently using +the network, i.e. has an open RADIUS session. The administrator of the +system decided to upgrade the *RADIUS Group* of Jane to ``power-users``, +allowing Jane to use the network without any limits. Without CoA, Jane +will have to logout of the captive portal (NAS) and log-in again to browse +the network without any limits. But when CoA is enabled in +openwisp-radius, openwisp-radius will update the NAS with the limits +defined in Jane's new RADIUS Group. In this case, openwisp-radius will +tell the NAS to unset the limits that were configured by the previous +RADIUS Group. + +If the system administrators later decided to downgrade the *RADIUS Group* +of Jane to ``users``, hence enforcing limits to the usage of the network, +openwisp-radius will update the NAS with the limits defined for the +``users`` group for all active RADIUS sessions if CoA is enabled in +openwisp-radius. diff --git a/docs/user/enforcing_limits.rst b/docs/user/enforcing_limits.rst new file mode 100644 index 00000000..d1856c6a --- /dev/null +++ b/docs/user/enforcing_limits.rst @@ -0,0 +1,180 @@ +Enforcing Session Limits +======================== + +The default freeradius schema does not include a table where groups are +stored, but openwisp-radius adds a model called ``RadiusGroup`` and alters +the default freeradius schema to add some optional foreign-keys from other +tables like: + +- ``radgroupcheck`` +- ``radgroupreply`` +- ``radusergroup`` + +These foreign keys make it easier to automate many synchronization and +integrity checks between the ``RadiusGroup`` table and its related tables +but they are not strictly mandatory from the database point of view: their +value can be ``NULL`` and their presence and validation is handled at +application level, this makes it easy to use existing freeradius +databases. + +For each group, checks and replies can be specified directly in the edit +page of a Radius Group (``admin`` > ``groups`` > ``add group`` or ``change +group``). + +Default Groups +-------------- + +Some groups are created automatically by **openwisp-radius** during the +initial migrations: + +- ``users``: this is the default group which limits users sessions to 3 + hours and 300 MB (daily) +- ``power-users``: this group does not have any check, therefore users who + are members of this group won't be limited in any way + +You can customize the checks and the replies of these groups, as well as +create new groups according to your needs and preferences. + +**Note on the default group**: keep in mind that the group flagged as +default will by automatically assigned to new users, it cannot be deleted +nor it can be flagged as non-default: to set another group as default +simply check that group as the default one, save and **openwisp-radius** +will remove the default flag from the old default group. + +.. _radius_counters: + +How Limits are Enforced: Counters +--------------------------------- + +In Freeradius, this kind of feature is implemented with the +`rml_sqlcounter `_. + +The problem with this FreeRADIUS module is that it doesn't know about +OpenWISP, so it does not support multi-tenancy. This means that if +multiple organizations are using the OpenWISP instance, it's possible that +a user may be an end user of multiple organizations and hence have one +radius group assigned for each, but the sqlcounter module will not +understand the right group to choose when enforcing limits, with the +result that the enforcing of limits will not work as expected, unless one +FreeRADIUS site with different sqlcounter configurations is created for +each organization using the system, which is doable but cumbersome to +maintain. + +For the reasons explained above, an alternative counter feature has been +implemented in the authorize API endpoint of OpenWISP RADIUS. + +The default counters available are described below. + +``DailyCounter`` +~~~~~~~~~~~~~~~~ + +This counter is used to limit the amount of time users can use the network +every day. It works by checking whether the total session time of a user +during a specific day is below the value indicated in the +``Max-Daily-Session`` group check attribute, sending the remaining session +time with a ``Session-Timeout`` reply message or rejecting the +authorization if the limit has been passed. + +.. _radius_daily_traffic_counter: + +``DailyTrafficCounter`` +~~~~~~~~~~~~~~~~~~~~~~~ + +This counter is used to limit the amount of traffic users can consume +every day. It works by checking whether the total amount of download plus +upload octets (bytes consumed) is below the value indicated in the +``Max-Daily-Session-Traffic`` group check attribute, sending the remaining +octets with a reply message or rejecting the authorization if the limit +has been passed. + +The attributes used for the check and or the reply message are +configurable because it can differ from NAS to NAS, see +:ref:`radius_traffic_counter_check_name` +:ref:`radius_traffic_counter_reply_name` for more information. + +``MonthlyTrafficCounter`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +This counter is used to limit the amount of traffic users can consume +every solar month. It works by checking whether the total amount of +download plus upload octets (bytes consumed) is below the value indicated +in the ``Max-Monthly-Session-Traffic`` group check attribute, sending the +remaining octets with a reply message or rejecting the authorization if +the limit has been passed. + +The reply message is configurable because it can differ from NAS to NAS, +:ref:`radius_traffic_counter_reply_name` for more information. + +``MonthlySubscriptionTrafficCounter`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. important:: + + This counter is not enabled by default. It can be enabled via the + :ref:`radius_counter_related_settings`. + +Same as ``MonthlyTrafficCounter``, but with the difference that the reset +period depends on the day in which the user subscribed to the service: if +the user signed up (or their account was created by an admin) on a date +like November 15 2022, the reset period will start on the 15th day of +every month. + +Database Support +~~~~~~~~~~~~~~~~ + +The counters described above are available for PostgreSQL, MySQL, SQLite +and are enabled by default. + +There's a different class of each counter for each database, because the +query is executed with raw SQL defined on each class, instead of the +classic django-ORM approach which is database agnostic. + +It was implemented this way to ensure maximum flexibility and adherence to +the FreeRADIUS sqlcounter implementation. + +Django Settings +~~~~~~~~~~~~~~~ + +The settings available to control the behavior of counters are described +in :ref:`radius_counter_related_settings`. + +Writing Custom Counter Classes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is possible to write custom counter classes to satisfy any need. + +The easiest way is to subclass +``openwisp_radius.counters.base.BaseCounter``, then implement at least the +following attributes: + +- ``counter_name``: name of the counter, used internally for debugging; +- ``check_name``: attribute name used in the database lookup to the group + check table; +- ``reply_name``: attribute name sent in the reply message; +- ``reset``: reset period, either ``daily``, ``weekly``, ``monthly``, + ``monthly_subscription`` or ``never``; +- ``sql``: the raw SQL query to execute; +- ``get_sql_params``: a method which returns a list of the arguments + passed to the interpolation of the raw sql query. + +Please look at the source code of OpenWISP RADIUS to find out more. + +- `openwisp_radius.counters.base + `_ +- `openwisp_radius.counters.postgresql + `_ + +Once the new class is ready, you will need to add it to +:ref:`radius_counters_setting`. + +It is also possible to implement a check class in a completely custom +fashion (that is, not inheriting from ``BaseCounter``), the only +requirements are: + +- the class must have a constructor (``__init__`` method) identical to the + one used in the ``BaseCounter`` class; +- the class must have a ``check`` method which doesn't need any required + argument and returns the remaining counter value or raises + ``MaxQuotaReached`` if the limit has been reached and the authorization + should be rejected; This method may return ``None`` if no additional + RADIUS attribute needs to be added to the response. diff --git a/docs/user/generating_users.rst b/docs/user/generating_users.rst new file mode 100644 index 00000000..290c29ad --- /dev/null +++ b/docs/user/generating_users.rst @@ -0,0 +1,86 @@ +Generating users +================ + +Many a times, a network admin might need to generate temporary users (eg: +events). + +This feature can be used for generating users by specifying a prefix and +the number of users to be generated. + +There are many features included in it such as: + +- **Generating users in batches**: all of the users of a particular + **prefix** would be stored in batches and can be retrieved/deleted + easily using the batch functions. +- **Download user credentials in PDF format**: get the usernames and + passwords generated outputted into a PDF. +- **Set an expiration date**: an expiration date can be set for a batch + after which the users would not able to authenticate to the RADIUS + Server. + +This operation can be performed via the admin interface, with a management +command or via the REST API. + +.. note:: + + Users imported or generated through this form will be flagged as + verified if the organization requires identity verification, otherwise + the generated users would not be able to log in. If this organization + requires identity verification, make sure the identity of the users is + verified before giving out the credentials. + +Using the admin interface +------------------------- + +To generate users from the admin interface, go to ``Home > Batch user +creation operations > Add`` (URL: +``/admin/openwisp_radius/radiusbatch/add``), set ``Strategy`` to +``Generate from prefix``, fill in the remaining fields that are shown +after the selection of the strategy and save. + +Once the batch object has been created, a PDF containing the user +credentials can be downloaded by using the "Download user credentials" +button in the upper right corner of the page: + +.. image:: ../images/download_user_credentials_button.png + :alt: Downlaod user credentials button in admin interface + +The contents of the PDF is in format of a table of users & their +passwords: + +.. image:: ../images/pdf_of_user_list.png + :alt: Sample contents of the user credentials PDF file + +Usage Demonstration: + +.. image:: ../images/add_users_prefix.gif + :alt: Demo: adding users from prefix + +Management command: ``prefix_add_users`` +---------------------------------------- + +This command generates users whose usernames start with a particular +prefix. Usage is as shown below. + +.. code-block:: shell + + ./manage.py prefix_add_users --name \ + --organization= \ + --prefix \ + --n \ + --expiration \ + --password-length \ + --output + +.. note:: + + The expiration, password-length and output are optional parameters. + The options expiration and password-length default to never and 8 + respectively. If output parameter is not provided, pdf file is not + created on the server and can be accessed from the admin interface. + +REST API: Batch user creation +----------------------------- + +See API documentation: :ref:`Batch user creation +`. diff --git a/docs/user/importing_users.rst b/docs/user/importing_users.rst new file mode 100644 index 00000000..70bda5d4 --- /dev/null +++ b/docs/user/importing_users.rst @@ -0,0 +1,118 @@ +Importing users +=============== + +This feature can be used for importing users from a csv file. There are +many features included in it such as: + +- Importing users in batches: all of the users of a particular csv file + would be stored in batches and can be retrieved/ deleted easily using + the batch functions. +- Set an expiration date: Expiration date can be set for a batch after + which the users would not able to authenticate to the RADIUS Server. +- Autogenerate usernames and passwords: The usernames and passwords are + automatically generated if they aren't provided in the csv file. + Usernames are generated from the email address whereas passwords are + generated randomly and their lengths can be customized. +- Passwords are accepted in both cleartext and hash formats from the CSV. +- Send mails to users whose passwords have been generated automatically. + +This operation can be performed via the admin interface, with a management +command or via the REST API. + +.. _radius_csv_format: + +CSV Format +---------- + +The CSV shall be of the format: + +:: + + username,password,email,firstname,lastname + +Imported users with hashed passwords +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The hashes are directly stored in the database if they are of the `django +hash format +`_. + +For example, a password myPassword123, hashed using salted SHA1 algorithm, +will look like: + +:: + + pbkdf2_sha256$100000$cKdP39chT3pW$2EtVk4Hhm1V65GNfYAA5AHj0uyD60f2CmqumqiB/gRk= + +So a full CSV line containing that password would be: + +:: + + username,pbkdf2_sha256$100000$cKdP39chT3pW$2EtVk4Hhm1V65GNfYAA5AHj0uyD60f2CmqumqiB/gRk=,email@email.com,firstname,lastname + +Importing users with clear-text passwords +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Clear-text passwords must be flagged with the prefix ``cleartext$``. + +For example, if we want to use the password ``qwerty``, we must use: +``cleartext$qwerty``. + +Auto-generation of usernames and passwords +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Email is the only mandatory field of the CSV file. + +Other fields like username and password will be auto-generated if omitted. + +Emails will be sent to users whose usernames or passwords have been +auto-generated and contents of these emails can be customized too. + +Here are some defined settings for doing that: + + - :ref:`OPENWISP_RADIUS_BATCH_MAIL_SUBJECT + ` + - :ref:`OPENWISP_RADIUS_BATCH_MAIL_MESSAGE + ` + - :ref:`OPENWISP_RADIUS_BATCH_MAIL_SENDER + ` + +Using the admin interface +------------------------- + +.. note:: + + The CSV uploaded must follow the :ref:`CSV format described above + `. + +To generate users from the admin interface, go to ``Home > Batch user +creation operations > Add`` (URL: +``/admin/openwisp_radius/radiusbatch/add``), set ``Strategy`` to ``Import +from CSV``, choose the CSV file to upload and save. + +.. image:: ../images/add_users_csv.gif + :alt: Demo: adding users from CSV + +Management command: ``batch_add_users`` +--------------------------------------- + +This command imports users from a csv file. Usage is as shown below. + +.. code-block:: shell + + ./manage.py batch_add_users --name \ + --organization= \ + --file \ + --expiration \ + --password-length + +.. note:: + + The expiration and password-length are optional parameters which + default to never and 8 respectively. + +REST API: Batch user creation +----------------------------- + +See :ref:`API documentation: Batch user creation +`. diff --git a/docs/user/intro.rst b/docs/user/intro.rst new file mode 100644 index 00000000..e807345e --- /dev/null +++ b/docs/user/intro.rst @@ -0,0 +1,13 @@ +RADIUS: Features +================ + +The RADIUS module provides the following features: + +- :doc:`Registration of new users ` +- :ref:`SMS verification ` +- :doc:`importing_users` +- :doc:`generating_users` +- :doc:`social_login` +- :doc:`saml` +- :doc:`enforcing_limits` +- :doc:`rest-api` diff --git a/docs/user/management_commands.rst b/docs/user/management_commands.rst new file mode 100644 index 00000000..549736ea --- /dev/null +++ b/docs/user/management_commands.rst @@ -0,0 +1,207 @@ +Management commands +=================== + +These management commands are necessary for enabling certain features and +for database cleanup. + +Example usage: + +.. code-block:: shell + + cd tests/ + ./manage.py + +In this page we list the management commands currently available in +**openwisp-radius**. + +``delete_old_radacct`` +---------------------- + +This command deletes RADIUS accounting sessions older than ````. + +.. code-block:: shell + + ./manage.py delete_old_radacct + +For example: + +.. code-block:: shell + + ./manage.py delete_old_radacct 365 + +``delete_old_postauth`` +----------------------- + +This command deletes RADIUS post-auth logs older than ````. + +.. code-block:: shell + + ./manage.py delete_old_postauth + +For example: + +.. code-block:: shell + + ./manage.py delete_old_postauth 365 + +``cleanup_stale_radacct`` +------------------------- + +This command closes stale RADIUS sessions that have remained open for the +number of specified ````. + +.. code-block:: shell + + ./manage.py cleanup_stale_radacct + +For example: + +.. code-block:: shell + + ./manage.py cleanup_stale_radacct 15 + +``deactivate_expired_users`` +---------------------------- + +.. note:: + + :ref:`Find out more about this feature in its dedicated page + ` + +This command deactivates expired user accounts which were created with +batch operation temporarily (eg: for en event) and have an expiration date +set. + +.. code-block:: shell + + ./manage.py deactivate_expired_users + +``delete_old_radiusbatch_users`` +-------------------------------- + +This command deletes users created using batch operation that have expired +(and should have been deactivated by ``deactivate_expired_users``) for +more than the specified ````. + +.. code-block:: shell + + ./manage.py delete_old_radiusbatch_users --older-than-months + +Note that the default duration is set to **18 months**. + +``delete_unverified_users`` +--------------------------- + +This command deletes unverified users that have been registered for more +than specified duration and have no associated radius session. This +feature is needed to delete users who have registered but never completed +the verification process. **Staff users will not be deleted by this +management command.** + +.. code-block:: shell + + ./manage.py delete_unverified_users --older-than-days + +Note that the default duration is set to **1 day**. + +It is also possible to exclude users that have registered using specified +methods. You can specify multiple methods separated by comma(`,`). +Following is an example: + +.. code-block:: shell + + ./manage.py delete_unverified_users --older-than-days 1 --exclude-methods mobile_phone,email + +``upgrade_from_django_freeradius`` +---------------------------------- + +If you are upgrading from `django-freeradius +`_ to openwisp-radius, +there is an easy migration script that will import your freeradius +database, sites, social website account users, users & groups to +openwisp-radius instance: + +.. code-block:: shell + + ./manage.py upgrade_from_django_freeradius + +The management command accepts an argument ``--backup``, that you can pass +to give the location of the backup files, by default it looks in the +``tests/`` directory, eg: + +.. code-block:: shell + + ./manage.py upgrade_from_django_freeradius --backup /home/user/django_freeradius/ + +The management command accepts another argument ``--organization``, if you +want to import data to a specific organization, you can give its UUID for +the same, by default the data is added to the first found organization, +eg: + +:: + + ./manage.py upgrade_from_django_freeradius --organization 900856da-c89a-412d-8fee-45a9c763ca0b + +.. note:: + + You can follow the `tutorial to migrate database from + django-freeradius + `_. + +.. warning:: + + It is not possible to export user credential data for radiusbatch + created using prefix, please manually preserve the PDF files if you + want to access the data in the future. + +.. _radius_convert_called_station_id: + +``convert_called_station_id`` +----------------------------- + +If an installation uses a centralized captive portal, the value of "Called +Station ID" of RADIUS Sessions will always show the MAC address of the +captive portal instead of the access points. + +This command will update the "Called Station ID" to reflect the MAC +address of the access points using information from OpenVPN. It requires +installing ``openvpn_status``, which can be installed using the following +command + +.. code-block:: shell + + pip install openwisp-radius[openvpn_status] + +In order to work, this command requires to be configured via the +:ref:`OPENWISP_RADIUS_CALLED_STATION_IDS +` setting. + +Use the following command if you want to perform this operation for all +RADIUS sessions that meet criteria of +``OPENWISP_RADIUS_CALLED_STATION_IDS`` setting. + +.. code-block:: shell + + ./manage.py convert_called_station_id + +You can also convert the "Called Station ID" of a particular RADIUS +session by replacing session's ``unique_id`` in the following command: + +.. code-block:: shell + + ./manage.py convert_called_station_id --unique_id= + +.. note:: + + If you encounter ``ParseError`` for datetime data, you can set the + datetime format of the parser using + :ref:`OPENWISP_RADIUS_OPENVPN_DATETIME_FORMAT + ` setting. + +.. note:: + + ``convert_called_station_id`` command will only operate on open RADIUS + sessions, i.e. the "stop_time" field is None. + + But if you are converting a single RADIUS session, it will operate on + it even if the session is closed. diff --git a/docs/user/registration.rst b/docs/user/registration.rst new file mode 100644 index 00000000..1e8b9875 --- /dev/null +++ b/docs/user/registration.rst @@ -0,0 +1,14 @@ +Registration of new users +========================= + +openwisp-radius uses `django-rest-auth +`_ which provides registration +of new users via REST API so you can implement registration and password +reset directly from your captive page. + +The registration API endpoint is described in :ref:`API: User Registration +`. + +If you need users to self-register to a public wifi service, we suggest to +take a look at :doc:`OpenWISP WiFi Login Pages `, +which is built to work with openwisp-radius. diff --git a/docs/user/rest-api.rst b/docs/user/rest-api.rst new file mode 100644 index 00000000..cb58b572 --- /dev/null +++ b/docs/user/rest-api.rst @@ -0,0 +1,873 @@ +REST API Reference +================== + +.. contents:: **Table of contents**: + :depth: 2 + :local: + +.. important:: + + The REST API of openwisp-radius is enabled by default and may be + turned off by setting :ref:`OPENWISP_RADIUS_API ` + to ``False``. + +.. _radius_live_documentation: + +Live documentation +------------------ + +.. image:: ../images/swagger_api.png + :alt: Swagger API Documentation + +A general live API documentation (following the OpenAPI specification) at +``/api/v1/docs/``. + +.. _radius_browsable_web_interface: + +Browsable web interface +----------------------- + +.. image:: ../images/drf_api_interface.png + :alt: API Interface + +Additionally, opening any of the endpoints :ref:`listed below +` directly in the browser will show the `browsable +API interface of Django-REST-Framework +`_, which +makes it even easier to find out the details of each endpoint. + +.. _radius_rest_endpoints: + +FreeRADIUS API Endpoints +------------------------ + +The following section is dedicated to API endpoints that are designed to +be consumed by FreeRADIUS (:ref:`Authorize `, :ref:`Post +Auth `, :ref:`Accounting `). + +.. important:: + + These endpoints can be consumed only by hosts which have been added to + the :ref:`freeradius allowed hosts list + `. + +.. _radius_freeradius_api_authentication: + +FreeRADIUS API Authentication +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are 3 different methods with which the FreeRADIUS API endpoints can +authenticate incoming requests and understand to which organization these +requests belong. + +.. _radius_user_token: + +Radius User Token ++++++++++++++++++ + +This method relies on the presence of a special token which was obtained +by the user when authenticating via the :ref:`Obtain Auth Token View +`, this means the user would have to +log in through something like a web form first. + +The flow works as follows: + +1. the user enters credentials in a login form belonging to a specific + organization and submits, the credentials are then sent to the + :ref:`Obtain Auth Token View `; +2. if credentials are correct, a **radius user token** associated to the + user and organization is created and returned in the response; +3. the login page or app must then initiate the HTTP request to the web + server of the captive portal, (the URL of the form action of the + default captive login page) using the radius user token as password, + example: + +.. code-block:: text + + curl -X POST http://captive.projcect.com:8005/index.php?zone=myorg \ + -d "auth_user=&auth_pass=" + +This method is recommended if you are using multiple organizations in the +same OpenWISP instance. + +.. note:: + + By default, ```` is valid for authentication for one + request only and a new ```` needs to be :ref:`obtained + for each request `. However, if + :ref:`OPENWISP_RADIUS_DISPOSABLE_RADIUS_USER_TOKEN + ` is set to ``False``, + the ```` is valid for authentication as long as + freeradius accounting ``Stop`` request is not sent or the token is not + deleted. + +.. warning:: + + If you are using Radius User token method, keep in mind that one user + account can only authenticate with one organization at a time, i.e a + single user account cannot consume services from multiple + organizations simultaneously. + +.. _radius_bearer_token: + +Bearer token +++++++++++++ + +This other method allows to use the system without the need for a user to +obtain a token first, the drawback is that one FreeRADIUS site has to be +configured for each organization, the authorization credentials for the +specific organization is sent in each request, see :ref:`freeradius_site` +for more information on the FreeRADIUS site configuration. + +The (:ref:`Organization UUID and Organization RADIUS token +`) are sent in the authorization header of +the HTTP request in the form of a Bearer token, eg: + +.. code-block:: text + + curl -X POST http://localhost:8000/api/v1/freeradius/authorize/ \ + -H "Authorization: Bearer " \ + -d "username=&password=" + +This method is recommended if you are using only one organization and you +have no need nor intention of adding more organizations in the future. + +.. _radius_querystring: + +Querystring ++++++++++++ + +This method is identical to the previous one, but the credentials are sent +in querystring parameters, eg: + +.. code-block:: text + + curl -X POST http://localhost:8000/api/v1/freeradius/authorize/?uuid=&token= \ + -d "username=&password=" + +This method is not recommended for production usage, it should be used for +testing and debugging only (because webservers can include the querystring +parameters in their logs). + +.. _radius_organization_uuid_token: + +Organization UUID & RADIUS API Token +++++++++++++++++++++++++++++++++++++ + +You can get (and set) the value of the OpenWISP RADIUS API token in the +organization configuration page on the OpenWISP dashboard (select your +organization in ``/admin/openwisp_users/organization/``): + +.. image:: ../images/token.png + :alt: Organization Radius Token + +.. note:: + + It is highly recommended that you use a hard to guess value, longer + than 15 characters containing both letters and numbers. Eg: + ``165f9a790787fc38e5cc12c1640db2300648d9a2``. + +You will also need the UUID of your organization from the organization +change page (select your organization in +``/admin/openwisp_users/organization/``): + +.. image:: ../images/org_uuid.png + :alt: Organization UUID + +Requests authorizing with :ref:`bearer-token ` or +:ref:`querystring ` method **must** contain +organization UUID & token. If the tokens are missing or invalid, the +request will receive a ``403`` HTTP error. + +For information on how to configure FreeRADIUS to send the bearer tokens, +see :ref:`freeradius_site`. + +API Throttling +~~~~~~~~~~~~~~ + +To override the default API throttling settings, add the following to your +``settings.py`` file: + +.. code-block:: python + + REST_FRAMEWORK = { + "DEFAULT_THROTTLE_CLASSES": [ + "rest_framework.throttling.ScopedRateThrottle", + ], + "DEFAULT_THROTTLE_RATES": { + # None by default + "authorize": None, + "postauth": None, + "accounting": None, + "obtain_auth_token": None, + "validate_auth_token": None, + "create_phone_token": None, + "phone_token_status": None, + "validate_phone_token": None, + # Relaxed throttling Policy + "others": "400/hour", + }, + } + +The rate descriptions used in ``DEFAULT_THROTTLE_RATES`` may include +``second``, ``minute``, ``hour`` or ``day`` as the throttle period, +setting it to ``None`` will result in no throttling. + +List of Endpoints +~~~~~~~~~~~~~~~~~ + +.. _radius_authorize: + +Authorize ++++++++++ + +Use by FreeRADIUS to perform the ``authorization`` phase. + +It's triggered when a user submits the form to login into the captive +portal. The captive portal has to be configured to send the password to +freeradius in clear text (will be encrypted with the freeradius shared +secret, can be tunneled via TLS for increased security if needed). + +FreeRADIUS in turn will send the username and password via HTTPs to this +endpoint. + +Responds to only **POST**. + +.. code-block:: text + + /api/v1/freeradius/authorize/ + +Example: + +.. code-block:: text + + POST /api/v1/freeradius/authorize/ HTTP/1.1 username=testuser&password=testpassword + +======== =========================== +Param Description +======== =========================== +username Username for the given user +password Password for the given user +======== =========================== + +If the authorization is successful, the API will return all group replies +related to the group with highest priority assigned to the user. + +If the authorization is unsuccessful, the response body can either be +empty or it can contain an explicit rejection, depending on how the +:ref:`OPENWISP_RADIUS_API_AUTHORIZE_REJECT +` setting is configured. + +.. _radius_post_auth: + +Post Auth ++++++++++ + +API endpoint designed to be used by FreeRADIUS ``postauth``. + +Responds only to **POST**. + +.. code-block:: text + + /api/v1/freeradius/postauth/ + +================== =================================== +Param Description +================== =================================== +username Username +password Password (*) +reply Radius reply received by freeradius +called_station_id Called Station ID +calling_station_id Calling Station ID +================== =================================== + +(*): the ``password`` is stored only on unsuccessful authorizations. + +Returns an empty response body in order to instruct FreeRADIUS to avoid +processing the response body. + +.. _radius_accounting: + +Accounting +++++++++++ + +.. code-block:: text + + /api/v1/freeradius/accounting/ + +GET +... + +Returns a list of accounting objects + +.. code-block:: text + + GET /api/v1/freeradius/accounting/ + +.. code-block:: json + + [ + { + "called_station_id": "00-27-22-F3-FA-F1:hostname", + "nas_port_type": "Async", + "groupname": null, + "id": 1, + "realm": "", + "terminate_cause": "User_Request", + "nas_ip_address": "172.16.64.91", + "authentication": "RADIUS", + "stop_time": null, + "nas_port_id": "1", + "service_type": "Login-User", + "username": "admin", + "update_time": null, + "connection_info_stop": null, + "start_time": "2018-03-10T14:44:17.234035+01:00", + "output_octets": 1513075509, + "calling_station_id": "5c:7d:c1:72:a7:3b", + "input_octets": 9900909, + "interval": null, + "session_time": 261, + "session_id": "35000006", + "connection_info_start": null, + "framed_protocol": "test", + "framed_ip_address": "127.0.0.1", + "unique_id": "75058e50" + } + ] + +POST +.... + +Add or update accounting information (start, interim-update, stop); does +not return any JSON response so that freeradius will avoid processing the +response without generating warnings + +===================== ===================== +Param Description +===================== ===================== +session_id Session ID +unique_id Accounting unique ID +username Username +groupname Group name +realm Realm +nas_ip_address NAS IP address +nas_port_id NAS port ID +nas_port_type NAS port type +start_time Start time +update_time Update time +stop_time Stop time +interval Interval +session_time Session Time +authentication Authentication +connection_info_start Connection Info Start +connection_info_stop Connection Info Stop +input_octets Input Octets +output_octets Output Octets +called_station_id Called station ID +calling_station_id Calling station ID +terminate_cause Termination Cause +service_type Service Type +framed_protocol Framed protocol +framed_ip_address framed IP address +===================== ===================== + +Pagination +'''''''''' + +Pagination is provided using a Link header pagination. Check `here for +more information about traversing with pagination +`_. + +.. code-block:: text + + { + .... + .... + link: ; rel=\"next\", + ; rel=\"last\" + .... + .... + } + +.. note:: + + Default page size is 10, which can be overridden using the `page_size` + parameter. + +Filters +''''''' + +The JSON objects returned using the GET endpoint can be filtered/queried +using specific parameters. + +================== ================================ +Filter Parameters Description +================== ================================ +username Username +called_station_id Called Station ID +calling_station_id Calling Station ID +start_time Start time (greater or equal to) +stop_time Stop time (less or equal to) +is_open If stop_time is null +================== ================================ + +User API Endpoints +------------------ + +These API endpoints are designed to be used by users (eg: creating an +account, changing their password, obtaining access tokens, validating +their phone number, etc.). + +.. note:: + + The API endpoints described below do not require the + :ref:`Organization API Token ` + described in the beginning of this document. + +Some endpoints require the sending of the user API access token sent in +the form of a "Bearer Token", example: + +.. code-block:: shell + + curl -H "Authorization: Bearer " \ + 'http://localhost:8000/api/v1/radius/organization/default/account/session/' + +List of Endpoints +~~~~~~~~~~~~~~~~~ + +.. _radius_user_registration: + +User Registration ++++++++++++++++++ + +.. important:: + + This endpoint is enabled by default but can be disabled either via a + :ref:`global setting or from the admin interface + `. + +.. code-block:: text + + /api/v1/radius/organization//account/ + +Responds only to **POST**. + +Parameters: + +============ =============== +Param Description +============ =============== +username string +phone_number string (\*) +email string +password1 string +password2 string +first_name string (\*\*) +last_name string (\*\*) +birth_date string (\*\*) +location string (\*\*) +method string (\*\*\*) +============ =============== + +(\*) ``phone_number`` is required only when the organization has enabled +:ref:`SMS verification in its "Organization RADIUS Settings" +`. + +(\*\*) ``first_name``, ``last_name``, ``birth_date`` and ``location`` are +optional fields which are disabled by default to make the registration +simple, but can be :ref:`enabled through configuration +`. + +(\*\*) ``method`` must be one of the available +:ref:`registration/verification methods +`; if identity verification +is disabled for a particular org, an empty string will be acceptable. + +.. _radius_registering_to_multiple_organizations: + +Registering to Multiple Organizations +..................................... + +An **HTTP 409** response will be returned if an existing user tries to +register on a URL of a different organization (because the account already +exists). The response will contain a list of organizations with which the +user has already registered to the system which may be shown to the user +in the UI. E.g.: + +.. code-block:: json + + { + "details": "A user like the one being registered already exists.", + "organizations":[ + {"slug":"default","name":"default"} + ] + } + +The existing user can register with a new organization using the +:ref:`login endpoint `. The user will +also get membership of the new organization only if the organization has +:ref:`user registration enabled +`. + +.. _radius_reset_password: + +Reset password +++++++++++++++ + +This is the classic "password forgotten recovery feature" which sends a +reset password token to the email of the user. + +.. code-block:: text + + /api/v1/radius/organization//account/password/reset/ + +Responds only to **POST**. + +Parameters: + +===== ====================================================== +Param Description +===== ====================================================== +input string that can be an email, phone_number or username. +===== ====================================================== + +Confirm reset password +++++++++++++++++++++++ + +Allows users to confirm their reset password after having it requested via +the :ref:`Reset password ` endpoint. + +.. code-block:: text + + /api/v1/radius/organization//account/password/reset/confirm/ + +Responds only to **POST**. + +Parameters: + +============= =========== +Param Description +============= =========== +new_password1 string +new_password2 string +uid string +token string +============= =========== + +Change password ++++++++++++++++ + +**Requires the user auth token (Bearer Token)**. + +Allows users to change their password after using the :ref:`Reset password +` endpoint. + +.. code-block:: text + + /api/v1/radius/organization//account/password/change/ + +Responds only to **POST**. + +Parameters: + +================ =========== +Param Description +================ =========== +current_password string +new_password string +confirm_password string +================ =========== + +.. _radius_login_obtain_user_auth_token: + +Login (Obtain User Auth Token) +++++++++++++++++++++++++++++++ + +.. code-block:: text + + /api/v1/radius/organization//account/token/ + +Responds only to **POST**. + +Returns: + +- ``radius_user_token``: the user radius token, which can be used to + authenticate the user in the captive portal by sending it in place of + the user password (it will be passed to freeradius which in turn will + send it to the :ref:`authorize API endpoint ` which + will recognize the token as the user passsword) +- ``key``: the user API access token, which will be needed to authenticate + the user to eventual subsequent API requests (eg: change password) +- ``is_active`` if it's ``false`` it means the user has been banned +- ``is_verified`` when identity verification is enabled, it indicates + whether the user has completed an indirect identity verification process + like confirming their mobile phone number +- ``method`` registration/verification method used by the user to + register, eg: ``mobile_phone``, ``social_login``, etc. +- ``username`` +- ``email`` +- ``phone_number`` +- ``first_name`` +- ``last_name`` +- ``birth_date`` +- ``location`` + +If the user account is inactive or unverified the endpoint will send the +data anyway but using the HTTP status code 401, this way consumers can +recognize these users and trigger the appropriate response needed (eg: +reject them or initiate account verification). + +If an existing user account tries to authenticate to an organization of +which they're not member of, then they would be automatically added as +members (if registration is enabled for that org). Please refer to +:ref:`"Registering to Multiple Organizations" +`. + +This endpoint updates the user language preference field according to the +``Accept-Language`` HTTP header. + +Parameters: + +======== =========== +Param Description +======== =========== +username string +password string +======== =========== + +Validate user auth token +++++++++++++++++++++++++ + +Used to check whether the auth token of a user is valid or not. + +Return also the radius user token and username in the response. + +.. code-block:: text + + /api/v1/radius/organization//account/token/validate/ + +Responds only to **POST**. + +Parameters: + +===== =============================== +Param Description +===== =============================== +token the rest auth token to validate +===== =============================== + +The user information is returned in the response (similarly to +:ref:`Obtain User Auth Token `), +along with the following additional parameter: + +- ``response_code``: string indicating whether the result is successful or + not, to be used for translation. + +This endpoint updates the user language preference field according to the +``Accept-Language`` HTTP header. + +User Radius Sessions +++++++++++++++++++++ + +**Requires the user auth token (Bearer Token)**. + +Returns the radius sessions of the logged-in user and the organization +specified in the URL. + +.. code-block:: text + + /api/v1/radius/organization//account/session/ + +Responds only to **GET**. + +.. _radius_usage_api_view: + +User Radius Usage ++++++++++++++++++ + +**Requires the user auth token (Bearer Token)**. + +Returns the radius usage of the logged-in user and the organization +specified in the URL. + +It executes the relevant RADIUS counters and returns information that +shows how much time and/or traffic the user has consumed. + +.. code-block:: text + + /api/v1/radius/organization//account/usage/ + +Responds only to **GET**. + +Create SMS token +++++++++++++++++ + +.. note:: + + This API endpoint will work only if the organization has enabled + :ref:`SMS verification `. + +**Requires the user auth token (Bearer Token)**. + +Used for SMS verification, sends a code via SMS to the phone number of the +user. + +.. code-block:: text + + /api/v1/radius/organization//account/phone/token/ + +Responds only to **POST**. + +No parameters required. + +Get active SMS token status ++++++++++++++++++++++++++++ + +.. note:: + + This API endpoint will work only if the organization has enabled + :ref:`SMS verification `. + +**Requires the user auth token (Bearer Token)**. + +Used for SMS verification, allows checking whether an active SMS token was +already requested for the mobile phone number of the logged in account. + +.. code-block:: text + + /api/v1/radius/organization//account/phone/token/active/ + +Responds only to **GET**. + +No parameters required. + +.. _radius_verify_validate_sms_token: + +Verify/Validate SMS token ++++++++++++++++++++++++++ + +.. note:: + + This API endpoint will work only if the organization has enabled + :ref:`SMS verification `. + +**Requires the user auth token (Bearer Token)**. + +Used for SMS verification, allows users to validate the code they receive +via SMS. + +.. code-block:: text + + /api/v1/radius/organization//account/phone/verify/ + +Responds only to **POST**. + +Parameters: + +===== =========== +Param Description +===== =========== +code string +===== =========== + +Change phone number ++++++++++++++++++++ + +.. note:: + + This API endpoint will work only if the organization has enabled + :ref:`SMS verification `. + +**Requires the user auth token (Bearer Token)**. + +Allows users to change their phone number, will flag the user as inactive +and send them a verification code via SMS. The phone number of the user is +updated only after this verification code has been :ref:`validated +`. + +.. code-block:: text + + /api/v1/radius/organization//account/phone/change/ + +Responds only to **POST**. + +Parameters: + +============ =========== +Param Description +============ =========== +phone_number string +============ =========== + +.. _radius_batch_user_creation: + +Batch user creation ++++++++++++++++++++ + +This API endpoint allows to use the features described in +:doc:`importing_users` and :doc:`generating_users`. + +.. code-block:: text + + /api/v1/radius/batch/ + +.. note:: + + This API endpoint allows to use the features described in + :doc:`importing_users` and :doc:`generating_users`. + +Responds only to **POST**, used to save a ``RadiusBatch`` instance. + +It is possible to generate the users of the ``RadiusBatch`` with two +different strategies: csv or prefix. + +The csv method needs the following parameters: + +================= ================================= +Param Description +================= ================================= +name Name of the operation +strategy csv +csvfile file with the users +expiration_date date of expiration of the users +organization_slug slug of organization of the users +================= ================================= + +These others are for the prefix method: + +================= ================================== +Param Description +================= ================================== +name name of the operation +strategy prefix +prefix prefix for the generation of users +number_of_users number of users +expiration_date date of expiration of the users +organization_slug slug of organization of the users +================= ================================== + +When using this strategy, in the response you can find the field +``user_credentials`` containing the list of users created (example: +``[['username', 'password'], ['sample_user', 'BBuOb5sN']]``) and the field +``pdf_link`` which can be used to download a PDF file containing the user +credentials. + +Batch CSV Download +++++++++++++++++++ + +.. code-block:: text + + /api/v1/radius/organization//batch//csv/ + +Responds only to **GET**. + +Parameters: + +======== =========== +Param Description +======== =========== +slug string +id string +filename string +======== =========== diff --git a/docs/user/saml.rst b/docs/user/saml.rst new file mode 100644 index 00000000..a1ffe933 --- /dev/null +++ b/docs/user/saml.rst @@ -0,0 +1,172 @@ +Single Sign-On (SAML) +===================== + +.. important:: + + The SAML registration method is disabled by default. + + In order to enable this feature you have to follow the :ref:`SAML + setup instructions ` below and then activate it via + :ref:`global setting or from the admin interface + `. + +`SAML `_ is supported by generating an +additional temporary token right after users authenticates via SSO, the +user is then redirected to the captive page with 3 querystring parameters: + +- ``username`` +- ``token`` (REST auth token) +- ``login_method=saml`` + +The captive page must recognize these two parameters, validate the token +and automatically perform the submit action of the captive portal login +form: ``username`` should obviously used for the username field, while +``token`` should be used for the password field. + +The third parameter, ``login_method=saml``, is needed because it allows +the captive page to remember that the user logged in via SAML. This +information will be used later on when performing the :ref:`SAML logout +`. + +The internal REST API of openwisp-radius will recognize the token and +authorize the user. + +This kind of implementation allows to support SAML with any captive portal +which already supports the RADIUS protocol because it's totally +transparent for it, that is, the captive portal doesn't even know the user +is signing-in with a SSO. + +.. note:: + + If you're building a public wifi service, we suggest to take a look at + :doc:`OpenWISP WiFi Login Pages `, which is + built to work with openwisp-radius. + +.. _radius_setup_saml: + +Setup +----- + +Install required system dependencies: + +.. code-block:: shell + + sudo apt install xmlsec1 + +Install Python dependencies: + +.. code-block:: shell + + pip install openwisp-radius[saml] + +Ensure your ``settings.py`` looks like the following: + +.. code-block:: python + + INSTALLED_APPS = [ + # ... other apps .. + # apps needed for SAML login + "rest_framework.authtoken", + "django.contrib.sites", + "allauth", + "allauth.account", + "djangosaml2", + ] + + SITE_ID = 1 + + # Update AUTHENTICATION_BACKENDS + AUTHENTICATION_BACKENDS = ( + "openwisp_users.backends.UsersAuthenticationBackend", + "openwisp_radius.saml.backends.OpenwispRadiusSaml2Backend", # <- add for SAML login + ) + + # Update MIDDLEWARE + MIDDLEWARE = [ + # ... other middlewares ... + "djangosaml2.middleware.SamlSessionMiddleware", + ] + +Ensure your main ``urls.py`` contains the +``openwisp_users.accounts.urls``: + +.. code-block:: python + + urlpatterns = [ + # .. other urls ... + path("accounts/", include("openwisp_users.accounts.urls")), + ] + +Configure the djangosaml2 settings +---------------------------------- + +Refer to the djangosaml2 documentation to find out `how to configure +required settings for SAML +`_. + +Captive page button example +--------------------------- + +After successfully configuring SAML settings for your Identity Provider, +you will need an HTML button similar to the one in the following example. + +This example needs the slug of the organization to assign the new user to +the right organization: + +.. code-block:: html + + + Log in with SSO + + +Substitute ``openwisp2.mywifiproject.com``, +``https://captivepage.mywifiproject.com`` and ``default`` with the +hostname of your openwisp-radius instance, your captive page and the +organization slug respectively. + +Alternatively, you can take a look at :doc:`OpenWISP WiFi Login Pages +`, which provides buttons for Single Sign-On +(SAML) by default. + +.. _radius_saml_logout: + +Logout +------ + +When logging out a user which logged in via SAML, the captive page should +also call the SAML logout URL: ``/radius/saml2/logout/``. + +The :doc:`OpenWISP WiFi Login Pages ` app +supports this with minimal configuration, refer to the :doc:`OpenWISP WiFi +Login Pages ` section. + +Settings +-------- + +See :ref:`SAML related settings `. + +FAQs +---- + +.. _radius_prevent_registered_user_username_change: + +Preventing change in username of a registered user +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``djangosaml2`` library requires configuring +``SAML_DJANGO_USER_MAIN_ATTRIBUTE`` setting which serves as the primary +lookup value for User objects. Whenever a user logs in or registers +through the SAML method, a database query is made to check whether such a +user already exists. This lookup is done using the value of +``SAML_DJANGO_USER_MAIN_ATTRIBUTE`` setting. If a match is found, the +details of the user are updated with the information received from SAML +Identity Provider. + +If a user (who has registered on OpenWISP with a different method from +SAML) logs into OpenWISP with SAML, then the default behavior of OpenWISP +RADIUS prevents updating username of this user. Because, this operation +could render the user's old credentials useless. If you want to update the +username in such scenarios with details received from Identity Provider, +set :ref:`OPENWISP_RADIUS_SAML_UPDATES_PRE_EXISTING_USERNAME +` to ``True``. diff --git a/docs/user/settings.rst b/docs/user/settings.rst new file mode 100644 index 00000000..89523a6f --- /dev/null +++ b/docs/user/settings.rst @@ -0,0 +1,1074 @@ +Settings +======== + +.. include:: /partials/settings-note.rst + +.. contents:: **Table of contents**: + :depth: 1 + :local: + +Admin related settings +---------------------- + +These settings control details of the administration interface of +openwisp-radius. + +.. note:: + + The values of overridden settings fields do not change even when the + global defaults are changed. + +``OPENWISP_RADIUS_EDITABLE_ACCOUNTING`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``False`` + +Whether ``radacct`` entries are editable from the django admin or not. + +``OPENWISP_RADIUS_EDITABLE_POSTAUTH`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``False`` + +Whether ``postauth`` logs are editable from the django admin or not. + +``OPENWISP_RADIUS_GROUPCHECK_ADMIN`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``False`` + +Direct editing of group checks items is disabled by default because these +can be edited through inline items in the Radius Group admin (Freeradius > +Groups). + +*This is done with the aim of simplifying the admin interface and avoid +overwhelming users with too many options*. + +If for some reason you need to enable direct editing of group checks you +can do so by setting this to ``True``. + +``OPENWISP_RADIUS_GROUPREPLY_ADMIN`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``False`` + +Direct editing of group reply items is disabled by default because these +can be edited through inline items in the Radius Group admin (Freeradius > +Groups). + +*This is done with the aim of simplifying the admin interface and avoid +overwhelming users with too many options*. + +If for some reason you need to enable direct editing of group replies you +can do so by setting this to ``True``. + +``OPENWISP_RADIUS_USERGROUP_ADMIN`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``False`` + +Direct editing of user group items (``radusergroup``) is disabled by +default because these can be edited through inline items in the User admin +(Users and Organizations > Users). + +*This is done with the aim of simplifying the admin interface and avoid +overwhelming users with too many options*. + +If for some reason you need to enable direct editing of user group items +you can do so by setting this to ``True``. + +``OPENWISP_RADIUS_USER_ADMIN_RADIUSTOKEN_INLINE`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``False`` + +The functionality of editing a user's ``RadiusToken`` directly through an +inline from the user admin page is disabled by default. + +*This is done with the aim of simplifying the admin interface and avoid +overwhelming users with too many options*. + +If for some reason you need to enable editing user's ``RadiusToken`` from +the user admin page, you can do so by setting this to ``True``. + +Model related settings +---------------------- + +These settings control details of the openwisp-radius model classes. + +``OPENWISP_RADIUS_DEFAULT_SECRET_FORMAT`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``NT-Password`` + +The default encryption format for storing radius check values. + +``OPENWISP_RADIUS_DISABLED_SECRET_FORMATS`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``[]`` + +A list of disabled encryption formats, by default all formats are enabled +in order to keep backward compatibility with legacy systems. + +``OPENWISP_RADIUS_BATCH_DEFAULT_PASSWORD_LENGTH`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``8`` + +The default password length of the auto generated passwords while batch +addition of users from the csv. + +``OPENWISP_RADIUS_BATCH_DELETE_EXPIRED`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``18`` + +It is the number of months after which the expired users are deleted. + +``OPENWISP_RADIUS_BATCH_PDF_TEMPLATE`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is the template used to generate the pdf when users are being generated +using the batch add users feature using the prefix. + +The value should be the absolute path to the template of the pdf. + +``OPENWISP_RADIUS_EXTRA_NAS_TYPES`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``tuple()`` + +This setting can be used to add custom NAS types that can be used from the +admin interface when managing NAS instances. + +For example, you want a custom NAS type called ``cisco``, you would add +the following to your project ``settings.py``: + +.. code-block:: python + + OPENWISP_RADIUS_EXTRA_NAS_TYPES = (("cisco", "Cisco Router"),) + +.. _openwisp_radius_freeradius_allowed_hosts: + +``OPENWISP_RADIUS_FREERADIUS_ALLOWED_HOSTS`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``[]`` + +List of host IP addresses or subnets allowed to consume the freeradius API +endpoints (Authorize, Accounting and Postauth), i.e the value of this +option should be the IP address of your freeradius instance. Example: If +your freeradius instance is running on the same host machine as OpenWISP, +the value should be ``127.0.0.1``. Similarly, if your freeradius instance +is on a different host in the private network, the value should be the +private IP of freeradius host like ``192.0.2.50``. If your freeradius is +on a public network, please use the public IP of your freeradius instance. + +You can use subnets when freeradius is hosted on a variable IP, eg: + +- ``198.168.0.0/24`` to allow the entire LAN. +- ``0.0.0.0/0`` to allow any address (useful for development / testing). + +This value can be overridden per organization in the organization change +page. You can skip setting this option if you intend to set it from +organization change page for each organization. + +.. image:: ../images/freeradius_allowed_hosts.png + :alt: Organization change page freeradius settings + +.. code-block:: python + + OPENWISP_RADIUS_FREERADIUS_ALLOWED_HOSTS = [ + "127.0.0.1", + "192.0.2.10", + "192.168.0.0/24", + ] + +If this option and organization change page option are both empty, then +all freeradius API requests for the organization will return ``403``. + +.. _radius_coa_enabled_setting: + +``OPENWISP_RADIUS_COA_ENABLED`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``False``` + +If set to ``True``, openwisp-radius will update the NAS with the user's +current RADIUS attributes whenever the ``RadiusGroup`` of user is changed. +This allow enforcing of rate limits on active RADIUS sessions without +requiring users to re-authenticate. For more details, :doc:`read the +dedicated section for configuring openwisp-radius and NAS for using CoA +`. + +This can be overridden for each organization separately via the +organization radius settings section of the admin interface. + +.. image:: ../images/organization_coa_enabled.png + :alt: CoA enabled + +``RADCLIENT_ATTRIBUTE_DICTIONARIES`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +============ ======== +**type**: ``list`` +**default**: ``[]`` +============ ======== + +List of absolute file paths of additional RADIUS dictionaries used for +RADIUS attribute mapping. + +.. note:: + + A `default dictionary + `_ + is shipped with openwisp-radius. Any dictionary added using this + setting will be used alongside the default dictionary. + +``OPENWISP_RADIUS_MAX_CSV_FILE_SIZE`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +============ ======================== +**type**: ``int`` +**default**: `5 * 1024 * 1024` (5 MB) +============ ======================== + +This setting can be used to set the maximum size limit for firmware +images, eg: + +.. code-block:: python + + OPENWISP_RADIUS_MAX_CSV_FILE_SIZE = 10 * 1024 * 1024 # 10MB + +.. note:: + + The numeric value represents the size of files in bytes. Setting this + to ``None`` will mean there's no max size. + +``OPENWISP_RADIUS_PRIVATE_STORAGE_INSTANCE`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +============ ======================================================================= +**type**: ``str`` +**default**: ``openwisp_radius.private_storage.storage.private_file_system_storage`` +============ ======================================================================= + +Dotted path to an instance of any one of the storage classes in +`private_storage +`_. +This instance is used for storing csv files of batch imports of users. + +By default, an instance of +``private_storage.storage.files.PrivateFileSystemStorage`` is used. + +.. _openwisp_radius_called_station_ids: + +``OPENWISP_RADIUS_CALLED_STATION_IDS`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``{}`` + +This setting allows to specify the parameters to connect to the different +OpenVPN management interfaces available for an organization. This setting +is used by the :ref:`convert_called_station_id +` command. + +It should contain configuration in following format: + +.. code-block:: python + + OPENWISP_RADIUS_CALLED_STATION_IDS = { + # UUID of the organization for which settings are being specified + # In this example 'default' + "": { + "openvpn_config": [ + { + # Host address of OpenVPN management + "host": "", + # Port of OpenVPN management interface. Defaults to 7505 (integer) + "port": 7506, + # Password of OpenVPN management interface (optional) + "password": "", + } + ], + # List of CALLED STATION IDs that has to be converted, + # These look like: 00:27:22:F3:FA:F1:gw1.openwisp.org + "unconverted_ids": [""], + } + } + +``OPENWISP_RADIUS_CONVERT_CALLED_STATION_ON_CREATE`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``False`` + +If set to ``True``, "Called Station ID" of a RADIUS session will be +converted (as per configuration defined in +:ref:`OPENWISP_RADIUS_CALLED_STATION_IDS +`) just after the RADIUS session is +created. + +.. _openwisp_radius_openvpn_datetime_format: + +``OPENWISP_RADIUS_OPENVPN_DATETIME_FORMAT`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``u'%a %b %d %H:%M:%S %Y'`` + +Specifies the datetime format of OpenVPN management status parser used by +the :ref:`convert_called_station_id ` +command. + +``OPENWISP_RADIUS_UNVERIFY_INACTIVE_USERS`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``0`` (disabled) + +Number of days from user's ``last_login`` after which the user will be +flagged as *unverified*. + +When set to ``0``, the feature would be disabled and the user will not be +flagged as *unverified*. + +``OPENWISP_RADIUS_DELETE_INACTIVE_USERS`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``0`` (disabled) + +Number of days from user's ``last_login`` after which the user will be +deleted. + +When set to ``0``, the feature would be disabled and the user will not be +deleted. + +API and user token related settings +----------------------------------- + +These settings control details related to the API and the radius user +token. + +``OPENWISP_RADIUS_API_URLCONF`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``None`` + +Changes the urlconf option of django urls to point the RADIUS API urls to +another installed module, example, ``myapp.urls`` (useful when you have a +seperate API instance.) + +``OPENWISP_RADIUS_API_BASEURL`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``/`` (points to same server) + +If you have a seperate instance of openwisp-radius API on a different +domain, you can use this option to change the base of the image download +URL, this will enable you to point to your API server's domain, example +value: ``https://myradius.myapp.com``. + +.. _openwisp_radius_api: + +``OPENWISP_RADIUS_API`` +~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``True`` + +Indicates whether the REST API of openwisp-radius is enabled or not. + +.. _openwisp_radius_disposable_radius_user_token: + +``OPENWISP_RADIUS_DISPOSABLE_RADIUS_USER_TOKEN`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``True`` + +Radius user tokens are used for authorizing users. + +When this setting is ``True`` radius user tokens are deleted right after a +successful authorization is performed. This reduces the possibility of +attackers reusing the access tokens and posing as other users if they +manage to intercept it somehow. + +.. _openwisp_radius_api_authorize_reject: + +``OPENWISP_RADIUS_API_AUTHORIZE_REJECT`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``False`` + +Indicates wether the :ref:`Authorize API view ` will +return ``{"control:Auth-Type": "Reject"}`` or not. + +Rejecting an authorization request explicitly will prevent freeradius from +attempting to perform authorization with other mechanisms (eg: radius +checks, LDAP, etc.). + +When set to ``False``, if an authorization request fails, the API will +respond with ``None``, which will allow freeradius to keep attempting to +authorize the request with other freeradius modules. + +Set this to ``True`` if you are performing authorization exclusively +through the REST API. + +``OPENWISP_RADIUS_API_ACCOUNTING_AUTO_GROUP`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``True`` + +When this setting is enabled, every accounting instance saved from the API +will have its ``groupname`` attribute automatically filled in. The value +filled in will be the ``groupname`` of the ``RadiusUserGroup`` of the +highest priority among the RadiusUserGroups related to the user with the +``username`` as in the accounting instance. In the event there is no user +in the database corresponding to the ``username`` in the accounting +instance, the failure will be logged with ``warning`` level but the +accounting will be saved as usual. + +.. _openwisp_radius_allowed_mobile_prefixes: + +``OPENWISP_RADIUS_ALLOWED_MOBILE_PREFIXES`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``[]`` + +This setting is used to specify a list of international mobile prefixes +which should be allowed to register into the system via the :ref:`user +registration API `. + +That is, only users with phone numbers using the specified international +prefixes will be allowed to register. + +Leaving this unset or setting it to an empty list (``[]``) will +effectively allow any international mobile prefix to register (which is +the default setting). + +For example: + +.. code-block:: python + + OPENWISP_RADIUS_ALLOWED_MOBILE_PREFIXES = ["+44", "+237"] + +Using the setting above will only allow phone numbers from the UK +(``+44``) or Cameroon (``+237``). + +.. note:: + + This setting is applicable only for organizations which have + :ref:`enabled the SMS verification option + `. + +``OPENWISP_RADIUS_ALLOW_FIXED_LINE_OR_MOBILE`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``False`` + +OpenWISP RADIUS only allow using mobile phone numbers for user +registration. This can cause issues in regions where fixed line and mobile +phone numbers uses the same pattern (e.g. USA). Setting the value to +``True`` would make phone number type checking less strict. + +.. _openwisp_radius_optional_registration_fields: + +``OPENWISP_RADIUS_OPTIONAL_REGISTRATION_FIELDS`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: + +.. code-block:: python + + { + "first_name": "disabled", + "last_name": "disabled", + "birth_date": "disabled", + "location": "disabled", + } + +This global setting is used to specify if the optional user fields +(``first_name``, ``last_name``, ``location`` and ``birth_date``) shall be +disabled (hence ignored), allowed or required in the :ref:`User +Registration API `. + +The allowed values are: + +- ``disabled``: (**default**) the field is disabled. +- ``allowed``: the field is allowed but not mandatory. +- ``mandatory``: the field is mandatory. + +For example: + +.. code-block:: python + + OPENWISP_RADIUS_OPTIONAL_REGISTRATION_FIELDS = { + "first_name": "disabled", + "last_name": "disabled", + "birth_date": "mandatory", + "location": "allowed", + } + +Means: + +- ``first_name`` and ``last_name`` fields are not required and their + values if provided are ignored. +- ``location`` field is not required but its value will be saved to the + database if provided. +- ``birth_date`` field is required and a ``ValidationError`` exception is + raised if its value is not provided. + +The setting for each field can also be overridden at organization level if +needed, by going to ``Home › Users and Organizations › Organizations > +Edit organization`` and then scrolling down to ``ORGANIZATION RADIUS +SETTINGS``. + +.. image:: ../images/optional_fields.png + :alt: optional field setting + +By default the fields at organization level hold a ``NULL`` value, which +means that the global setting specified in ``settings.py`` will be used. + +``OPENWISP_RADIUS_PASSWORD_RESET_URLS`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. note:: + + This setting can be overridden for each organization in the + organization admin page, the setting implementation is left for + backward compatibility but may be deprecated in the future. + +**Default**: + +.. code-block:: python + + { + "__all__": "https://{site}/{organization}/password/reset/confirm/{uid}/{token}" + } + +A dictionary representing the frontend URLs through which end users can +complete the password reset operation. + +The frontend could be :doc:`OpenWISP WiFi Login Pages +` or another in-house captive page solution. + +Keys of the dictionary must be either UUID of organizations or +``__all__``, which is the fallback URL that will be used in case there's +no customized URL for a specific organization. + +The password reset URL must contain the "{token}" and "{uid}" +placeholders. + +The meaning of the variables in the string is the following: + +- ``{site}``: site domain as defined in the `django site framework + `_ (defaults + to example.com and an be changed through the django admin) +- ``{organization}``: organization slug +- ``{uid}``: uid of the password reset request +- ``{token}``: token of the password reset request + +If you're using :doc:`OpenWISP WiFi Login Pages +`, the configuration is fairly simple, in case +the nodejs app is installed in the same domain of openwisp-radius, you +only have to ensure the domain field in the main Site object is correct, +if instead the nodejs app is deployed on a different domain, say +``login.wifiservice.com``, the configuration should be simply changed to: + +.. code-block:: python + + { + "__all__": "https://login.wifiservice.com/{organization}/password/reset/confirm/{uid}/{token}" + } + +.. _openwisp_radius_registration_api_enabled: + +``OPENWISP_RADIUS_REGISTRATION_API_ENABLED`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``True`` + +Indicates whether the API registration view is enabled or not. When this +setting is disabled (i.e. ``False``), the registration API view is +disabled. + +**This setting can be overridden in individual organizations via the admin +interface**, by going to *Organizations* then edit a specific organization +and scroll down to *"Organization RADIUS settings"*, as shown in the +screenshot below. + +.. image:: ../images/organization_registration_setting.png + :alt: Organization RADIUS settings + +.. note:: + + We recommend using the override via the admin interface only when + there are special organizations which need a different configuration, + otherwise, if all the organization use the same configuration, we + recommend changing the global setting. + +.. _openwisp_radius_sms_verification_enabled: + +``OPENWISP_RADIUS_SMS_VERIFICATION_ENABLED`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``False`` + +.. note:: + + If you're looking for instructions on how to configure SMS sending, + see :ref:`SMS Token Related Settings + `. + +If :ref:`Identity verification is required +`, this setting indicates +whether users who sign up should be required to verify their mobile phone +number via SMS. + +This can be overridden for each organization separately via the +organization radius settings section of the admin interface. + +.. image:: ../images/organization_sms_verification_setting.png + :alt: SMS verification enabled + +.. _openwisp_radius_needs_identity_verification: + +``OPENWISP_RADIUS_MAC_ADDR_ROAMING_ENABLED`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``False`` + +Indicates whether MAC address roaming is supported. When this setting is +enabled (i.e. ``True``), MAC address roaming is enabled for all +organizations. + +**This setting can be overridden in individual organizations via the admin +interface**, by going to *Organizations* then edit a specific organization +and scroll down to *"Organization RADIUS settings"*, as shown in the +screenshot below. + +.. image:: ../images/mac-address-roaming.png + :alt: Organization MAC Address Roaming settings + +.. note:: + + We recommend using the override via the admin interface only when + there are special organizations which need a different configuration, + otherwise, if all the organization use the same configuration, we + recommend changing the global setting. + +``OPENWISP_RADIUS_NEEDS_IDENTITY_VERIFICATION`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``False`` + +Indicates whether organizations require a user to be verified in order to +login. This can be overridden globally or for each organization separately +via the admin interface. + +If this is enabled, each registered user should be verified using a +verification method. The following choices are available by default: + +- ``''`` (empty string): unspecified +- ``manual``: manually created +- ``email``: Email (No Identity Verification) +- ``mobile_phone``: Mobile phone number :ref:`verification via SMS + ` +- ``social_login``: :doc:`social login feature ` + +.. note:: + + Of the methods listed above, ``mobile_phone`` is generally accepted as + a legal and valid form of indirect identity verification in those + countries who require to provide a valid ID document before buying a + SIM card. + + Organizations which are required by law to identify their users before + allowing them to access the network (eg: ISPs) can restrict users to + register only through this method and can configure the system to only + :ref:`allow international mobile prefixes + ` of countries which require + a valid ID document to buy a SIM card. + + **Disclaimer:** these are just suggestions on possible configurations + of OpenWISP RADIUS and must not be considered as legal advice. + +Adding support for more registration/verification methods ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +For those who need to implement additional registration and identity +verification methods, such as supporting a National ID card, new methods +can be added or an existing method can be removed using the +``register_registration_method`` and ``unregister_registration_method`` +functions respectively. + +For example: + +.. code-block:: python + + from openwisp_radius.registration import ( + register_registration_method, + unregister_registration_method, + ) + + # Enable registering via national digital ID + register_registration_method("national_id", "National Digital ID") + + # Remove mobile verification method + unregister_registration_method("mobile_phone") + +.. note:: + + Both functions will fail if a specific registration method is already + registered or unregistered, unless the keyword argument ``fail_loud`` + is passed as ``False`` (this useful when working with additional + registration methods which are supported by multiple custom modules). + + Pass ``strong_identity`` as ``True`` to to indicate that users who + register using that method have indirectly verified their identity + (eg: :ref:`SMS verification + `, credit card, national ID + card, etc). + +.. warning:: + + If you need to implement a registration method that needs to grant + limited internet access to unverified users so they can complete their + verification process online on other websites which cannot be + predicted and hence cannot be added to the walled garden, you can pass + ``authorize_unverified=True`` to the ``register_registration_method`` + function. + + This is needed to implement payment flows in which users insert a + specific 3D secure code in the website of their bank. Keep in mind + that you should create a specific limited radius group for these + unverified users. + + Payment flows and credit/debit card verification are fully implemented + in **OpenWISP Subscriptions**, a premium module available only to + customers of the `commercial support offering of OpenWISP + `_. + +Email related settings +---------------------- + +Emails can be sent to users whose usernames or passwords have been +auto-generated. The content of these emails can be customized with the +settings explained below. + +.. _openwisp_radius_batch_mail_subject: + +``OPENWISP_RADIUS_BATCH_MAIL_SUBJECT`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``Credentials`` + +It is the subject of the mail to be sent to the users. Eg: ``Login +Credentials``. + +.. _openwisp_radius_batch_mail_message: + +``OPENWISP_RADIUS_BATCH_MAIL_MESSAGE`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``username: {}, password: {}`` + +The message should be a string in the format ``Your username is {} and +password is {}``. + +The text could be anything but should have the format string operator +``{}`` for ``.format`` operations to work. + +.. _openwisp_radius_batch_mail_sender: + +``OPENWISP_RADIUS_BATCH_MAIL_SENDER`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``settings.DEFAULT_FROM_EMAIL`` + +It is the sender email which is also to be configured in the SMTP +settings. The default sender email is a common setting from the `Django +core settings +`_ +under ``DEFAULT_FROM_EMAIL``. Currently, ``DEFAULT_FROM_EMAIL`` is set to +to ``webmaster@localhost``. + +.. _radius_counter_related_settings: + +Counter related settings +------------------------ + +.. _radius_counters_setting: + +``OPENWISP_RADIUS_COUNTERS`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: depends on the database backend in use, see +:ref:`radius_counters` to find out what are the default counters enabled. + +It's a list of strings, each representing the python path to a counter +class. + +It may be set to an empty list or tuple to disable the counter feature, +eg: + +.. code-block:: python + + OPENWISP_RADIUS_COUNTERS = [] + +If custom counters have been implemented, this setting should be changed +to include the new classes, eg: + +.. code-block:: python + + OPENWISP_RADIUS_COUNTERS = [ + # default counters for PostgreSQL, may be removed if not needed + "openwisp_radius.counters.postgresql.daily_counter.DailyCounter", + "openwisp_radius.counters.postgresql.radius_daily_traffic_counter.DailyTrafficCounter", + # custom counters + "myproject.counters.CustomCounter1", + "myproject.counters.CustomCounter2", + ] + +.. _radius_traffic_counter_check_name: + +``OPENWISP_RADIUS_TRAFFIC_COUNTER_CHECK_NAME`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``Max-Daily-Session-Traffic`` + +Used by :ref:`radius_daily_traffic_counter`, it indicates the check +attribute which is looked for in the database to find the maximum amount +of daily traffic which users having the default ``users`` radius group +assigned can consume. + +.. _radius_traffic_counter_reply_name: + +``OPENWISP_RADIUS_TRAFFIC_COUNTER_REPLY_NAME`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``CoovaChilli-Max-Total-Octets`` + +Used by :ref:`radius_daily_traffic_counter`, it indicates the reply +attribute which is returned to the NAS to indicate how much remaining +traffic users which users having the default ``users`` radius group +assigned can consume. + +It should be changed according to the NAS software in use, for example, if +using PfSense, this setting should be set to ``pfSense-Max-Total-Octets``. + +``OPENWISP_RADIUS_RADIUS_ATTRIBUTES_TYPE_MAP`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``{}`` + +Used by :ref:`User Radius Usage API `, it stores +mapping of RADIUS attributes to the unit of value enforced by the +attribute, e.g. ``bytes`` for traffic counters and ``seconds`` for session +time counters. + +In the following example, the setting is configured to return ``bytes`` +type in the API response for ``ChilliSpot-Max-Input-Octets`` attribute: + +.. code-block:: python + + OPENWISP_RADIUS_RADIUS_ATTRIBUTES_TYPE_MAP = { + "ChilliSpot-Max-Input-Octets": "bytes" + } + +.. _radius_social_login_settings: + +Social Login related settings +----------------------------- + +The following settings are related to the :doc:`social login feature +`. + +.. _openwisp_radius_social_registration_enabled: + +``OPENWISP_RADIUS_SOCIAL_REGISTRATION_ENABLED`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``False`` + +Indicates whether the registration using social applications is enabled or +not. When this setting is enabled (i.e. ``True``), authentication using +social applications is enabled for all organizations. + +**This setting can be overridden in individual organizations via the admin +interface**, by going to *Organizations* then edit a specific organization +and scroll down to *"Organization RADIUS settings"*, as shown in the +screenshot below. + +.. image:: ../images/organization_social_login_setting.png + :alt: Organization social login settings + +.. note:: + + We recommend using the override via the admin interface only when + there are special organizations which need a different configuration, + otherwise, if all the organization use the same configuration, we + recommend changing the global setting. + +.. _radius_saml_settings: + +SAML related settings +--------------------- + +The following settings are related to the :doc:`SAML feature `. + +.. _openwisp_radius_saml_registration_enabled: + +``OPENWISP_RADIUS_SAML_REGISTRATION_ENABLED`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``False`` + +Indicates whether registration using SAML is enabled or not. When this +setting is enabled (i.e. ``True``), authentication using SAML is enabled +for all organizations. + +**This setting can be overridden in individual organizations via the admin +interface**, by going to *Organizations* then edit a specific organization +and scroll down to *"Organization RADIUS settings"*, as shown in the +screenshot below. + +.. image:: ../images/organization_saml_setting.png + :alt: Organization SAML settings + +.. note:: + + We recommend using the override via the admin interface only when + there are special organizations which need a different configuration, + otherwise, if all the organization use the same configuration, we + recommend changing the global setting. + +``OPENWISP_RADIUS_SAML_REGISTRATION_METHOD_LABEL`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``'Single Sign-On (SAML)'`` + +Sets the verbose name of SAML registration method. + +``OPENWISP_RADIUS_SAML_IS_VERIFIED`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``False`` + +Setting this to ``True`` will automatically flag user accounts created +during SAML sign-in as verified users +(``RegisteredUser.is_verified=True``). + +This is useful when SAML identity providers can be trusted to be legally +valid identity verifiers. + +.. _openwisp_radius_saml_updates_pre_existing_username: + +``OPENWISP_RADIUS_SAML_UPDATES_PRE_EXISTING_USERNAME`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``False`` + +Allows updating username of a registered user with the value received from +SAML Identity Provider. Read the :ref:`FAQs in SAML integration +documentation ` for +details. + +.. _radius_sms_token_related_settings: + +SMS token related settings +-------------------------- + +These settings allow to control aspects and limitations of the SMS tokens +which are sent to users for the purpose of :ref:`verifying their mobile +phone number `. + +These settings are applicable only when :ref:`SMS verification is enabled +`. + +``SENDSMS_BACKEND`` +~~~~~~~~~~~~~~~~~~~ + +This setting takes a python path which points to the `django-sendsms +`__ backend which will be +used by the system to send SMS messages. + +The list of supported SMS services can be seen in the source code of `the +django-sendsms backends +`__. +Adding support for other SMS services can be done by subclassing the +``BaseSmsBackend`` and implement the logic needed to talk to the SMS +service. + +The value of this setting can point to any class on the python path, so +the backend doesn't have to be necessarily shipped in django-sendsms but +can be deployed in any other location. + +``OPENWISP_RADIUS_SMS_TOKEN_DEFAULT_VALIDITY`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``30`` + +For how many minutes the SMS token is valid for. + +``OPENWISP_RADIUS_SMS_TOKEN_LENGTH`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``6`` + +The length of the SMS token. + +``OPENWISP_RADIUS_SMS_TOKEN_HASH_ALGORITHM`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``'sha256'`` + +The hashing algorithm used to generate the numeric code. + +``OPENWISP_RADIUS_SMS_COOLDOWN`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``30`` + +Seconds users needs to wait before being able to request a new SMS token. + +``OPENWISP_RADIUS_SMS_TOKEN_MAX_ATTEMPTS`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``5`` + +The max number of mistakes tolerated during verification, after this +amount of mistaken attempts, it won't be possible to verify the token +anymore and it will be necessary to request a new one. + +``OPENWISP_RADIUS_SMS_TOKEN_MAX_USER_DAILY`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``5`` + +The max number of SMS tokens a single user can request within a day. + +``OPENWISP_RADIUS_SMS_TOKEN_MAX_IP_DAILY`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``999`` + +The max number of tokens which can be requested from the same IP address +during the same day. + +``OPENWISP_RADIUS_SMS_MESSAGE_TEMPLATE`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Default**: ``{organization} verification code: {code}`` + +The template used for sending verification code to users via SMS. + +.. note:: + + The template should always contain ``{code}`` placeholder. Otherwise, + the sent SMS will not contain the verification code. + +This value can be overridden per organization in the organization change +page. You can skip setting this option if you intend to set it from +organization change page for each organization. Keep in mind that the +default value is translated in other languages. If the value is customized +the translations will not work, so if you need this message to be +translated in different languages you should either not change the default +value or prepare the additional translations. diff --git a/docs/user/social_login.rst b/docs/user/social_login.rst new file mode 100644 index 00000000..4dcfac60 --- /dev/null +++ b/docs/user/social_login.rst @@ -0,0 +1,134 @@ +Social Login +============ + +.. important:: + + The social login feature is disabled by default. + + In order to enable this feature you have to follow the :ref:`setup + instructions ` below and then activate it + via :ref:`global setting or from the admin interface + `. + +Social login is supported by generating an additional temporary token +right after users perform the social sign-in, the user is then redirected +to the captive page with two querystring parameters: ``username`` and +``token``. + +The captive page must recognize these two parameters and automatically +perform the submit action of the login form: ``username`` should obviously +used for the username field, while ``token`` should be used for the +password field. + +The internal REST API of openwisp-radius will recognize the token and +authorize the user. + +This kind of implementation allows to implement the social login with any +captive portal which already supports the RADIUS protocol because it's +totally transparent for it, that is, the captive portal doesn't even know +the user is signing-in with a social network. + +.. note:: + + If you're building a public wifi service, we suggest to take a look at + :doc:`OpenWISP WiFi Login Pages `, which is + built to work with openwisp-radius. + +.. _radius_setup_social_login: + +Setup +----- + +Install ``django-allauth``: + +:: + + pip install django-allauth[socialaccount] + +Ensure your ``settings.py`` looks like the following (we will show how to +configure of the facebook social provider): + +.. code-block:: python + + INSTALLED_APPS = [ + # ... other apps .. + # apps needed for social login + "rest_framework.authtoken", + "django.contrib.sites", + "allauth", + "allauth.account", + "allauth.socialaccount", + # showing facebook as an example + # to configure social login with other social networks + # refer to the django-allauth documentation + "allauth.socialaccount.providers.facebook", + ] + + SITE_ID = 1 + + # showing facebook as an example + # to configure social login with other social networks + # refer to the django-allauth documentation + SOCIALACCOUNT_PROVIDERS = { + "facebook": { + "METHOD": "oauth2", + "SCOPE": ["email", "public_profile"], + "AUTH_PARAMS": {"auth_type": "reauthenticate"}, + "INIT_PARAMS": {"cookie": True}, + "FIELDS": [ + "id", + "email", + "name", + "first_name", + "last_name", + "verified", + ], + "VERIFIED_EMAIL": True, + } + } + +Ensure your main ``urls.py`` contains the ``allauth.urls``: + +.. code-block:: python + + urlpatterns = [ + # .. other urls ... + path("accounts/", include("allauth.urls")), + ] + +Configure the social account application +---------------------------------------- + +Refer to the django-allauth documentation to find out `how to complete the +configuration of a sample facebook login app +`_. + +Captive page button example +--------------------------- + +Following the previous example configuration with facebook, in your +captive page you will need an HTML button similar to the ones in the +following examples. + +This example needs the slug of the organization to assign the new user to +the right organization: + +.. code-block:: html + + Log in with Facebook + + +Substitute ``openwisp2.mywifiproject.com``, +``captivepage.mywifiproject.com`` and ``default`` with the hostname of +your openwisp-radius instance, your captive page and the organization slug +respectively. + +Alternatively, you can take a look at :doc:`OpenWISP WiFi Login Pages +`, which provides buttons for Facebook, Google +and Twitter by default. + +Settings +-------- + +See :ref:`social login related settings `.