Compare commits

...

2 Commits

Author SHA1 Message Date
vakarisz c7b212d3b6 TEMP: flask-security basic setup 2022-08-08 10:35:06 +03:00
vakarisz 4321bfafb4 TEMP: Quick hack of session based authentication 2022-08-03 14:18:20 +03:00
13 changed files with 246 additions and 161 deletions

View File

@ -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"

View File

@ -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": [

View File

@ -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

View File

@ -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)

View File

@ -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})

View File

@ -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)

View File

@ -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(),

View File

@ -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)

View File

@ -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

View File

@ -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}

View File

@ -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() {

View File

@ -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;

View File

@ -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 = () => {