forked from p15670423/monkey
Compare commits
2 Commits
develop
...
PROTECTED_
Author | SHA1 | Date |
---|---|---|
vakarisz | c7b212d3b6 | |
vakarisz | 4321bfafb4 |
|
@ -32,6 +32,8 @@ marshmallow-enum = "*"
|
||||||
readerwriterlock = "*"
|
readerwriterlock = "*"
|
||||||
pymongo = "*"
|
pymongo = "*"
|
||||||
cryptography = "*"
|
cryptography = "*"
|
||||||
|
flask-login = "*"
|
||||||
|
flask-wtf = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
virtualenv = ">=20.0.26"
|
virtualenv = ">=20.0.26"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "09e1b190955d23328ac5e2de385904f7b109eb6b748e7ae9b2b5c7bfd62f690c"
|
"sha256": "6b6bff4ffa42faed7e784a6c6820aca5d882e0ec7ddf20834af42f46b5b551fa"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -32,11 +32,11 @@
|
||||||
},
|
},
|
||||||
"attrs": {
|
"attrs": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4",
|
"sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6",
|
||||||
"sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"
|
"sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
"markers": "python_version >= '3.5'",
|
||||||
"version": "==21.4.0"
|
"version": "==22.1.0"
|
||||||
},
|
},
|
||||||
"bcrypt": {
|
"bcrypt": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -210,19 +210,27 @@
|
||||||
},
|
},
|
||||||
"flask": {
|
"flask": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:15972e5017df0575c3d6c090ba168b6db90259e620ac8d7ea813a396bad5b6cb",
|
"sha256:10dc2bae7a9b6ab59111d6dbece2e08fb0015d2e88d296c40323cc0c7aac2c2e",
|
||||||
"sha256:9013281a7402ad527f8fd56375164f3aa021ecfaff89bfe3825346c24f87e04c"
|
"sha256:98b33b13ad76ee9c7a80d2f56a6c578780e55bf8281790c62d50d4b7fadec2b8"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2.1.3"
|
"version": "==2.2.0"
|
||||||
},
|
},
|
||||||
"flask-jwt-extended": {
|
"flask-jwt-extended": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:793f9e720d0e679cea8f99af6436819bfd1622fc345afeff48878c09f42548f6",
|
"sha256:188907ea9332bdd123a95a457e7487556770480264ce3b78c8835b4347e324cc",
|
||||||
"sha256:f582bba980fcf728ab7250dbb6fbdca8d4e074e24eb2915b01184376ffff98c7"
|
"sha256:a2571df484c5635ad996d364242ec28fc69f386915cd69b1842639712b84c36d"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==4.4.2"
|
"version": "==4.4.3"
|
||||||
|
},
|
||||||
|
"flask-login": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1ef79843f5eddd0f143c2cd994c1b05ac83c0401dc6234c143495af9a939613f",
|
||||||
|
"sha256:c0a7baa9fdc448cdd3dd6f0939df72eec5177b2f7abe6cb82fc934d29caac9c3"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.6.2"
|
||||||
},
|
},
|
||||||
"flask-pymongo": {
|
"flask-pymongo": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -240,6 +248,14 @@
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==0.3.9"
|
"version": "==0.3.9"
|
||||||
},
|
},
|
||||||
|
"flask-wtf": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:34fe5c6fee0f69b50e30f81a3b7ea16aa1492a771fe9ad0974d164610c09a6c9",
|
||||||
|
"sha256:9d733658c80be551ce7d5bc13c7a7ac0d80df509be1e23827c847d9520f4359a"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==1.0.1"
|
||||||
|
},
|
||||||
"future": {
|
"future": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"
|
"sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"
|
||||||
|
@ -799,11 +815,11 @@
|
||||||
},
|
},
|
||||||
"setuptools": {
|
"setuptools": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0d33c374d41c7863419fc8f6c10bfe25b7b498aa34164d135c622e52580c6b16",
|
"sha256:273b6847ae61f7829c1affcdd9a32f67aa65233be508f4fbaab866c5faa4e408",
|
||||||
"sha256:c04b44a57a6265fe34a4a444e965884716d34bae963119a76353434d6f18e450"
|
"sha256:d5340d16943a0f67057329db59b564e938bb3736c6e50ae16ea84d5e5d9ba6d0"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==63.2.0"
|
"version": "==63.3.0"
|
||||||
},
|
},
|
||||||
"six": {
|
"six": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -823,19 +839,19 @@
|
||||||
},
|
},
|
||||||
"urllib3": {
|
"urllib3": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:8298d6d56d39be0e3bc13c1c97d133f9b45d797169a0e11cdd0e0489d786f7ec",
|
"sha256:c33ccba33c819596124764c23a97d25f32b28433ba0dedeb77d873a38722c9bc",
|
||||||
"sha256:879ba4d1e89654d9769ce13121e0f94310ea32e8d2f8cf587b77c08bbcdb30d6"
|
"sha256:ea6e8fb210b19d950fab93b60c9009226c63a28808bc8386e05301e25883ac0a"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'",
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'",
|
||||||
"version": "==1.26.10"
|
"version": "==1.26.11"
|
||||||
},
|
},
|
||||||
"werkzeug": {
|
"werkzeug": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:1ce08e8093ed67d638d63879fd1ba3735817f7a80de3674d293f5984f25fb6e6",
|
"sha256:4d7013ef96fd197d1cdeb03e066c6c5a491ccb44758a5b2b91137319383e5a5a",
|
||||||
"sha256:72a4b735692dd3135217911cbeaa1be5fa3f62bffb8745c5215420a03dc55255"
|
"sha256:7e1db6a5ba6b9a8be061e47e900456355b8714c0f238b0313f53afce1a55a79a"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2.1.2"
|
"version": "==2.2.1"
|
||||||
},
|
},
|
||||||
"wirerope": {
|
"wirerope": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -843,6 +859,14 @@
|
||||||
],
|
],
|
||||||
"version": "==0.4.5"
|
"version": "==0.4.5"
|
||||||
},
|
},
|
||||||
|
"wtforms": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:6b351bbb12dd58af57ffef05bc78425d08d1914e0fd68ee14143b7ade023c5bc",
|
||||||
|
"sha256:837f2f0e0ca79481b92884962b914eba4e72b7a2daaf1f939c890ed0124b834b"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==3.0.1"
|
||||||
|
},
|
||||||
"zipp": {
|
"zipp": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2",
|
"sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2",
|
||||||
|
@ -933,18 +957,18 @@
|
||||||
},
|
},
|
||||||
"attrs": {
|
"attrs": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4",
|
"sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6",
|
||||||
"sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"
|
"sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
"markers": "python_version >= '3.5'",
|
||||||
"version": "==21.4.0"
|
"version": "==22.1.0"
|
||||||
},
|
},
|
||||||
"babel": {
|
"babel": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51",
|
"sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51",
|
||||||
"sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"
|
"sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"
|
||||||
],
|
],
|
||||||
"markers": "python_full_version >= '3.6.0'",
|
"markers": "python_version >= '3.6'",
|
||||||
"version": "==2.10.3"
|
"version": "==2.10.3"
|
||||||
},
|
},
|
||||||
"black": {
|
"black": {
|
||||||
|
@ -1236,7 +1260,7 @@
|
||||||
"sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
|
"sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
|
||||||
"sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
|
"sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
|
||||||
],
|
],
|
||||||
"markers": "python_full_version >= '3.6.0'",
|
"markers": "python_version >= '3.6'",
|
||||||
"version": "==1.0.0"
|
"version": "==1.0.0"
|
||||||
},
|
},
|
||||||
"py": {
|
"py": {
|
||||||
|
@ -1268,7 +1292,7 @@
|
||||||
"sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb",
|
"sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb",
|
||||||
"sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"
|
"sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"
|
||||||
],
|
],
|
||||||
"markers": "python_full_version >= '3.6.0'",
|
"markers": "python_version >= '3.6'",
|
||||||
"version": "==2.12.0"
|
"version": "==2.12.0"
|
||||||
},
|
},
|
||||||
"pyparsing": {
|
"pyparsing": {
|
||||||
|
@ -1326,11 +1350,11 @@
|
||||||
},
|
},
|
||||||
"setuptools": {
|
"setuptools": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0d33c374d41c7863419fc8f6c10bfe25b7b498aa34164d135c622e52580c6b16",
|
"sha256:273b6847ae61f7829c1affcdd9a32f67aa65233be508f4fbaab866c5faa4e408",
|
||||||
"sha256:c04b44a57a6265fe34a4a444e965884716d34bae963119a76353434d6f18e450"
|
"sha256:d5340d16943a0f67057329db59b564e938bb3736c6e50ae16ea84d5e5d9ba6d0"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==63.2.0"
|
"version": "==63.3.0"
|
||||||
},
|
},
|
||||||
"six": {
|
"six": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -1392,7 +1416,7 @@
|
||||||
"sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07",
|
"sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07",
|
||||||
"sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"
|
"sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"
|
||||||
],
|
],
|
||||||
"markers": "python_full_version >= '3.6.0'",
|
"markers": "python_version >= '3.6'",
|
||||||
"version": "==2.0.0"
|
"version": "==2.0.0"
|
||||||
},
|
},
|
||||||
"sphinxcontrib-jsmath": {
|
"sphinxcontrib-jsmath": {
|
||||||
|
@ -1483,19 +1507,19 @@
|
||||||
},
|
},
|
||||||
"urllib3": {
|
"urllib3": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:8298d6d56d39be0e3bc13c1c97d133f9b45d797169a0e11cdd0e0489d786f7ec",
|
"sha256:c33ccba33c819596124764c23a97d25f32b28433ba0dedeb77d873a38722c9bc",
|
||||||
"sha256:879ba4d1e89654d9769ce13121e0f94310ea32e8d2f8cf587b77c08bbcdb30d6"
|
"sha256:ea6e8fb210b19d950fab93b60c9009226c63a28808bc8386e05301e25883ac0a"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'",
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'",
|
||||||
"version": "==1.26.10"
|
"version": "==1.26.11"
|
||||||
},
|
},
|
||||||
"virtualenv": {
|
"virtualenv": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:288171134a2ff3bfb1a2f54f119e77cd1b81c29fc1265a2356f3e8d14c7d58c4",
|
"sha256:0ef5be6d07181946891f5abc8047fda8bc2f0b4b9bf222c64e6e8963baee76db",
|
||||||
"sha256:b30aefac647e86af6d82bfc944c556f8f1a9c90427b2fb4e3bfbf338cb82becf"
|
"sha256:635b272a8e2f77cb051946f46c60a54ace3cb5e25568228bd6b57fc70eca9ff3"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==20.15.1"
|
"version": "==20.16.2"
|
||||||
},
|
},
|
||||||
"vulture": {
|
"vulture": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
|
|
@ -5,7 +5,18 @@ from datetime import timedelta
|
||||||
from typing import Iterable, Type
|
from typing import Iterable, Type
|
||||||
|
|
||||||
import flask_restful
|
import flask_restful
|
||||||
from flask import Flask, Response, send_from_directory
|
from flask import Flask, Response, jsonify, send_from_directory
|
||||||
|
from flask_login import LoginManager, UserMixin, login_required, logout_user
|
||||||
|
from flask_mongoengine import MongoEngine
|
||||||
|
from flask_security import MongoEngineUserDatastore, RoleMixin, Security
|
||||||
|
from mongoengine import (
|
||||||
|
BooleanField,
|
||||||
|
DateTimeField,
|
||||||
|
Document,
|
||||||
|
ListField,
|
||||||
|
ReferenceField,
|
||||||
|
StringField,
|
||||||
|
)
|
||||||
from werkzeug.exceptions import NotFound
|
from werkzeug.exceptions import NotFound
|
||||||
|
|
||||||
from common import DIContainer
|
from common import DIContainer
|
||||||
|
@ -84,28 +95,62 @@ def serve_home():
|
||||||
def init_app_config(app, mongo_url):
|
def init_app_config(app, mongo_url):
|
||||||
app.config["MONGO_URI"] = mongo_url
|
app.config["MONGO_URI"] = mongo_url
|
||||||
|
|
||||||
# See https://flask-jwt-extended.readthedocs.io/en/stable/options
|
# Used for signing session tokens
|
||||||
app.config["JWT_ACCESS_TOKEN_EXPIRES"] = AUTH_EXPIRATION_TIME
|
app.secret_key = str(uuid.uuid4())
|
||||||
# Invalidate the signature of JWTs if the server process restarts. This avoids the edge case
|
|
||||||
# of getting a JWT,
|
|
||||||
# deciding to reset credentials and then still logging in with the old JWT.
|
|
||||||
app.config["JWT_SECRET_KEY"] = str(uuid.uuid4())
|
|
||||||
|
|
||||||
# By default, Flask sorts keys of JSON objects alphabetically.
|
# By default, Flask sorts keys of JSON objects alphabetically.
|
||||||
# See https://flask.palletsprojects.com/en/1.1.x/config/#JSON_SORT_KEYS.
|
# See https://flask.palletsprojects.com/en/1.1.x/config/#JSON_SORT_KEYS.
|
||||||
app.config["JSON_SORT_KEYS"] = False
|
app.config["JSON_SORT_KEYS"] = False
|
||||||
|
app.config.update(
|
||||||
|
SESSION_COOKIE_HTTPONLY=True,
|
||||||
|
REMEMBER_COOKIE_HTTPONLY=True,
|
||||||
|
SESSION_COOKIE_SAMESITE="Strict",
|
||||||
|
)
|
||||||
|
|
||||||
app.url_map.strict_slashes = False
|
app.url_map.strict_slashes = False
|
||||||
app.json_encoder = CustomJSONEncoder
|
app.json_encoder = CustomJSONEncoder
|
||||||
|
|
||||||
|
# flask security configuration
|
||||||
|
# generated using: secrets.token_urlsafe()
|
||||||
|
app.config["SECRET_KEY"] = "pf9Wkove4IKEAXvy-cQkeDPhv9Cb3Ag-wyJILbq_dFw"
|
||||||
|
# argon2 uses double hashing by default - so provide key.
|
||||||
|
# For python3: secrets.SystemRandom().getrandbits(128)
|
||||||
|
app.config["SECURITY_PASSWORD_SALT"] = "146585145368132386173505678016728509634"
|
||||||
|
|
||||||
|
|
||||||
def init_app_services(app):
|
def init_app_services(app):
|
||||||
init_jwt(app)
|
|
||||||
mongo.init_app(app)
|
mongo.init_app(app)
|
||||||
|
|
||||||
|
db = MongoEngine()
|
||||||
|
app.config["MONGODB_SETTINGS"] = [
|
||||||
|
{
|
||||||
|
"db": "monkeyisland",
|
||||||
|
"host": "localhost",
|
||||||
|
"port": 27017,
|
||||||
|
"alias": "flask-security",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
class Role(Document, RoleMixin):
|
||||||
|
name = StringField(max_length=80, unique=True)
|
||||||
|
description = StringField(max_length=255)
|
||||||
|
permissions = StringField(max_length=255)
|
||||||
|
|
||||||
|
class User(Document, UserMixin):
|
||||||
|
email = StringField(max_length=255, unique=True)
|
||||||
|
password = StringField(max_length=255)
|
||||||
|
active = BooleanField(default=True)
|
||||||
|
fs_uniquifier = StringField(max_length=64, unique=True)
|
||||||
|
confirmed_at = DateTimeField()
|
||||||
|
roles = ListField(ReferenceField(Role), default=[])
|
||||||
|
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
database.init()
|
database.init()
|
||||||
|
|
||||||
|
# Setup Flask-Security
|
||||||
|
user_datastore = MongoEngineUserDatastore(db, User, Role)
|
||||||
|
security = Security(app, user_datastore)
|
||||||
|
|
||||||
|
|
||||||
def init_app_url_rules(app):
|
def init_app_url_rules(app):
|
||||||
app.add_url_rule("/", "serve_home", serve_home)
|
app.add_url_rule("/", "serve_home", serve_home)
|
||||||
|
@ -222,4 +267,10 @@ def init_app(mongo_url: str, container: DIContainer):
|
||||||
flask_resource_manager = FlaskDIWrapper(api, container)
|
flask_resource_manager = FlaskDIWrapper(api, container)
|
||||||
init_api_resources(flask_resource_manager)
|
init_api_resources(flask_resource_manager)
|
||||||
|
|
||||||
|
@app.route("/api/logout")
|
||||||
|
@login_required
|
||||||
|
def logout():
|
||||||
|
logout_user()
|
||||||
|
return jsonify({"logout": True})
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from flask_login import UserMixin
|
||||||
|
from mongoengine import Document, StringField
|
||||||
|
|
||||||
|
|
||||||
|
class User(Document, UserMixin):
|
||||||
|
username = StringField()
|
||||||
|
password_hash = StringField()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_by_id(id: str):
|
||||||
|
return User.objects.get(id)
|
|
@ -2,9 +2,11 @@ import logging
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
|
|
||||||
import flask_jwt_extended
|
import flask_jwt_extended
|
||||||
from flask import make_response, request
|
from flask import jsonify, make_response, request
|
||||||
|
from flask_login import current_user, login_required, login_user
|
||||||
|
|
||||||
from common.utils.exceptions import IncorrectCredentialsError
|
from common.utils.exceptions import IncorrectCredentialsError
|
||||||
|
from monkey_island.cc.models.user import User
|
||||||
from monkey_island.cc.resources.AbstractResource import AbstractResource
|
from monkey_island.cc.resources.AbstractResource import AbstractResource
|
||||||
from monkey_island.cc.resources.auth.credential_utils import get_username_password_from_request
|
from monkey_island.cc.resources.auth.credential_utils import get_username_password_from_request
|
||||||
from monkey_island.cc.resources.request_authentication import create_access_token
|
from monkey_island.cc.resources.request_authentication import create_access_token
|
||||||
|
@ -22,8 +24,6 @@ def init_jwt(app):
|
||||||
|
|
||||||
class Authenticate(AbstractResource):
|
class Authenticate(AbstractResource):
|
||||||
"""
|
"""
|
||||||
Resource for user authentication. The user provides the username and password and we \
|
|
||||||
give them a JWT. \
|
|
||||||
See `AuthService.js` file for the frontend counterpart for this code. \
|
See `AuthService.js` file for the frontend counterpart for this code. \
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -33,6 +33,9 @@ class Authenticate(AbstractResource):
|
||||||
def __init__(self, authentication_service: AuthenticationService):
|
def __init__(self, authentication_service: AuthenticationService):
|
||||||
self._authentication_service = authentication_service
|
self._authentication_service = authentication_service
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
return jsonify({"authenticated": current_user.is_authenticated})
|
||||||
|
|
||||||
def post(self):
|
def post(self):
|
||||||
"""
|
"""
|
||||||
Example request: \
|
Example request: \
|
||||||
|
@ -45,10 +48,10 @@ class Authenticate(AbstractResource):
|
||||||
username, password = get_username_password_from_request(request)
|
username, password = get_username_password_from_request(request)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._authentication_service.authenticate(username, password)
|
user = self._authentication_service.authenticate(username, password)
|
||||||
access_token = create_access_token(username)
|
|
||||||
except IncorrectCredentialsError:
|
except IncorrectCredentialsError:
|
||||||
return make_response({"error": "Invalid credentials"}, HTTPStatus.UNAUTHORIZED)
|
return make_response({"error": "Invalid credentials"}, HTTPStatus.UNAUTHORIZED)
|
||||||
|
|
||||||
# API Spec: Why are we sending "error" here?
|
# API Spec: Why are we sending "error" here?
|
||||||
return make_response({"access_token": access_token, "error": ""}, HTTPStatus.OK)
|
login_user(user)
|
||||||
|
return jsonify({"login": True})
|
||||||
|
|
|
@ -2,10 +2,10 @@ import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from flask import make_response, request
|
from flask import make_response, request
|
||||||
|
from flask_login import login_required
|
||||||
|
|
||||||
from monkey_island.cc.models import IslandMode as IslandModeEnum
|
from monkey_island.cc.models import IslandMode as IslandModeEnum
|
||||||
from monkey_island.cc.resources.AbstractResource import AbstractResource
|
from monkey_island.cc.resources.AbstractResource import AbstractResource
|
||||||
from monkey_island.cc.resources.request_authentication import jwt_required
|
|
||||||
from monkey_island.cc.services import IslandModeService
|
from monkey_island.cc.services import IslandModeService
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -18,7 +18,6 @@ class IslandMode(AbstractResource):
|
||||||
self._island_mode_service = island_mode_service
|
self._island_mode_service = island_mode_service
|
||||||
|
|
||||||
# API Spec: Instead of POST, this should be PUT
|
# API Spec: Instead of POST, this should be PUT
|
||||||
@jwt_required
|
|
||||||
def post(self):
|
def post(self):
|
||||||
try:
|
try:
|
||||||
body = json.loads(request.data)
|
body = json.loads(request.data)
|
||||||
|
@ -35,7 +34,7 @@ class IslandMode(AbstractResource):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return make_response({}, 422)
|
return make_response({}, 422)
|
||||||
|
|
||||||
@jwt_required
|
@login_required
|
||||||
def get(self):
|
def get(self):
|
||||||
island_mode = self._island_mode_service.get_mode()
|
island_mode = self._island_mode_service.get_mode()
|
||||||
return make_response({"mode": island_mode.value}, 200)
|
return make_response({"mode": island_mode.value}, 200)
|
||||||
|
|
|
@ -26,7 +26,6 @@ class Root(AbstractResource):
|
||||||
else:
|
else:
|
||||||
return make_response(400, {"error": "unknown action"})
|
return make_response(400, {"error": "unknown action"})
|
||||||
|
|
||||||
@jwt_required
|
|
||||||
def get_server_info(self):
|
def get_server_info(self):
|
||||||
return jsonify(
|
return jsonify(
|
||||||
ip_addresses=local_ip_addresses(),
|
ip_addresses=local_ip_addresses(),
|
||||||
|
|
|
@ -8,6 +8,7 @@ from common.utils.exceptions import (
|
||||||
UnknownUserError,
|
UnknownUserError,
|
||||||
)
|
)
|
||||||
from monkey_island.cc.models import UserCredentials
|
from monkey_island.cc.models import UserCredentials
|
||||||
|
from monkey_island.cc.models.user import User
|
||||||
from monkey_island.cc.repository import IUserRepository
|
from monkey_island.cc.repository import IUserRepository
|
||||||
from monkey_island.cc.server_utils.encryption import (
|
from monkey_island.cc.server_utils.encryption import (
|
||||||
ILockableEncryptor,
|
ILockableEncryptor,
|
||||||
|
@ -29,20 +30,21 @@ class AuthenticationService:
|
||||||
self._repository_encryptor = repository_encryptor
|
self._repository_encryptor = repository_encryptor
|
||||||
|
|
||||||
def needs_registration(self) -> bool:
|
def needs_registration(self) -> bool:
|
||||||
return not self._user_repository.has_registered_users()
|
return not User.objects.first()
|
||||||
|
|
||||||
def register_new_user(self, username: str, password: str):
|
def register_new_user(self, username: str, password: str):
|
||||||
if not username or not password:
|
if not username or not password:
|
||||||
raise InvalidRegistrationCredentialsError("Username or password can not be empty.")
|
raise InvalidRegistrationCredentialsError("Username or password can not be empty.")
|
||||||
|
|
||||||
credentials = UserCredentials(username, _hash_password(password))
|
credentials = UserCredentials(username, _hash_password(password))
|
||||||
self._user_repository.add_user(credentials)
|
# self._user_repository.add_user(credentials)
|
||||||
|
User(username=username, password_hash=_hash_password(password)).save()
|
||||||
self._reset_repository_encryptor(username, password)
|
self._reset_repository_encryptor(username, password)
|
||||||
reset_database()
|
reset_database()
|
||||||
|
|
||||||
def authenticate(self, username: str, password: str):
|
def authenticate(self, username: str, password: str):
|
||||||
try:
|
try:
|
||||||
registered_user = self._user_repository.get_user_credentials(username)
|
registered_user = User.objects.first()
|
||||||
except UnknownUserError:
|
except UnknownUserError:
|
||||||
raise IncorrectCredentialsError()
|
raise IncorrectCredentialsError()
|
||||||
|
|
||||||
|
@ -50,6 +52,7 @@ class AuthenticationService:
|
||||||
raise IncorrectCredentialsError()
|
raise IncorrectCredentialsError()
|
||||||
|
|
||||||
self._unlock_repository_encryptor(username, password)
|
self._unlock_repository_encryptor(username, password)
|
||||||
|
return registered_user
|
||||||
|
|
||||||
def _unlock_repository_encryptor(self, username: str, password: str):
|
def _unlock_repository_encryptor(self, username: str, password: str):
|
||||||
secret = _get_secret_from_credentials(username, password)
|
secret = _get_secret_from_credentials(username, password)
|
||||||
|
|
|
@ -36,6 +36,7 @@ class Database(object):
|
||||||
return (
|
return (
|
||||||
not collection.startswith("system.")
|
not collection.startswith("system.")
|
||||||
and not collection == AttackMitigations.COLLECTION_NAME
|
and not collection == AttackMitigations.COLLECTION_NAME
|
||||||
|
and not collection == "user"
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -11,7 +11,7 @@ import LicensePage from './pages/LicensePage';
|
||||||
import AuthComponent from './AuthComponent';
|
import AuthComponent from './AuthComponent';
|
||||||
import LoginPageComponent from './pages/LoginPage';
|
import LoginPageComponent from './pages/LoginPage';
|
||||||
import RegisterPageComponent from './pages/RegisterPage';
|
import RegisterPageComponent from './pages/RegisterPage';
|
||||||
import LandingPage from "./pages/LandingPage";
|
import LandingPage from './pages/LandingPage';
|
||||||
import Notifier from 'react-desktop-notification';
|
import Notifier from 'react-desktop-notification';
|
||||||
import NotFoundPage from './pages/NotFoundPage';
|
import NotFoundPage from './pages/NotFoundPage';
|
||||||
import GettingStartedPage from './pages/GettingStartedPage';
|
import GettingStartedPage from './pages/GettingStartedPage';
|
||||||
|
@ -21,13 +21,13 @@ import 'normalize.css/normalize.css';
|
||||||
import 'styles/App.css';
|
import 'styles/App.css';
|
||||||
import 'react-table/react-table.css';
|
import 'react-table/react-table.css';
|
||||||
import LoadingScreen from './ui-components/LoadingScreen';
|
import LoadingScreen from './ui-components/LoadingScreen';
|
||||||
import SidebarLayoutComponent from "./layouts/SidebarLayoutComponent";
|
import SidebarLayoutComponent from './layouts/SidebarLayoutComponent';
|
||||||
import {CompletedSteps} from "./side-menu/CompletedSteps";
|
import {CompletedSteps} from './side-menu/CompletedSteps';
|
||||||
import Timeout = NodeJS.Timeout;
|
import Timeout = NodeJS.Timeout;
|
||||||
import IslandHttpClient from "./IslandHttpClient";
|
import IslandHttpClient from './IslandHttpClient';
|
||||||
import _ from "lodash";
|
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
import {faFileCode, faLightbulb} from '@fortawesome/free-solid-svg-icons';
|
||||||
import {faFileCode, faLightbulb} from "@fortawesome/free-solid-svg-icons";
|
import LogoutPageComponent from './pages/LogoutPage';
|
||||||
|
|
||||||
|
|
||||||
let notificationIcon = require('../images/notification-logo-512x512.png');
|
let notificationIcon = require('../images/notification-logo-512x512.png');
|
||||||
|
@ -41,6 +41,7 @@ export const Routes = {
|
||||||
SecurityReport: '/report/security',
|
SecurityReport: '/report/security',
|
||||||
RansomwareReport: '/report/ransomware',
|
RansomwareReport: '/report/ransomware',
|
||||||
LoginPage: '/login',
|
LoginPage: '/login',
|
||||||
|
Logout: '/logout',
|
||||||
RegisterPage: '/register',
|
RegisterPage: '/register',
|
||||||
ConfigurePage: '/configure',
|
ConfigurePage: '/configure',
|
||||||
RunMonkeyPage: '/run-monkey',
|
RunMonkeyPage: '/run-monkey',
|
||||||
|
@ -49,7 +50,7 @@ export const Routes = {
|
||||||
LicensePage: '/license'
|
LicensePage: '/license'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isReportRoute(route){
|
export function isReportRoute(route) {
|
||||||
return route.startsWith(Routes.Report);
|
return route.startsWith(Routes.Report);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,43 +74,23 @@ class AppComponent extends AuthComponent {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = this.auth.loggedIn();
|
this.auth.loggedIn().then((res) => {
|
||||||
|
console.log("called")
|
||||||
|
if (this.state.isLoggedIn !== res) {
|
||||||
|
this.setState({
|
||||||
|
isLoggedIn: res
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (this.state.isLoggedIn !== res) {
|
if (!res) {
|
||||||
this.setState({
|
this.auth.needsRegistration()
|
||||||
isLoggedIn: res
|
.then(result => {
|
||||||
});
|
this.setState({
|
||||||
}
|
needsRegistration: result
|
||||||
|
});
|
||||||
if (!res) {
|
})
|
||||||
this.auth.needsRegistration()
|
}
|
||||||
.then(result => {
|
});
|
||||||
this.setState({
|
|
||||||
needsRegistration: result
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res) {
|
|
||||||
this.setMode()
|
|
||||||
.then(() => {
|
|
||||||
if (this.state.islandMode === "unset") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.authFetch('/api')
|
|
||||||
.then(res => res.json())
|
|
||||||
.then(res => {
|
|
||||||
let completedSteps = CompletedSteps.buildFromResponse(res.completed_steps);
|
|
||||||
// This check is used to prevent unnecessary re-rendering
|
|
||||||
if (_.isEqual(this.state.completedSteps, completedSteps)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.setState({completedSteps: completedSteps});
|
|
||||||
this.showInfectionDoneNotification();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
setMode = () => {
|
setMode = () => {
|
||||||
|
@ -121,6 +102,7 @@ class AppComponent extends AuthComponent {
|
||||||
|
|
||||||
renderRoute = (route_path, page_component, is_exact_path = false) => {
|
renderRoute = (route_path, page_component, is_exact_path = false) => {
|
||||||
let render_func = () => {
|
let render_func = () => {
|
||||||
|
console.log(this.state.isLoggedIn);
|
||||||
switch (this.state.isLoggedIn) {
|
switch (this.state.isLoggedIn) {
|
||||||
case true:
|
case true:
|
||||||
if (this.needsRedirectionToLandingPage(route_path)) {
|
if (this.needsRedirectionToLandingPage(route_path)) {
|
||||||
|
@ -151,12 +133,12 @@ class AppComponent extends AuthComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
needsRedirectionToLandingPage = (route_path) => {
|
needsRedirectionToLandingPage = (route_path) => {
|
||||||
return (this.state.islandMode === "unset" && route_path !== Routes.LandingPage)
|
return (this.state.islandMode === 'unset' && route_path !== Routes.LandingPage)
|
||||||
}
|
}
|
||||||
|
|
||||||
needsRedirectionToGettingStarted = (route_path) => {
|
needsRedirectionToGettingStarted = (route_path) => {
|
||||||
return route_path === Routes.LandingPage &&
|
return route_path === Routes.LandingPage &&
|
||||||
this.state.islandMode !== "unset" && this.state.islandMode !== undefined
|
this.state.islandMode !== 'unset' && this.state.islandMode !== undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
redirectTo = (userPath, targetPath) => {
|
redirectTo = (userPath, targetPath) => {
|
||||||
|
@ -176,43 +158,49 @@ class AppComponent extends AuthComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaultReport() {
|
getDefaultReport() {
|
||||||
if(this.state.islandMode === 'ransomware'){
|
if (this.state.islandMode === 'ransomware') {
|
||||||
return Routes.RansomwareReport;
|
return Routes.RansomwareReport;
|
||||||
} else {
|
} else {
|
||||||
return Routes.SecurityReport;
|
return Routes.SecurityReport;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getIslandModeTitle(){
|
getIslandModeTitle() {
|
||||||
if(this.state.islandMode === 'ransomware'){
|
if (this.state.islandMode === 'ransomware') {
|
||||||
return this.formIslandModeTitle("Ransomware", faFileCode);
|
return this.formIslandModeTitle('Ransomware', faFileCode);
|
||||||
} else {
|
} else {
|
||||||
return this.formIslandModeTitle("Custom", faLightbulb);
|
return this.formIslandModeTitle('Custom', faLightbulb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
formIslandModeTitle(title, icon){
|
formIslandModeTitle(title, icon) {
|
||||||
return (<>
|
return (<>
|
||||||
<h5 className={'text-muted'}>
|
<h5 className={'text-muted'}>
|
||||||
<FontAwesomeIcon icon={icon} /> {title}
|
<FontAwesomeIcon icon={icon}/> {title}
|
||||||
</h5>
|
</h5>
|
||||||
</>)
|
</>)
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
let defaultSideNavProps = {completedSteps: this.state.completedSteps,
|
let defaultSideNavProps = {
|
||||||
onStatusChange: this.updateStatus,
|
completedSteps: this.state.completedSteps,
|
||||||
islandMode: this.state.islandMode,
|
onStatusChange: this.updateStatus,
|
||||||
defaultReport: this.getDefaultReport(),
|
islandMode: this.state.islandMode,
|
||||||
sideNavHeader: this.getIslandModeTitle()}
|
defaultReport: this.getDefaultReport(),
|
||||||
|
sideNavHeader: this.getIslandModeTitle()
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
<Container fluid>
|
<Container fluid>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path={Routes.LoginPage} render={() => (<LoginPageComponent onStatusChange={this.updateStatus}/>)}/>
|
<Route path={Routes.LoginPage}
|
||||||
<Route path={Routes.RegisterPage} render={() => (<RegisterPageComponent onStatusChange={this.updateStatus}/>)}/>
|
render={() => (<LoginPageComponent onStatusChange={this.updateStatus}/>)}/>
|
||||||
|
<Route path={Routes.RegisterPage}
|
||||||
|
render={() => (<RegisterPageComponent onStatusChange={this.updateStatus}/>)}/>
|
||||||
|
<Route path={Routes.Logout}
|
||||||
|
render={() => (<LogoutPageComponent />)}/>
|
||||||
{this.renderRoute(Routes.LandingPage,
|
{this.renderRoute(Routes.LandingPage,
|
||||||
<SidebarLayoutComponent component={LandingPage}
|
<SidebarLayoutComponent component={LandingPage}
|
||||||
sideNavShow={false}
|
sideNavShow={false}
|
||||||
|
|
|
@ -9,7 +9,9 @@ class LoginPageComponent extends React.Component {
|
||||||
login = (event) => {
|
login = (event) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
this.auth.login(this.username, this.password).then(res => {
|
this.auth.login(this.username, this.password).then(res => {
|
||||||
if (res['result']) {
|
console.log("HERE")
|
||||||
|
console.log(res)
|
||||||
|
if (res) {
|
||||||
this.redirectToHome();
|
this.redirectToHome();
|
||||||
} else {
|
} else {
|
||||||
this.setState({failed: true});
|
this.setState({failed: true});
|
||||||
|
@ -42,17 +44,16 @@ class LoginPageComponent extends React.Component {
|
||||||
failed: false
|
failed: false
|
||||||
};
|
};
|
||||||
|
|
||||||
this.auth.needsRegistration()
|
//this.auth.needsRegistration()
|
||||||
.then(result => {
|
// .then(result => {
|
||||||
if (result) {
|
// if (result) {
|
||||||
this.redirectToRegistration()
|
// this.redirectToRegistration()
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
|
|
||||||
if (this.auth.loggedIn()) {
|
|
||||||
this.redirectToHome();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
this.auth.loggedIn().then((res) => {
|
||||||
|
if(res){this.redirectToHome();}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {Button, Col, Container, Form, Row} from 'react-bootstrap';
|
||||||
|
|
||||||
|
import AuthService from '../../services/AuthService';
|
||||||
|
import monkeyGeneral from '../../images/militant-monkey.svg';
|
||||||
|
import ParticleBackground from '../ui-components/ParticleBackground';
|
||||||
|
import {Redirect} from 'react-router-dom';
|
||||||
|
import {Routes} from '../Main';
|
||||||
|
|
||||||
|
class LogoutPageComponent extends React.Component {
|
||||||
|
|
||||||
|
redirectToLogin = () => {
|
||||||
|
window.location.href = '/login';
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
fetch('/api/logout').then(this.redirectToLogin);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (<p>Logging out</p>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LogoutPageComponent;
|
|
@ -28,13 +28,7 @@ export default class AuthService {
|
||||||
})
|
})
|
||||||
}).then(response => response.json())
|
}).then(response => response.json())
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (Object.prototype.hasOwnProperty.call(res, 'access_token')) {
|
return res["login"]
|
||||||
this._setToken(res['access_token']);
|
|
||||||
return {result: true};
|
|
||||||
} else {
|
|
||||||
this._removeToken();
|
|
||||||
return {result: false};
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -61,31 +55,8 @@ export default class AuthService {
|
||||||
};
|
};
|
||||||
|
|
||||||
_authFetch = (url, options = {}) => {
|
_authFetch = (url, options = {}) => {
|
||||||
const headers = {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.loggedIn()) {
|
|
||||||
headers['Authorization'] = 'Bearer ' + this._getToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.prototype.hasOwnProperty.call(options, 'headers')) {
|
|
||||||
for (let header in headers) {
|
|
||||||
options['headers'][header] = headers[header];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
options['headers'] = headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fetch(url, options)
|
return fetch(url, options)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.status === 401) {
|
|
||||||
res.clone().json().then(res_json => {
|
|
||||||
console.log('Got 401 from server while trying to authFetch: ' + JSON.stringify(res_json));
|
|
||||||
});
|
|
||||||
this._removeToken();
|
|
||||||
}
|
|
||||||
return res;
|
return res;
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
@ -100,8 +71,12 @@ export default class AuthService {
|
||||||
};
|
};
|
||||||
|
|
||||||
loggedIn() {
|
loggedIn() {
|
||||||
const token = this._getToken();
|
return fetch(this.AUTHENTICATION_API_ENDPOINT)
|
||||||
return ((token !== null) && !this._isTokenExpired(token));
|
.then(response => response.json())
|
||||||
|
.then(res => {
|
||||||
|
console.log(res);
|
||||||
|
return res['authenticated'];
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
logout = () => {
|
logout = () => {
|
||||||
|
|
Loading…
Reference in New Issue