Commit df1bc7c1 authored by Manuel's avatar Manuel

started usermanagement

added options to:
- add user
- delete user
- fetch users
- authenticate with username/password
parent dfc75504
## Rabbit MQ
RABBIT_MQ_HOSTNAME = 'rabbit-mq'
RABBIT_MQ_PORT = 5672
# RABBIT_MQ_HOSTNAME = 'articonf1.itec.aau.at'
# RABBIT_MQ_PORT = 30302
# RABBIT_MQ_HOSTNAME = 'rabbit-mq'
# RABBIT_MQ_PORT = 5672
RABBIT_MQ_HOSTNAME = 'articonf1.itec.aau.at'
RABBIT_MQ_PORT = 30302
## Trace Retrieval
TRACE_RETRIEVAL_HOSTNAME = 'trace-retrieval'
......@@ -21,3 +21,11 @@ ROLESTAGE_DISCOVERY_HOSTNAME = 'role-stage-discovery'
ROLESTAGE_DISCOVERY_REST_PORT = 80
ROLESTAGE_DISCOVERY_DB_HOSTNAME = f'{ROLESTAGE_DISCOVERY_HOSTNAME}-db'
ROLESTAGE_DISCOVERY_DB_PORT = 27017
## Rest Gateway
# REST_GATEWAY_HOSTNAME = 'rest-gateway'
REST_GATEWAY_HOSTNAME = RABBIT_MQ_HOSTNAME
REST_GATEWAY_REST_PORT = 30401
REST_GATEWAY_DB_HOSTNAME = RABBIT_MQ_HOSTNAME
REST_GATEWAY_DB_PORT = 30402
......@@ -13,6 +13,83 @@ basePath: "/api"
# Paths supported by the server application
paths:
/tokens:
post:
operationId: "rest.user.authenticate"
tags:
- "User"
summary: "Authenticates user at the backend"
description: "Authenticates user at the backend creating a JWT token in the backend"
parameters:
- in: body
name: "Object"
required: true
schema:
$ref: '#/definitions/TokenRequest'
responses:
'200':
description: "Authentication successful"
schema:
$ref: "#/definitions/TokenReply"
'400':
description: "Wrong credentials"
/users/username/{username}:
delete:
operationId: "rest.user.delete"
tags:
- "User"
summary: "Deletes a user identified by the username from the database"
description: "Deletes a user identified by the username from the database"
parameters:
- name: "username"
in: "path"
description: "Username of the user to be deleted"
required: true
type: "string"
responses:
'200':
description: "Deletion succeeded"
'400':
description: "User does not exist"
/users:
get:
operationId: "rest.user.all"
tags:
- "User"
summary: "Retrieves all users from the database"
description: "Retrieves all users from the database"
responses:
'200':
description: complete user object including numeric ID
schema:
$ref: "#/definitions/User"
'400':
description: wrong username or password
post:
operationId: "rest.user.add"
tags:
- "User"
summary: "Adds a new user to the database"
description: "Adds a new user to the database"
parameters:
- in: body
name: "Object"
required: true
schema:
type: object
properties:
username:
type: string
example: "username@domain.com"
password:
type: string
example: "secure_passw0rd"
responses:
200:
description: "User was added to the database"
400:
description: "User already exists"
/debug:
post:
operationId: "rest.debug.echo"
......@@ -51,6 +128,50 @@ paths:
description: "Invalid input"
definitions:
TokenReply:
type: "object"
required:
- token
properties:
token:
type: string
example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.Et9HFtf9R3GEMA0IICOfFMVXY7kkTX1wr4qCyhIf58U"
TokenRequest:
type: "object"
required:
- username
- password
properties:
username:
type: string
example: "username@domain.com"
password:
type: string
example: "secure_passw0rd"
User:
type: "object"
required:
- username
- password
- role
- created_at
- last_login
properties:
username:
type: string
example: "username@domain.com"
password:
type: string
example: "secure_passw0rd"
role:
type: string
example: "u"
created_at:
type: string
example: "2020-07-14 14:37:31.670671"
last_login:
type: string
example: "2020-07-14 14:37:31.670671"
BlockchainTrace:
type: "object"
properties:
......
import json
from typing import Dict
from datetime import datetime
class User:
'''
This class represents a user in the SMART system
'''
def __init__(self, username : str, password : str, role : str ="u"):
'''
Initializes the new user object with the given data
@params:
username - Required : unique identifier for the user i.e. an E-Mail address
password - Required : raw, unhashed password used to authenticate the user later
role - Optional : indicates the privileges of the user "u": standard user
'''
self.username = username
self.password = password
self.created_at = str(datetime.now())
self.last_login = str(datetime.now())
self.role = role
def to_serializable_dict(self) -> Dict:
return {
"username": self.username,
"password": self.password,
"role": self.role,
"created_at": self.created_at,
"last_login": self.last_login,
}
@staticmethod
def from_serializable_dict(user_dict: Dict):
result = User(user_dict["username"], user_dict["password"], user_dict["role"])
result.created_at = user_dict["created_at"]
result.last_login = user_dict["last_login"]
return result
def __repr__(self):
return json.dumps(self.to_serializable_dict())
def __str__(self):
return f"User({self.__repr__()})"
# global imports (dont't worry, red is normal)
import network_constants as netconst
from database.MongoRepositoryBase import MongoRepositoryBase
import pymongo
import json
from db.entities.user import User
from typing import List
class Repository(MongoRepositoryBase):
'''This is a repository for MongoDb.'''
def __init__(self):
super().__init__(netconst.REST_GATEWAY_DB_HOSTNAME,
netconst.REST_GATEWAY_DB_PORT,
'rest-gateway-db')
self._user_collection = 'user'
def one_by_username(self, username : str) -> User:
return list(super().get_entries(self._user_collection, selection={"username": username}))
def add(self, user : User):
super().insert_entry(self._user_collection, user.to_serializable_dict())
def delete_all_with_username(self, username: str):
collection = self._database[self._user_collection]
collection.delete_many({"username": username})
# TODO maybe movable to MongoRepositoryBase?
def all(self) -> List[User]:
result = super().get_entries(self._user_collection, projection={'_id': False})
return list(result)
# def get_layers(self) -> List[Layer]:
# entries = super().get_entries(self._layer_collection)
# return [Layer(e) for e in entries]
# def get_layer(self, layer_name) -> Layer:
# entries = super().get_entries(self._layer_collection, selection={'layer_name': layer_name})
# entries = [Layer(e) for e in entries]
# if entries is not None and len(entries) > 0:
# return entries[0]
# else:
# return None
# def add_layer_node(self, node: dict):
# super().insert_entry(self._layer_nodes_collection, node)
# def add_layer_nodes(self, nodes:List[dict]):
# super().insert_many(self._layer_nodes_collection, nodes)
# def get_layer_nodes(self, layer_name: str) -> dict:
# '''Returns all nodes for the layer.'''
# entries = super().get_entries(self._layer_nodes_collection, selection={'layer_name': layer_name}, projection={'_id': 0})
# return [e for e in entries]
# #endregion
# #region Clusters
# def add_clusters(self, clusters: List[Cluster]):
# cluster_dicts = [c.to_serializable_dict(for_db=True) for c in clusters]
# super().insert_many(self._clusters_collection, cluster_dicts)
# def get_clusters_for_layer(self, layer_name: str) -> List[Cluster]:
# entries = super().get_entries(self._clusters_collection, selection={'layer_name': layer_name}, projection={'_id': 0})
# return [Cluster(cluster_dict=e, from_db=True) for e in entries]
# #endregion
# #region TimeSlice
# def add_time_slice(self, timeslice: TimeSlice):
# super().insert_entry(self._time_slice_collection, timeslice.to_serializable_dict(for_db=True))
# def get_time_slices(self) -> List[TimeSlice]:
# '''Returns all time slices.'''
# entries = super().get_entries(self._time_slice_collection)
# return [TimeSlice(None, None, time_slice_dict=e, from_db=True) for e in entries]
# def get_time_slices_by_name(self, layer_name) -> List[TimeSlice]:
# '''Returns all time slices with the given layer_name.'''
# entries = super().get_entries(self._time_slice_collection, selection={'layer_name': layer_name})
# return [TimeSlice(None, None, time_slice_dict=e, from_db=True) for e in entries]
# def remove_all_time_slices(self):
# super().drop_collection(self._time_slice_collection)
# #endregion
\ No newline at end of file
......@@ -2,3 +2,6 @@ from flask import request
def echo():
return request.json
def test():
return "Hello there!"
\ No newline at end of file
# global imports (dont't worry, red is normal)
from db.repository import Repository
from db.entities.user import User
from flask import request, Response
import bcrypt
from datetime import datetime
repository = Repository()
def authenticate():
data = request.json
username = data["username"]
user = repository.one_by_username(username)
# return 400 if the user does not exist
if len(user) == 0:
return Response(status = 400, response=f'User with username "{username}" does not exist')
user = User.from_serializable_dict(user[0])
hashed_password = user.password
actual_password = data["password"]
if bcrypt.checkpw(actual_password.encode("utf-8"), hashed_password.encode("utf-8")):
# TODO token generation
return "Nice"
return Response(status = 400, response=f'Wrong credentials for user "{username}"')
def delete(username):
reference_users = repository.one_by_username(username)
# return 400 if the user does not exist
if len(reference_users) == 0:
return Response(status = 400, response=f'User with username "{username}" does not exist')
repository.delete_all_with_username(username)
return Response(status=200)
def add():
data = request.json
if not "role" in data:
data["role"] = "u"
username = data["username"]
reference_users = repository.one_by_username(username)
# return 400 if the user already exists
if len(reference_users) > 0:
return Response(status = 400, response=f'User with username "{username}" already exists')
data["created_at"] = str(datetime.now())
data["last_login"] = "0"
print(data["password"])
data["password"] = bcrypt.hashpw(data["password"].encode("utf-8"), bcrypt.gensalt()).decode("utf-8")
user = User.from_serializable_dict(data)
repository.add(user)
return Response(status=200)
def all():
'''
return all users stored in the DB
'''
users = repository.all()
return str(users)
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment