Viva-con-Agua / drops

Implements an user management service for a microservice application, supporting Viva con Agua in organizing volunteering activities.
GNU General Public License v3.0
1 stars 1 forks source link

Drops

This is a user management component for a micro-component application, supporting Viva con Agua in organizing volunteering activities. The component is based on the Play framework, Silhouette and the dwPlayDemo, implementing a basic user management. The authentication will be explained by Pablo Pedemonte from IBM. It provides:

Install

Drops can be deployed using Docker. The drops.informatik.hu-berlin.de config file (in conf/ directory) contains all configuration information needed to run the service on production. Additionally, the following BASH scripts should be used:

#!/bin/bash
docker pull mongo
docker pull cses/drops:0.9.0

(install.sh)

#!/bin/bash
docker run --name drops-mongo --restart=unless-stopped -d mongo

docker run --name drops --link drops-mongo:mongo --restart=unless-stopped -v $(pwd)/drops.informatik.hu-berlin.de.p12:/certs.p12 -h drops.informatik.hu-berlin.de -p 443:9443 cses/drops:0.9.0 \
    -Dconfig.resource=drops.informatik.hu-berlin.de.conf \
    -Dhttps.port=9443 \
    -Dhttps.address=drops.informatik.hu-berlin.de \
    -Dhttp.address=drops.informatik.hu-berlin.de \
    -Dhttp.port=disabled \
    -Dplay.server.https.keyStore.path=/certs.p12 \
    -Dplay.server.https.keyStore.type=PKCS12 \
    -Dplay.server.https.keyStore.password=61aca05eb0eb07c4c0c53f35b7edf3e1 \
    -Dmongodb.uri=mongodb://mongo/drops \
    -J-Xms128M -J-Xmx512m -J-server \
    > server-output 2>&1 &

docker pull mariadb:latest
docker run --name drops-mariadb \
    -e MYSQL_ROOT_PASSWORD=admin \
    -e MYSQL_DATABASE=drops \
    -e MYSQL_USER=drops \
    -e MYSQL_PASSWORD=STRONG_PASSWORD \
    -d mariadb

(start.sh)

#!/bin/bash
docker stop drops-mongo
docker stop drops-mariadb
docker stop drops

(stop.sh)

#!/bin/bash
docker rm drops-mongo
docker rm drops-mariadb
docker rm drops

(remove.sh)

Notice: All server generated output will be written to the server-output file. This is also needed to confirm users since the production server also uses a mock mail server.

After the default Play 2 App production deployment, the system requires the call of the route /auth/init. This call creates a default admin account using a configured Email and Password. Both can be changed inside the admin.conf. Additionally the same can be done for crews. The route that should be used is /crews/init.

OAuth 2 Handshake

You can add a specific URL to the configuration that will be used for redirect after login. By default routes.Application.index is used, but you can add:

login.flow {
  ms.switch=true
  ms.url=/pool/
}

to your application.conf. ms.url can hold every valid URL (absolute and relative).

Dummy Data

Using an admin account test users can be generated. For generating the following route has to be called: /users/init/:count/:countSpecialRoles, where :count has to be replaced by the count of new users that should be generated and :countSpecialRoles by the number of users with special roles (next to "Supporter").

Currently, the generation does not uses bulk inserts, so an insert operation will be executed for each test user. As a consequence, the system needs a lot of time.

Webservice

Results

The Drops service implements a webservice for requesting users and crews. Users will be described by the following JSON that is also returned to a valid request:

{
    "id": "f2329fe0-c94b-4b33-a039-296c1a7dcba6",
    "profiles": [
      {
        "loginInfo": {
          "providerID": "credentials",
          "providerKey": "test@test.com"
        },
        "primary": true,
        "confirmed": true,
        "email": "test@test.com",
        "supporter": {
          "firstName": "Tester",
          "lastName": "Tester",
          "fullName": "Tester Tester",
          "mobilePhone": "0000/0000000",
          "placeOfResidence": "Hamburg",
          "birthday": 315529200000,
          "sex": "male",
          "crew": {
            "crew": {
              "name": "Berlin",
              "country": "DE",
              "cities": [
                "Berlin"
              ]
            },
            "active": true
          },
          "pillars": [
            {
              "pillar": "operation"
            },
            {
              "pillar": "finance"
            }
          ]
        }
      }
    ],
    "roles": [
      {
        "role": "supporter"
      }
    ]
  }

A user consists of an ID, multiple profiles and multiple roles. Drops implements different ways to create a virtual representation for a user. So he or she could use default credentials or an existing Google or Facebook account. Also the users could connect their Drops accounts to Google or Facebook Accounts. In order to establish which profile should be used, the primary flag marks the so called profile. Additionally, it is possible to detect if an user has confirmed his or her account using the confirmation mail after the sign up (confirmed flag).

Next to the master data associated to the Supporter, there are two information specific to Viva con Agua:

Currently, four different roles are implemented: supporter (default role), volunteerManager, employee and admin. Supporters are all volunteering people of Viva con Agua, while a volunteer manager is a supporter that coordinates a crew. So, these roles are connected and one crew can have multiple volunteer managers. An employee is not volunteering, but works for Viva con Agua and coordinates all entities inside the social system (means crews and supporter). Contrary to the other roles the admin is more technical. Users holding this role are able to access all possible configurations of the system.

Also the webservice will describe a crew by the following JSON:

{
  "id": "4e899ba5-2897-4500-acdc-7ce998b033db",
  "name": "Berlin",
  "country": "DE",
  "cities": [
    "Berlin"
  ]
}

A crew has a name, a country code (described using the 2-Alpha codes of the ISO 3166-1) and a set of cities. Maybe there are regions with a lot of small cities or towns and a working infrastructure, where multiple volunteers in different cities join to one crew.

Access

There are three entry points implemented:

There are three query parameter those have to be defined:

Currently, there are two different versions of the webservice:

All the mentioned above entry points are routes using the HTTP method POST and the body of these requests can contain a query JSON like the following example (Version 1.0.0):

{
    "filterBy" : {
        "page" :  {
            "lastId": "f2329fe0-c94b-4b33-a039-296c1a7dcba6",
            "countsPerPage": 100
        },
        "search" : [
            {
                "keyword" : "Berlin",
                "fields": ["profiles.supporter.crew.crew.name"]
            },
            {
                "keyword" : "Test",
                "fields": ["profiles.supporter.firstName", "profiles.supporter.lastName"]
            }
        ],
        "groups" : [
            {
                "groupName" : "supporter",
                "area" : { "name" : "role" }
            },
            {
                "groupName" : "finance",
                "area" : { "name" : "pillar" }
            }   
        ]
    },
    "sortBy" : [
        {
            "field": "profiles.supporter.firstName",
            "dir" : "asc"
        }
    ]
}

If the crews webservice is requested, the groups filter will be ignored. Additionally, the lastId inside the page filter can be used for the crews name.

Since version 1.1.0 the following addional parameter is allowed:

{
    "filterBy" : {
        "all" : true
    }
}

all is a boolean parameter and it's also optional. If this parameter is set to true a possibly given page filter will be ignored.

The filterBy JSON block reduces the resulting set. It is possible to reduce the set using a pagination (lastId is optional and if given the object with this ID won't be returned), a search query (Regex based - it found every object that contains the given keyword as a substring of it's value inside one of the given fields) and groups (returns each object that is part of all given groups).

Using the sortBy list of fields the results can be ordered. The sorting criteria will be applied in the given order.

Note: Every POST request to an entry points has to set the HTTP header Content-Type: application/json

OAuth2 based session handshake

Drops allows other services to intiate an OAuth 2 handshake. The service implements an OAuth 2 server and allows authorization_code and password based authentication. Assuming that all other services are part of the official infrastructure, Drops implements a small addition to the OAuth 2 standard: The authorization_code authentication does not require additional permission by the user.

Basically, your service will make an HTTP redirect to the Drops service, so Drops can check if there exists a session for the redirected client. If there exists no session until now, Drops will show a login screen and the can access using his or her default credentials. Otherwise Drops will generate an authorization code (based on the assumption that all registered services are part of the official infrastructure. So, you can trust these services) and redirects back to your service. Now, your service is able to request an access token using a webservice provided by Drops and to request the users profile by another webservice that is also provided by Drops and requires a valid access token as parameter.

Preparation

Your service has to be registered in Drops. For this purpose you have to send a mail to the Drops-Service administrator containing the following information:

Info: Don't forget possible special signs at the end of the redirectUri (e.g. / or ?code=), because Drops simply concatinates the given URI and the generated code.

Additionally, it should be obvious that you have to know the URL of the Drops Service you want to connect to.

Implementation

Implementing the handshake is very simple and consists of three steps:

(1) Implement an URL path pointing to an action of your service that redirects (HTTP 303 or HTTP 302) to:

<drops_url>/oauth2/code/get?client_id=<client_id>&response_type=code&state=<state>&redirect_uri=<redirect_uri>

The variables <drops_url>, <client_id> and <redirect_uri> have been defined during preparation phase. state can be used to save the current state of the OAuth2 client across redirects. Additionally, you can add the query paramater ajax indicating if the current redirects was issued by an ajax request. In that case no login screen will be shown, but a JSON encoded error message is returned.

(2) The action handling the redirectUri has to be implemented. This action will be accessed by an HTTP redirect initiated by the Drops service. It receives a code by a query parameter or inside the URL path and uses this code to receive an OAuth 2 AccessToken. For this purpose it calls the Drops service directly using the webservice endpoint <drops_url>/oauth2/access_token and the following query parameter:

(3) The responded AccessToken can be used to request the users profile, by requesting another webservice supplied by the Drops service:

An error-free response of the request for an access token will be a JSON:

{
    "token_type" : "some_string",
    "access_token" : "a random string",
    "expires_in" : a long,
    "refresh_token" : "a random string"
}

The key access_token of such an response should be used as value of the variable access_token used in step 3.

An error-free response of the request for a profile will be a JSON describing a user as shown before.

Using this information your service is able to initiate a session for the user and your service.

ChangeLog

Version 0.36.61 (2019-4-16)

Version 0.19.14 (2017-12-14)

Version 0.9.4 (2017-01-26)