Skip to content

Developing

Erkki Hietaranta edited this page Feb 18, 2024 · 1 revision

Start developin

Installing prerequisites

In order to run your local development environment of mulysa, there are some prerequisites you first need to install.

on Debian 11.2

sudo apt install git python3-dev gettext pipenv default-libmysqlclient-dev

Installing prerequisites on Mac OS

Make sure you have Homebrew installed, then run:

brew install git
brew install pipenv

Note: Homebrew will automatically install python for you since it is a prerequisite of pipenv.

brew install mysql
brew install gettext

Installing prerequisites on other platforms

Find a way to install the software on this list (click the links to find installer downloads):

Setup dev enviorment

When you have the prerequisites installed, run these commands:

git clone https://github.com/TampereHacklab/mulysa.git
cd mulysa
pipenv sync --dev
pipenv shell

Create your own drfx/settings_local.py file with at least this to get cookies working without ssl

# revert back to cookie names that work in dev
SESSION_COOKIE_NAME = '__NotReallyHost-sessionid'
LANGUAGE_COOKIE_NAME = '__NotReallyHost-language'
CSRF_COOKIE_NAME = '__NotReallyHost-csrf'

First run

./manage.py migrate --skip-checks
./manage.py loaddata memberservices
./manage.py runserver

To login devenviorment default admin credintials are user: [email protected] pass: rootme

To update localizations

Always start everything by opening the pipenv shell for this project first! (pipenv shell) or by prepending individual commands with (pipenv run) for system users that does not have shell access normally.

./manage.py makemessages -l fi

Edit the .po files

./manage.py compilemessages

Push only the .po files to git, not .mo's!

to update dependecies

run:

pipenv update
pipenv lock
pipenv sync
tox

to update local bootstrap files

This will download some js and css files from external sites to be hosted locally.

run:

./manage.py update_local_bootstrap

the command spits out the new filenames that need to be updated to the settings.py file.

  • update settings.py with the new values
  • commit all the files

Style checks & tests

Before committing, run

  • black

Running all test cases

./manage.py test

Running just one test case

To speed up writing your tests you can run only one test case with something like this

./manage.py test api.tests.TestAccess.test_access_phone_list_unauthenticated

Future improvements

  • Automate fetching and processing of member payment data from bank

Door access api

There are multiple api endpoints for checking door access: One phone number, nfc card and Matrix ID.

Phone number based access is based on the users phone number and they must have a active subscription to the default service (on a default installation this would be serviceid=2 "tilankäyttöoikeus"). User can also have multiple NFC cards that check the same service access.

All endpoints expect the same data. a device id which needs to be first added to access devices (this is for future, there might be multiple doors with diffrent levels of access for example) and the payload (the phone number, nfc card id or Matrix ID).

Examples:

curl -X POST \
  http://127.0.0.1:8000/api/v1/access/nfc/ \
  -H 'Content-Type: application/json' \
  -d '{
     "deviceid": "123",
     "payload": "ABC321"
   }'
curl -X POST \
  http://127.0.0.1:8000/api/v1/access/phone/ \
  -H 'Content-Type: application/json' \
  -d '{
     "deviceid": "123",
     "payload": "+358123123123"
   }'
curl -X POST \
  http://127.0.0.1:8000/api/v1/access/mxid/ \
  -H 'Content-Type: application/json' \
  -d '{
     "deviceid": "123",
     "payload": "@user:server.org"
   }'

HTTP 200 responses can be considered valid access, all other are invalid. 200 responses will also contain some basic user data for example for showing in a door access welcome message.

API will return these HTTP status code responses on certain error conditions:

  • 400 when query has invalid content
  • 404 when deviceid isn't found
  • 480 when phone number/NFC id/mxid is not found at all
  • 481 when phone number/NFC id/mxid is found within member but has no access rights, response will also contain basic user data for example executing proper procedures and admin logs
  • 429 if rate throttling kicks in

There are two example implementations for esp32 based access readers that can be found here:

https://github.com/TampereHacklab/mulysa_callerid https://github.com/TampereHacklab/mulysa_nfc_reader

Door access api listings

Door access information can also be fetched for local checking and caching.

curl -X GET -H "Authorization: Token xxxxxxxxxxxxxx" http://127.0.0.1:8000/api/v1/access/phone/

will return a list of users with access to the door. Calling this endpoint you must be authenticated and have superadmin permissions.

use oauth auth (advanced usage with keycloak integration)

These two settings are a bit intermingled, you need the redirect url in mulysa to create the app to get the id and secret and you need the id and secret to create the keycloack end to get the redirect url :D

Just do one first with either dummy data or good guess then do the other.

as a logged in admin user in mulysa

  • go to: https://yourmulysadomain/admin/oauth2_provider/application/
  • click on add application
    • clientid: autogenerated value works fine
    • user: leave empty
    • redirect uris: the url from keycloack
    • client type: confidential
    • authorization grant type: auth code
    • clientsecret: autogenerated value works fine (keep it secret!)
    • Name: keycloack
    • Skip authorisation: checked (this will skip one extra step after login)
    • algorithm: HMAC with SHA-2 256 (or RSA but then you need to create the key etc)
  • take your clientid and client secret and endpoint and configure them in keycloak

as a logged in admin user in keycloak

  • go to [realm] -> identity providers -> add provider -> openid connect v1.0

  • create new (only changes to defaults listed here)

  • go to [realm] -> identity providers -> [display name] -> Mappers

  • create

    • name: firstnamemapper
    • Sync Mode Override: force
    • Mapper Type: Attribute Importer
    • Claim: firstName
    • User Attribute Name: firstName
    • save
  • go to [realm] -> identity providers -> [display name] -> Mappers

  • create

    • name: lastnamemapper
    • Sync Mode Override: force
    • Mapper Type: Attribute Importer
    • Claim: lastName
    • User Attribute Name: lastName
    • save

fingers crossed

just try it out :)