athena-test/docs/Dipal/Backend/Kaiser/Third-party services.md

6.6 KiB

Third-party services

Keycloak

We use Keycloak for the user management because it is a comfortable and multifunctional system. All passwords are stored in a secure database, making it impossible to retrieve them.

For username we use user's phone number.

How do we store passwords?

Our system has no passwords, instead we send an SMS each time a user logs in, where he receives a 6-digit one-time password (OTP). This OTP expires after OTP_EXPIRATION_TIME milliseconds. The user will not be able to use this OTP until NEXT_TRY_TIME milliseconds expires. Both OTP_EXPIRATION_TIME and NEXT_TRY_TIME default to 60000.

Inside Keycloak in account attributes we have timers:

  • next_try_timestamp - the time stamp of the next attempt, which will be possible, according to the server time
  • otp_expiration_timestamp - the time stamp when otp will be expired, according to the server time

In the development version, we use the 111111 mock for OTPs of any account.

When user encrypt the OTP with an init vector (IV) and a security key.

  • We generate random 6-digit token (OTP) and send in to the user with SMS.
  • We get the security key from the environment variables.
  • We generate a random init vector, add OTP and salt from code, and store it as the password of the Keycloak account.
  • We store the result of encryption in the session_data attribute.

When the user logs in (enters his OTP), we use session_data as a raw data and OTP + salt as a init vector.

This means that the data to be accessed is stored in several parts:

  • Kubernetes secrets or enviromental variables (security key).
  • Keycloak (as a password and as an attribute).
  • Code constants (this is where we store the salt).

Without all this data, a hacker will not be able to gain access to someone else's account. Moreover, no one but the user himself can see the OTP sent to him.

Roles

We store information about roles (superadmin, admin, user) inside Keycloak. Superadmin has full control over the system. Admin can handle only those objects he is responsible for. There are different privileges for administrators and users. User is just a user of the Dipal app. Only superadmin and admins have access to the admin panel (Zoo).

HTTPS required

If you try to connect to Keycloak on a server by external IP, it will throw HTTPS required error. To fix it go to a Docker container at first. If you use Docker Compose, cd to yml directory and:

sudo docker-compose exec keycloak sh

If you are using Docker:

sudo docker exec <ID> sh

After you enter the container, execute some instructions:

cd /opt/jboss/keycloak/bin
./kcadm.sh config credentials --server http://localhost:8080/auth --realm master --user admin
./kcadm.sh update realms/master -s sslRequired=NONE

"I accidentally deleted an admin account"

If you deleted an admin account, go to the container and execute this from the bin directory:

./add-user-keycloak.sh --server http://ip_address_of_the_server:8080/admin --realm master --user admin --password adminPassword

Firebase

Firebase is a set of hosting services for any type of application. We use the messaging system from it to send data from Dipal to the user, such as push notifications. We set up a websocket connection between Dipal and Firebase servers and between Dipal and Firebase clients.

Integration with Dipal

To connect Firebase to Pigeons, we've created an app inside Firebase settings and saved google-services.json. This information must be set in the Pigeon .env file. The frontend part should send a request for a registration token from Firebase. It then sends the received registration token to Kaiser, and the backend part stores it in the user's Keycloak attribute of the registration_token. When the system needs to send a notification to this user, it will take the registration token from Keycloak and send the data to the Firebase server.

vapidKey is not a token. The token looks like that:

eUyCZGz_QMKZIzI7QOAq-U:APA91bGTymTn4doKJNl_aBVVqHKXr9y2JyvLjlhzJosPJUYsXBHCEbZL3A1rEpVUZOuvN8UQvlO1_A2bx-ha8lVdHUJ0RDVpyQ3BTxoY6qO2-QcK1ySxAmn5_NXgsOEEjyywh_eyuiZG

Every notification has data and push fields. If the notification doesn't have push notification (e.g. intercom call) , it doesn't send push field.

Notification example:

{
 "data": {
   "type": "user_place",
   "action": "request_confirmed",
   "member_name": "Кирилл Некрасов",
   "place_name": "flat 7"
 },
 "push": {
   "title": "Place request was accepted!",
   "body": "Кирилл Некрасов confirmed your request to flat 7!"
 }
}

Apache Kafka

Apache Kafka guarantees message delivery and is adapted to microservice architecture.

Rules for sending messages

Early we used the following format for Kakfa topics: from_<microservice>_to_<miscoservice>_to_<action>_v1_0_0, e.g from_zoo_to_crow_to_delete_settings_v1_0_0. As you can see, source and destination services are defined statically.

Now we use another rule for the Kafka topics: <microservice>.<module>.<function>, e.g crow.settings.delete. Now it doesn't matter who the message came from.

Redis

Redis (from the remote dictionary server) - the resident database management system class NoSQL with open source, working with data structures such as "key - value".

Billing systems

On the future our system is supposed to use various billing systems, but now it only supports Comfortel (UUT-Telecom) billing system.

Every account in our billing system has an account number. This account number is specified in the contract with UUT-Telecom. To establish a connection to the billing system server, your server's external IP address must be on the list of allowed billing system IP addresses. At first you need to take an access token for the billing system. The test account is 9999999 with the password password. After that you can get a list of information about the user.

We save the account_number in user collection of our database.

Filebeat

MongoDB

MongoDB is a non-relational document database that provides support for JSON-like storage. The data is stored inside documents with a similar data structure, which themselves are stored in different collections. One collection is for one type of records.

To make a private key for a document, you can create an index for it.

You can use MongoDB Compass to work with our databases.