Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
SMART
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
3
Issues
3
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
UNI-KLU
SMART
Commits
eaf5c1f0
Commit
eaf5c1f0
authored
Jul 15, 2020
by
Manuel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
restGateway: added CRUD methods for user
added generation of JWT tokens
parent
df1bc7c1
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
200 additions
and
102 deletions
+200
-102
swagger.yml
src/rest-gateway/app/configs/swagger.yml
+22
-2
repository.py
src/rest-gateway/app/db/repository.py
+1
-58
user.py
src/rest-gateway/app/rest/user.py
+87
-42
user_service.py
src/rest-gateway/app/services/user_service.py
+90
-0
No files found.
src/rest-gateway/app/configs/swagger.yml
View file @
eaf5c1f0
...
@@ -13,6 +13,24 @@ basePath: "/api"
...
@@ -13,6 +13,24 @@ basePath: "/api"
# Paths supported by the server application
# Paths supported by the server application
paths
:
paths
:
/tokens/{token}
:
post
:
operationId
:
"
rest.user.verify"
tags
:
-
"
User"
summary
:
"
Verifies
a
user
token"
description
:
"
Verifies
a
user
token"
parameters
:
-
name
:
"
token"
in
:
"
path"
description
:
"
Target
token
that
will
be
verified"
required
:
true
type
:
"
string"
responses
:
'
200'
:
description
:
"
Verification
successful"
'
401'
:
description
:
"
Invalid
token"
/tokens
:
/tokens
:
post
:
post
:
operationId
:
"
rest.user.authenticate"
operationId
:
"
rest.user.authenticate"
...
@@ -40,7 +58,7 @@ paths:
...
@@ -40,7 +58,7 @@ paths:
-
"
User"
-
"
User"
summary
:
"
Deletes
a
user
identified
by
the
username
from
the
database"
summary
:
"
Deletes
a
user
identified
by
the
username
from
the
database"
description
:
"
Deletes
a
user
identified
by
the
username
from
the
database"
description
:
"
Deletes
a
user
identified
by
the
username
from
the
database"
parameters
:
parameters
:
-
name
:
"
username"
-
name
:
"
username"
in
:
"
path"
in
:
"
path"
description
:
"
Username
of
the
user
to
be
deleted"
description
:
"
Username
of
the
user
to
be
deleted"
...
@@ -61,7 +79,9 @@ paths:
...
@@ -61,7 +79,9 @@ paths:
responses
:
responses
:
'
200'
:
'
200'
:
description
:
complete user object including numeric ID
description
:
complete user object including numeric ID
schema
:
schema
:
type
:
array
items
:
$ref
:
"
#/definitions/User"
$ref
:
"
#/definitions/User"
'
400'
:
'
400'
:
description
:
wrong username or password
description
:
wrong username or password
...
...
src/rest-gateway/app/db/repository.py
View file @
eaf5c1f0
...
@@ -31,61 +31,4 @@ class Repository(MongoRepositoryBase):
...
@@ -31,61 +31,4 @@ class Repository(MongoRepositoryBase):
def
all
(
self
)
->
List
[
User
]:
def
all
(
self
)
->
List
[
User
]:
result
=
super
()
.
get_entries
(
self
.
_user_collection
,
projection
=
{
'_id'
:
False
})
result
=
super
()
.
get_entries
(
self
.
_user_collection
,
projection
=
{
'_id'
:
False
})
return
list
(
result
)
return
list
(
result
)
\ No newline at end of file
# 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
src/rest-gateway/app/rest/user.py
View file @
eaf5c1f0
# global imports (dont't worry, red is normal)
# global imports (dont't worry, red is normal)
from
db.repository
import
Repository
from
db.entities.user
import
User
from
db.entities.user
import
User
from
services.user_service
import
UserService
from
flask
import
request
,
Response
from
flask
import
request
,
Response
import
bcrypt
import
bcrypt
import
jwt
from
datetime
import
datetime
from
datetime
import
datetime
,
timedelta
repository
=
Repository
()
SIGNING_KEY
=
"yteNrMy6142WKwp8fKfrHkS5nlFpxtHgOXJh1ZPsOrV_gTcsO9eMY7aB7HUzRbTRO9dmZhCl3FdPtuvMe3K8aBA_wc2MmHRo8IkUIGmvUJGsAxKFClN_6oNW5fEvoeVKiL1krA-qjWbR_em-WksePgPoTsySW7QbKdi4f7cwuyK2_JZ2fQj9hDKlfJ2GzMXkKiWcfyCTr30yC6BviAFeRDD_Bpvg6znsrXr53Tq66hnwDwQ6QU7aHVu-bERblKZTYuvkSxsov6yRMEVWQoiuBITsQtIOcgSWK4Dy3BjSbqoIcKw3WG-s3wx1lTen19QbEu8vJC64e0iGeGDWT6vbtg"
TOKEN_VALIDITY_IN_DAYS
=
1
def
authenticate
():
def
generate_token
(
user
:
User
)
->
str
:
data
=
request
.
json
'''
username
=
data
[
"username"
]
creates a JWT token for a user which has the following fields:
- username
- created_at
- valid_until
'''
created_at
=
datetime
.
now
()
valid_until
=
created_at
+
timedelta
(
days
=
1
)
return
jwt
.
encode
(
{
'username'
:
user
.
username
,
'created_at'
:
str
(
created_at
),
'valid_until'
:
str
(
valid_until
),
},
SIGNING_KEY
,
algorithm
=
'HS256'
)
.
decode
(
"utf-8"
)
def
verify
(
token
):
'''
verifies the validity of a JWT token.
performs the following tests (int this order):
- is the JWT token parsable? (it has not been damaged + the signature is valid)
- does the payload contain all necessary fields?
- does the user specified by the payload exist?
- is the expiration/creation date sound?
'''
user
=
repository
.
one_by_username
(
username
)
try
:
payload
=
jwt
.
decode
(
token
,
SIGNING_KEY
,
algorithms
=
[
'HS256'
])
except
:
return
Response
(
status
=
401
,
response
=
f
'Invalid JWT token'
)
#
return 400 if the user does not exist
#
check if all needed fields are in the payload
if
len
(
user
)
==
0
:
if
not
"username"
in
payload
or
not
"created_at"
in
payload
or
not
"valid_until"
in
payload
:
return
Response
(
status
=
40
0
,
response
=
f
'User with username "{username}" does not exist
'
)
return
Response
(
status
=
40
1
,
response
=
f
'Invalid JWT token
'
)
user
=
User
.
from_serializable_dict
(
user
[
0
])
try
:
UserService
.
get_by_username
(
payload
[
"username"
])
except
ValueError
as
e
:
# return 400 if the user does not exist
return
Response
(
status
=
400
,
response
=
str
(
e
))
# check if token has already expired
token_created_at
=
datetime
.
strptime
(
payload
[
"created_at"
],
'
%
Y-
%
m-
%
d
%
H:
%
M:
%
S.
%
f'
)
valid_until
=
datetime
.
strptime
(
payload
[
"valid_until"
],
'
%
Y-
%
m-
%
d
%
H:
%
M:
%
S.
%
f'
)
now
=
datetime
.
now
()
if
now
<=
token_created_at
or
now
>=
valid_until
:
return
Response
(
status
=
401
,
response
=
f
'Token expired'
)
hashed_password
=
user
.
password
# everthing is fine
actual_password
=
data
[
"password"
]
return
Response
(
status
=
200
)
if
bcrypt
.
checkpw
(
actual_password
.
encode
(
"utf-8"
),
hashed_password
.
encode
(
"utf-8"
)):
def
authenticate
():
# TODO token generation
'''
return
"Nice"
takes the credentials from the user and generates a JWT token out of them
'''
return
Response
(
status
=
400
,
response
=
f
'Wrong credentials for user "{username}"'
)
data
=
request
.
json
username
=
data
[
"username"
]
try
:
user
=
UserService
.
get_by_credentials
(
username
,
data
[
"password"
])
return
{
"token"
:
generate_token
(
user
)}
except
ValueError
as
e
:
# return 400 if the user does not exist or the password is wrong
return
Response
(
status
=
400
,
response
=
str
(
e
))
def
delete
(
username
):
def
delete
(
username
):
reference_users
=
repository
.
one_by_username
(
username
)
'''
deletes a user from the DB. should be protected later
'''
try
:
UserService
.
delete
(
username
)
# return 400 if the user does not exist
return
Response
(
status
=
200
)
if
len
(
reference_users
)
==
0
:
except
ValueError
as
e
:
return
Response
(
status
=
400
,
response
=
f
'User with username "{username}" does not exist'
)
# return 400 if the user already exists
return
Response
(
status
=
400
,
response
=
str
(
e
))
repository
.
delete_all_with_username
(
username
)
return
Response
(
status
=
200
)
def
add
():
def
add
():
'''
adds a new user to the DB. expects the provided password to be plaintext i.e. it should not
be encrypted, encoded or hashed
'''
data
=
request
.
json
data
=
request
.
json
if
not
"role"
in
data
:
if
not
"role"
in
data
:
...
@@ -48,22 +104,11 @@ def add():
...
@@ -48,22 +104,11 @@ def add():
username
=
data
[
"username"
]
username
=
data
[
"username"
]
reference_users
=
repository
.
one_by_username
(
username
)
try
:
UserService
.
add
(
username
,
data
[
"password"
],
data
[
"role"
])
# return 400 if the user already exists
except
ValueError
as
e
:
if
len
(
reference_users
)
>
0
:
# return 400 if the user already exists
return
Response
(
status
=
400
,
response
=
f
'User with username "{username}" already exists'
)
return
Response
(
status
=
400
,
response
=
str
(
e
))
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
)
return
Response
(
status
=
200
)
...
@@ -72,6 +117,6 @@ def all():
...
@@ -72,6 +117,6 @@ def all():
return all users stored in the DB
return all users stored in the DB
'''
'''
users
=
repository
.
all
()
users
=
UserService
.
_
repository
.
all
()
return
str
(
users
)
return
str
(
users
)
\ No newline at end of file
src/rest-gateway/app/services/user_service.py
0 → 100644
View file @
eaf5c1f0
# global imports (dont't worry, red is normal)
from
db.repository
import
Repository
from
db.entities.user
import
User
from
datetime
import
datetime
import
bcrypt
class
UserService
:
_repository
=
Repository
()
@
staticmethod
def
get_by_username
(
username
):
'''
fetches the given user from the database
throws a ValueError if the user does not exist
@params:
username - Required : string identifier for the user i.e. an email address
'''
user
=
UserService
.
_repository
.
one_by_username
(
username
)
# return 400 if the user does not exist
if
len
(
user
)
==
0
:
raise
ValueError
(
f
'User with username "{username}" does not exist'
)
return
User
.
from_serializable_dict
(
user
[
0
])
@
staticmethod
def
get_by_credentials
(
username
,
password
):
'''
fetches the given user from the database and checks if the password matches the stored one
throws a ValueError if the user does not exist or the password is wrong
@params:
username - Required : string identifier for the user i.e. an email address
password - Required : passphrase used to authenticate later, raw plaintext
'''
user
=
UserService
.
get_by_username
(
username
)
hashed_password
=
user
.
password
if
not
bcrypt
.
checkpw
(
password
.
encode
(
"utf-8"
),
hashed_password
.
encode
(
"utf-8"
)):
raise
ValueError
(
f
'Wrong credentials for user "{username}"'
)
return
user
@
staticmethod
def
delete
(
username
):
'''
deletes the given user from the database
throws a ValueError if the user does not exist
@params:
username - Required : string identifier for the user i.e. an email address
'''
reference_users
=
UserService
.
_repository
.
one_by_username
(
username
)
# return 400 if the user does not exist
if
len
(
reference_users
)
==
0
:
raise
ValueError
(
f
'User with username "{username}" does not exist'
)
UserService
.
_repository
.
delete_all_with_username
(
username
)
@
staticmethod
def
add
(
username
,
password
,
role
=
"u"
):
'''
adds the given user to the database
throws a ValueError if the user already exists
@params:
username - Required : string identifier for the user i.e. an email address
password - Required : passphrase used to authenticate later, raw plaintext
role - Optional : user type, one of the following: [u=regular user (default)]
'''
reference_users
=
UserService
.
_repository
.
one_by_username
(
username
)
if
len
(
reference_users
)
>
0
:
raise
ValueError
(
f
'User with username "{username}" already exists'
)
created_at
=
str
(
datetime
.
now
())
last_login
=
str
(
datetime
.
min
)
# hash the password using the BCrypt algorithm, which generates a string that
# contains the algorithm, the salt and the hash
password
=
bcrypt
.
hashpw
(
password
.
encode
(
"utf-8"
),
bcrypt
.
gensalt
())
.
decode
(
"utf-8"
)
user_new
=
User
(
username
,
password
,
role
=
role
)
user_new
.
created_at
=
created_at
user_new
.
last_login
=
last_login
UserService
.
_repository
.
add
(
user_new
)
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment