Compare commits

..

55 Commits

Author SHA1 Message Date
059d45b117
patch to require admin APIKey on admin endpoints
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-16 02:19:41 +03:00
39b7d25a57 WIP: enroll device
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-16 02:18:59 +03:00
fc9f1c5c05 file headers
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-16 02:18:43 +03:00
7f90e104d3 sql_app: emergency open time
For some reason, having 3600 set as the
	time for door to stay open in a case of
	fire does not open the door at all.
	Prob a limtiation in door implementation

Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-12 21:53:37 +03:00
2c3206fc06
verbose at emerg
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-12 19:56:00 +03:00
b704aee0fc
init_secrets: change file mode when making .env file
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-12 19:34:31 +03:00
99ac2acc0d sql_app: open door on emergency
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-12 19:30:14 +03:00
6a771c589b sql_app: init_db: do not recreate sensor_data and access_log
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-12 12:57:38 +03:00
2c60e14260 sql_app: enforce strict file permissions for .env
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-12 12:52:04 +03:00
21aef6ec6c sql_app: update monitor column
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-11 20:28:39 +03:00
20dfa6dcc4 sql_app: Respond to emergency events record db entry
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-11 18:05:52 +03:00
e1a7c4023b
sql_app: tools: Iot door emulator for testing in absence of door
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-11 17:39:18 +03:00
50a7d8251c
print monitor requests
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-10 21:45:44 +03:00
6ae5b811f4
sql_app: correct monitor mac
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-10 19:11:12 +03:00
21d72f17b2 sql_app: fixed error while setting door state
Old code was taking state from device itself,
	meaning it never changes. Now it is set from
	the incoming request.

Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-09 22:41:07 +03:00
dcd2ff5b89 sql_app: Split doors and monitors
A alot of interface and database boiler plate

Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-09 21:52:42 +03:00
a316374da6 sql_app: remove api key from some functions for testing
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-09 18:21:02 +03:00
a034f17a63 Merge branch 'master' of https://github.com/HeshamTB/ibs
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-08 18:30:09 +03:00
40030c91a1 sql_app: systemd unit file
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-08 18:27:25 +03:00
cdf3a6dfb1 sql_app: implement user activate/deactivate
Previous code now rejects all user authenticated
	endpoints. Even /users/me.

Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-08 14:37:26 +03:00
35f0e8abb4 sql_app: Use ORM relations for access log requests
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-08 14:31:47 +03:00
6942d4881d sql_app: missing attribute force_close
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-08 14:31:08 +03:00
6a3d9d9e95 sql_app: Use more relations in database
Instead of using manual db lookup for access log,
	use device.access_list to fetch the data from
	access_log table. This does the SQL query underneath.

	To do so, instructions from SQLAlchemey were followed
	to to many-to-one relations for users and devices,
	respectevly.

Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-08 14:19:27 +03:00
ae5f4e040d sql_app: init_db: added connections and open/close commands
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-08 14:18:43 +03:00
0a4a560ac5 sql_app: fixed errors in room access log endpoints
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-08 13:26:41 +03:00
fdb5be583b sql_app: data: Endpoint to fetch sensor data
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-08 13:01:47 +03:00
c2048d8dba sql_app: User can change password
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-08 12:00:12 +03:00
84db98b6f6
sql_app: Remove hard coded HTTPException codes
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-07 21:40:01 +03:00
2811b8a5ef
sql_app: update TODO
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-07 21:29:08 +03:00
b394bd9f2b
hot fix
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-07 21:14:25 +03:00
4fdd7c231c
sql_app: door: check if valid token on file requests
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-07 21:09:38 +03:00
b790c6657a
sql_app: door: send actual access_list_counter instead of 0
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-07 20:15:24 +03:00
0125f3e5fc
sql_app: Add correct door mac
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-07 20:01:43 +03:00
6f45fc95aa sql_app: remove record_user_connection() due to bug. fix later
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-07 19:17:32 +03:00
3bcb333df6
sql_app: Added state to Iot Door
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-07 18:58:23 +03:00
2eed2db77e sql_app: data: collect user connection times
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-07 16:11:39 +03:00
d9f3a9da4e
sql_app: Introduce force_close flag for Iot Door
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-07 15:48:28 +03:00
4789b2778c
sql_app: init_db: Added a link (allowance)
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-07 15:02:33 +03:00
6850823ae8
sql_app: init_db: Add total of 3 users
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-07 14:54:04 +03:00
2d7ba032a8
encryption: scrapped attempt for BT encryption on esp32
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-07 14:43:43 +03:00
e45e335d50 sql_app: added init db
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-07 14:41:48 +03:00
311058f09b sql_app: init_secrets: add new variable for first user password
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-07 14:41:06 +03:00
01e6c990f7
sql_app: Reorder
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-07 12:58:56 +03:00
bf14c97cb6
sql_app: main: User access list now includes room sensor data
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-07 12:56:10 +03:00
317b3825c3
helper scripts
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-06 20:07:26 +03:00
7c94abebe5
tests: refactor and added user token tests
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-06 20:03:15 +03:00
1006baa212
sql_app: tests: Add basic unit tests (User Create)
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-06 19:26:48 +03:00
868c97fe73
sql_apps: tests: Add reqs
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-06 18:24:32 +03:00
89f0bc354c sql_app: Added new endpoints to fetch iot dev access list info
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-06 18:17:03 +03:00
80f6c629d4 sql_app: create user now stores new api token
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-06 18:16:06 +03:00
2b4d3bf7f5 sql_app: Implemented access list counter:
A counter is associated with every
	Iot Device. The counter is always increment
	when a user is allowed or disallowed to use
	the device, hence, ensuring coherency.
	It is also now exposed in the
	IotDoorPollingRequest schema, enabling the
	Iot Device to fetch the new access list.

Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-06 16:38:35 +03:00
3a6a1ccefd sql_app: db: Changed incorrect variable name
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-06 16:33:02 +03:00
244b7c6b91
sql_app: db: Fixed column copy warning.
A warning was raised whenever the user<->device
	relation was accessed. The assumption is that
	now the queries perform faster, having fixed
	this issue.

Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-05 17:10:35 +03:00
232ff82c46 sql_app: save last issued token in user record
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-05 16:25:20 +03:00
59757ae269
sql_app: TODO
Signed-off-by: HeshamTB <hishaminv@gmail.com>
2022-06-05 14:02:50 +03:00
26 changed files with 1383 additions and 591 deletions

1
.gitignore vendored
View File

@ -5,4 +5,5 @@ server.orig.key
__pycache__
venv/
*.db
sql_app.db*
.vscode/

View File

@ -0,0 +1,130 @@
From b08a24bedfb247fd148c48e00ee5d9b544991dfe Mon Sep 17 00:00:00 2001
From: HeshamTB <hishaminv@gmail.com>
Date: Thu, 14 Apr 2022 07:16:28 +0300
Subject: [PATCH] admin: All admin path functions require an APIKey
Signed-off-by: HeshamTB <hishaminv@gmail.com>
---
sql_app/auth_helper.py | 10 +++++++++-
sql_app/main.py | 19 ++++++++++---------
2 files changed, 19 insertions(+), 10 deletions(-)
diff --git a/sql_app/auth_helper.py b/sql_app/auth_helper.py
index a9b866b..12aa271 100644
--- a/sql_app/auth_helper.py
+++ b/sql_app/auth_helper.py
@@ -3,18 +3,22 @@ from typing import Optional
from decouple import config
from datetime import datetime, timedelta
from sqlalchemy.orm import Session
-from fastapi import Depends
+from fastapi import Depends, Security, HTTPException
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
+from fastapi.security.api_key import APIKey, APIKeyHeader
from . import crud, crypto, schemas
import jwt
import time
JWT_SECRET = config('jwt_secret')
JWT_ALGO = config('jwt_algorithm')
+__API_KEY = config('API_KEY')
+__API_KEY_NAME = config('API_KEY_NAME')
+api_key_header = APIKeyHeader(name=__API_KEY_NAME)
def create_access_token(data : dict, expires_delta : Optional[timedelta] = None):
# TODO: Consider making non-expiring token
@@ -33,3 +37,7 @@ def authenticate_user(db: Session, username : str, password : str):
return False
return crypto.verify_key(password, user.passwd_salt, user.hashed_password)
+def valid_api_key(key = Security(api_key_header)):
+ if not __API_KEY == key:
+ raise HTTPException(401, detail="invalid key")
+ return
diff --git a/sql_app/main.py b/sql_app/main.py
index 413db35..9a9434e 100644
--- a/sql_app/main.py
+++ b/sql_app/main.py
@@ -1,5 +1,6 @@
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
+from fastapi.security.api_key import APIKey
from sqlalchemy.orm import Session
from . import crud, models, schemas, auth_helper
@@ -65,31 +66,31 @@ def get_user_details(current_user: schemas.User = Depends(get_current_active_use
return current_user
@app.get("/admin/users/", response_model=List[schemas.User], tags=['Admin'])
-def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
+def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db), api_key: APIKey = Depends(auth_helper.valid_api_key)):
users = crud.get_users(db, skip=skip, limit=limit)
return users
@app.get("/admin/iotentities/", response_model=List[schemas.IotEntity], tags=['Admin'])
-def read_iot_entities(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
+def read_iot_entities(skip: int = 0, limit: int = 100, db: Session = Depends(get_db), api_key: APIKey = Depends(auth_helper.valid_api_key)):
iot_entities = crud.get_iot_entities(db, skip=skip, limit=limit)
return iot_entities
# TODO: Can duplicate
@app.post("/admin/iotentities/create", response_model=schemas.IotEntity, tags=['Admin'])
-def create_iot_entities(iot_entity: schemas.IotEntityCreate, db: Session = Depends(get_db)):
+def create_iot_entities(iot_entity: schemas.IotEntityCreate, db: Session = Depends(get_db), api_key: APIKey = Depends(auth_helper.valid_api_key)):
iot_entities = crud.create_iot_entity(db, iot_entity)
return iot_entities
@app.get("/admin/users/{user_id}", response_model=schemas.User, tags=['Admin'])
-def read_user(user_id: int, db: Session = Depends(get_db)):
+def read_user(user_id: int, db: Session = Depends(get_db), api_key: APIKey = Depends(auth_helper.valid_api_key)):
db_user = crud.get_user(db, user_id=user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user
# TODO: Can duplicate
@app.post("/admin/users/allowdevice/id", tags=['Admin'])
-def allow_user_for_iot_entity_by_id(request: schemas.UserAllowForIotEntityRequestByID, db: Session = Depends(get_db)):
+def allow_user_for_iot_entity_by_id(request: schemas.UserAllowForIotEntityRequestByID, db: Session = Depends(get_db), api_key: APIKey = Depends(auth_helper.valid_api_key)):
user = crud.get_user(db, request.user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
@@ -105,7 +106,7 @@ def allow_user_for_iot_entity_by_id(request: schemas.UserAllowForIotEntityReques
return user
@app.post("/admin/users/disallowdevice/id", tags=['Admin'])
-def disallow_user_for_iot_entity_by_id(request: schemas.UserAllowForIotEntityRequestByID, db: Session = Depends(get_db)):
+def disallow_user_for_iot_entity_by_id(request: schemas.UserAllowForIotEntityRequestByID, db: Session = Depends(get_db), api_key: APIKey = Depends(auth_helper.valid_api_key)):
user = crud.get_user(db, request.user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
@@ -122,7 +123,7 @@ def disallow_user_for_iot_entity_by_id(request: schemas.UserAllowForIotEntityReq
return
@app.post("/admin/users/allowdevice/name", tags=['Admin'])
-def allow_user_for_iot_entity_by_name(request: schemas.UserAllowForIotEntityRequestByUsername, db: Session = Depends(get_db)):
+def allow_user_for_iot_entity_by_name(request: schemas.UserAllowForIotEntityRequestByUsername, db: Session = Depends(get_db), api_key: APIKey = Depends(auth_helper.valid_api_key)):
user = crud.get_user_by_username(db, request.username)
if not user:
raise HTTPException(status_code=404, detail="User not found")
@@ -138,11 +139,11 @@ def allow_user_for_iot_entity_by_name(request: schemas.UserAllowForIotEntityRequ
return
@app.post("/admin/users/{user_id}/deactiveate", tags=['Admin'])
-def deactiveate_user(user_id: int, db:Session = Depends(get_db)):
+def deactiveate_user(user_id: int, db:Session = Depends(get_db), api_key: APIKey = Depends(auth_helper.valid_api_key)):
return
@app.post("/admin/users/{user_id}/activeate", tags=['Admin'])
-def deactiveate_user(user_id: int, db:Session = Depends(get_db)):
+def deactiveate_user(user_id: int, db:Session = Depends(get_db), api_key: APIKey = Depends(auth_helper.valid_api_key)):
return
@app.get("/users/acesslist/", response_model=List[schemas.IotEntity], tags=['Users'])
--
libgit2 1.4.3

33
encryption/encryption.c Normal file
View File

@ -0,0 +1,33 @@
/*
* Wrapper around mbedtls to provide AES encryption
*
* Needs esp32 arduino platform libraries
*
* Hesham T. Banafa
* May 9th, 2022
*
*/
#include <string.h>
#include "encryption.h"
static int valid_time(long long time_epoch);
extern void aes_init(aes_t *ctx, char *key)
{
mbedtls_aes_init(&ctx->aes_ctx);
//mbedtls_aes_setkey_enc(ctx->aes_ctx, (const unsigned char*)key, strlen(key) * 8 );
ctx->psk_key = key; // Save key ptr
}
extern void aes_encrypt(aes_t *ctx, char *plain_text, char *out_buf)
{
if (ctx == NULL) return; // What are you doing? out_buf remains as is.
mbedtls_aes_setkey_enc(&ctx->aes_ctx, (const unsigned char*)ctx->psk_key, strlen(ctx->psk_key) * 8 );
mbedtls_aes_crypt_ecb(&ctx->aes_ctx, MBEDTLS_AES_ENCRYPT, (const unsigned char*)plain_text, (unsigned char*)out_buf);
}
extern void aes_decrypt(aes_t *ctx, char *cipher_text, char *out_buf)
{
}

24
encryption/encryption.h Normal file
View File

@ -0,0 +1,24 @@
/*
* Wrapper around mbedtls to provide AES encryption
*
* Needs esp32 arduino platform libraries
*
* Hesham T. Banafa
* May 9th, 2022
*
*/
#include "mbedtls/aes.h"
typedef struct aes_t
{
const char *psk_key;
mbedtls_aes_context aes_ctx;
} aes_t;
static int valid_time(long long time_epoch);
extern void aes_init(aes_t *ctx, char *key);
extern void aes_encrypt(aes_t *ctx, char *plain_text, char *out_buf);
extern void aes_decrypt(aes_t *ctx, char *cipher_text, char *out_buf);

View File

@ -1,9 +1,12 @@
aiocoap==0.4.3
anyio==3.5.0
asgiref==3.5.0
attrs==21.4.0
bitlist==0.6.2
cbor2==5.4.2.post1
certifi==2022.5.18.1
cffi==1.15.0
charset-normalizer==2.0.12
click==8.0.4
cryptography==36.0.1
Cython==0.29.28
@ -17,22 +20,31 @@ greenlet==1.1.2
h11==0.13.0
httptools==0.3.0
idna==3.3
iniconfig==1.1.1
LinkHeader==0.4.3
packaging==21.3
parts==1.2.2
pluggy==1.0.0
py==1.11.0
pycparser==2.21
pydantic==1.9.0
Pygments==2.11.2
PyJWT==2.3.0
pyparsing==3.0.9
pytest==7.1.2
python-decouple==3.6
python-dotenv==0.19.2
python-multipart==0.0.5
PyYAML==6.0
requests==2.27.1
six==1.16.0
sniffio==1.2.0
SQLAlchemy==1.4.31
starlette==0.17.1
termcolor==1.1.0
tomli==2.0.1
typing_extensions==4.1.1
urllib3==1.26.9
uvicorn==0.17.5
uvloop==0.16.0
watchgod==0.7

10
run-tls
View File

@ -1,4 +1,14 @@
#!/bin/bash
source venv/bin/activate
cd sql_app/
./file_permissios.py
if [ $? == 1 ]
then
echo "enviorment file_permissions are incorrect"
exit 1
fi
cd ../
exec uvicorn sql_app.main:app --ssl-certfile server.crt --ssl-keyfile server.key --port 4040 --host 0.0.0.0 --no-server-header

4
run-tls-reload Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
source venv/bin/activate
exec uvicorn sql_app.main:app --ssl-certfile server.crt --ssl-keyfile server.key --port 4040 --host 0.0.0.0 --no-server-header --reload

View File

@ -1,13 +1,45 @@
- [ ] Redesign database
- [ ] Define emrgancy triggers (manual and automatic)
- [x] Constrcut DB Schema
- [x] Issue requests from Lap to Pi or vica versa
- [x] Decide HTTP vs MQTT vs CoAP
- [x] Hash passwords
- [x] Salt passwords
- [x] User registraion
- [x] User login in API
- [x] JWT token access
- [x] Look into how to revoke a signed key
- [ ] Record Session info in token & db (allow for session cancel)
- [x] Add username for users (now only email)
- [X] Expose Room monitor function (temp, count humid..)
- [X] Expose door lock function
- [X] Record access log
- [X] Expose data analysis
- [X] Load backend onto RPi
- [X] Test connections in lab network
- [X] Define emrgancy triggers (manual and automatic)
- [ ] Expose temporary control in case of emergancy
- Triggers
- Acccess
- Resolve (revoke access)
- [ ] Temporal door open commands (Needs door state to operate efficiatly)
- Open for 1H
- Open to 1:30PM
- Set schedual
- [X] Temporal door open commands (Needs door state to operate efficiatly)
- [X] Open for 1H
- [ ] Open to 1:30PM
- [ ] Set schedual
- [X] Issue door open command
- [X] Make functions to gen a IotEntity token
- [ ] Save prefix of token to allow/revoke token??
- [ ] Write a small program/script to generate new Iot token and add new Iot Device into database
- [X] Make inital database entries automatic for easy reset
- [X] Expose access list endpoint for doors
- [X] Access list coutner for iot door
- [X] Force close in middle of timed open request
- [ ] Record user connections and time
- [ ] Record Iot dev connection and time
- [ ] Write unit tests
- [ ] Develop a program to visualize the data
- [ ] CLI frontend
- [X] Emergaency
- [X] Send state with accesslist
- [X] Split monitor into different class
- [ ] Make a script that adds types, and thier basic database ops?? (avoid writing boiler-plate)
- [ ] Make a script that emulates a door and monitor
- [ ] Check file premissions on .env file, if global reject
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJibHVldG9vdGhfbWFjIjoic3RyaW5nIn0.ELl5AfBR1NdM4_OFhl_SCTm9EMPpqjiCKOSS0CrOJps

View File

@ -1,3 +1,5 @@
# May 2022
# Hesham T. Banafa <hishaminv@gmail.com>
from typing import Optional
from decouple import config
@ -47,22 +49,22 @@ def create_iot_dev_token(data: dict):
encoded_jwt = jwt.encode(to_encode, JWT_SECRET, algorithm=JWT_ALGO)
return encoded_jwt
def valid_iot_door_token(token : str, db: Session):
def valid_iot_token(token : str, db: Session):
try:
payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGO])
except jwt.DecodeError:
return None
mac_signed = payload.get("bluetooth_mac")
device = crud.get_door_by_bluetooth_mac(db, mac_signed)
device = crud.get_iot_entity_by_bluetooth_mac(db, mac_signed)
return device
def valid_iot_monitor_token(token : str, db: Session):
def valid_monitor_token(token: str, db: Session):
try:
payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGO])
except jwt.DecodeError:
return None
mac_signed = payload.get("bluetooth_mac")
device = crud.get_monitor_by_bluetooth_mac(db, mac_signed)
return device
monitor = crud.get_monitor_bluetooth(db, mac_signed)
return monitor

View File

@ -1,192 +1,265 @@
# March 2022
# Hesham T. Banafa <hishaminv@gmail.com>
# CRUD (Create, Read, Update, Delete) from db
from sqlalchemy import select, join
from sqlalchemy.orm import Session
from . import models, schemas, crypto, auth_helper
from datetime import datetime
from warnings import warn
# TODO: Data we can collect or log
# - Last user connection (link to user)
# - Last Iot Entity Connection (link to IotEntity)
# - Any open request (link to user)
# - Any polling from IotEntity? Maybe to much data
def get_user(db: Session, user_id: int):
def get_user(db: Session, user_id: int) -> models.User:
return db.query(models.User).get(user_id)
def get_door_by_id(door_id: int):
return db.query(models.IotDoor).get(door_id)
def get_iot_entity(db: Session, id: int) -> models.IotEntity:
return db.query(models.IotEntity).get(id)
def get_door_by_description(db: Session, description: str):
# TODO: 2 or more Desciptions may collide
return db.query(models.IotDoor).filter(models.IotDoor.description == description).first()
def get_iot_entity_by_description(db: Session, description: str):
return db.query(models.IotEntity).filter(models.IotEntity.description == description).first()
def get_door_by_bluetooth_mac(db: Session, bluetooth_mac: str):
return db.query(models.IotDoor).filter(models.IotDoor.bluetooth_mac == bluetooth_mac).first()
def get_iot_entity_by_bluetooth_mac(db: Session, bluetooth_mac: str) -> models.IotEntity:
return db.query(models.IotEntity).filter(models.IotEntity.bluetooth_mac == bluetooth_mac).first()
def get_monitor_by_bluetooth_mac(db: Session, bluetooth_mac: str):
return db.query(models.IotMonitor).filter(models.IotMonitor.bluetooth_mac == bluetooth_mac).first()
def get_user_by_email(db: Session, email: str):
def get_user_by_email(db: Session, email: str) -> models.User:
return db.query(models.User).filter(models.User.email == email).first()
def get_user_by_username(db: Session, username: str):
def get_user_by_username(db: Session, username: str) -> models.User:
return db.query(models.User).filter(models.User.username == username).first()
def get_users(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.User).offset(skip).limit(limit).all()
def get_access_log_for_room(db: Session, room_id: int):
return db.query(models.DoorAccessLog).filter(models.DoorAccessLog.room == room_id).all()
def get_access_log_for_door_by_door_mac(db: Session, iot_id: str):
warn("Manual access log read is deprecated. Use device.access_log",
DeprecationWarning, stacklevel=2)
return db.query(models.DoorAccessLog)\
.filter(models.DoorAccessLog.iot_id == iot_id).all()
def get_access_log_for_user_by_id(db: Session, id : str):
return db.query(models.DoorAccessLog).filter(models.DoorAccessLog.user_id == id).all()
def get_room_data_now(db: Session, room_id: int):
return db.query(models.IotMonitor).filter(models.IotMonitor.room_id == room_id)
def get_doors(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.IotDoor).offset(skip).limit(limit).all()
def get_monitors(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.IotMonitor).offset(skip).limit(limit).all()
def get_rooms(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.Room).offset(skip).limit(limit).all()
def get_user_room_access_list_by_username(db: Session, username: str):
user : models.User = get_user_by_username(db, username)
links : List[models.UserRoomAuth] = db.query(models.UserRoomAuth).filter(models.UserRoomAuth.user_id == user.id).all()
return db.query(models.Room).filter(models.Room.authorized_users == user.id).all()
def get_room_door(db: Session, room_id: int):
door = db.query(models.IotDoor).filter(models.IotDoor.room_id == room_id).first()
return door
def get_room(db: Session, room_id: int):
room = db.query(models.Room).get(room_id)
return room
def get_room_access_log_for_user(db: Session, user_id: int, room_id: int):
log = db.query(models.DoorAccessLog).filter(models.DoorAccessLog.user_id == user_id, models.DoorAccessLog.room == room_id).all()
return log
def get_room_current_readings(db: Session, room: schemas.Room) -> models.IotMonitor:
room : models.Room = get_room(db, room.id)
monitor = db.query(models.IotMonitor).filter(models.IotMonitor.room_id == room.id).first()
return mon
def get_monitor_for_room(db: Session, room: schemas.Room) -> models.IotMonitor:
return db.query(models.IotMonitor).filter(models.IotMonitor.room_id == room.id).first()
def get_door_for_room(db: Session, room_id: int) -> models.IotDoor:
return db.query(models.IotDoor).filter(models.IotDoor.room_id == room_id).first()
def get_room_from_door(db: Session, door: models.IotDoor):
return get_room(db, door.room_id)
# def get_room_data_now(db: Session, door_id: int) -> models.RoomSensorData:
# door = get_iot_entity(db, door_id)
# monitor : models.Monitors = door.monitor
# if not monitor: return -1
# if len(monitor.sensor_history) == 0: return -2
# return monitor.sensor_history[-1]
def create_user(db: Session, user: schemas.UserCreate):
key = crypto.gen_new_key(user.password)
salt = key[1]
hashed_pass = key[0]
db_user = models.User(email=user.email, username=user.username,hashed_password=hashed_pass, passwd_salt=salt)
db_user = models.User(email=user.email,
username=user.username,
hashed_password=hashed_pass,
passwd_salt=salt)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
def create_door(db: Session, door: schemas.IotDoorCreate):
db_item = models.IotDoor(room_id=door.room_id,
description=door.description,
bluetooth_mac=door.bluetooth_mac)
def update_user_password(db: Session, user: models.User, request: schemas.UserUpdatePassword):
key = crypto.gen_new_key(request.password)
salt = key[1]
hashed_pass = key[0]
user.passwd_salt = salt
user.hashed_password = hashed_pass
db.add(user)
db.commit()
db.refresh(user)
def get_iot_entities(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.IotEntity).offset(skip).limit(limit).all()
def get_monitors(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.Monitors).offset(skip).limit(limit).all()
def create_iot_entity(db: Session, iot_entity: schemas.IotEntityCreate):
db_item = models.IotEntity(bluetooth_mac=iot_entity.bluetooth_mac,
description=iot_entity.description)
db.add(db_item)
db.commit()
db.refresh(db_item)
return db_item
def create_monitor(db: Session, monitor: schemas.IotMonitorCreate):
db_item = models.IotMonitor(room_id=monitor.room_id,
description=monitor.description,
bluetooth_mac=monitor.bluetooth_mac)
db.add(db_item)
db.commit()
db.refresh(db_item)
return
def create_monitor(db: Session, monitor: schemas.IotEntityBase):
db_item = models.Monitors(bluetooth_mac=monitor.bluetooth_mac,
description=monitor.description)
def create_room(db: Session, room: schemas.RoomCreate):
db_item = models.Room(building_name=room.building_name,
building_number=room.building_number)
db.add(db_item)
db.commit()
db.refresh(db_item)
return db_item
def allow_user_room_access(db: Session, user_id: int, room_id: int):
link = db.query(models.UserRoomAuth).filter(models.UserRoomAuth.user_id == user_id, models.UserRoomAuth.room_id == room_id).first()
if link: return link
new_link = models.UserRoomAuth(user_id=user_id, room_id=room_id)
def get_monitor(db: Session, id: int) -> models.Monitors:
return db.query(models.Monitors).get(id)
def get_monitor_bluetooth(db: Session, bluetooth_mac: str) -> models.Monitors:
return db.query(models.Monitors).filter(models.Monitors.bluetooth_mac == bluetooth_mac).first()
def update_monitor(db: Session, monitor: models.Monitors):
db.add(monitor)
db.commit()
db.refresh(monitor)
def update_monitor_readings(db: Session, monitor_upadte: schemas.MonitorUpdateReadings, bluetooth_mac: str):
monitor = get_monitor_bluetooth(db, bluetooth_mac)
monitor.humidity = monitor_upadte.humidity
monitor.people = monitor_upadte.people
monitor.smoke_sensor_reading = monitor_upadte.smoke_sensor_reading
monitor.temperature = monitor_upadte.temperature
db.add(monitor)
db.commit()
db.refresh(monitor)
def create_user_link_to_iot(db: Session, user_id: int, iot_dev_id: int):
# Ensure link is not already present and it does not allow duplicates
link = db.query(models.UserAuthToIoTDev).filter(models.UserAuthToIoTDev.user_id == user_id).filter(models.UserAuthToIoTDev.iot_id == iot_dev_id).first()
if link: return True
new_link = models.UserAuthToIoTDev(user_id=user_id,
iot_id=iot_dev_id,
timestamp=datetime.now())
db.add(new_link)
db.commit()
db.refresh(new_link)
return link
return True
def disallow_user_room_access(db: Session, user_id, room_id: int):
link = db.query(models.UserRoomAuth).filter(models.UserRoomAuth.user_id == user_id, models.UserRoomAuth.room_id == room_id)
def remove_user_link_to_iot(db: Session, user_id: int, iot_dev_id: int):
# Ensure link is not already present and it does not allow duplicates
link = (db.query(models.UserAuthToIoTDev)
.filter(models.UserAuthToIoTDev.user_id == user_id)
.filter(models.UserAuthToIoTDev.iot_id == iot_dev_id)
.first())
if not link: return True
db.delete(link)
db.flush()
db.commit()
#db.refresh(link)
return True
def open_door_request(db: Session, request: schemas.OpenRoomRequest, time_seconds: int = 10):
link = db.query(models.UserRoomAuth).filter(models.UserRoomAuth.user_id == request.user_id,
models.UserRoomAuth.room_id == request.room_id).first()
if not link: return False
door : models.IotDoor = db.query(models.IotDoor).filter(models.IotDoor.room_id == request.room_id).first()
if not door: return False
setattr(door, "open_request", True)
def set_open_door_request(db: Session, iot_entity_id: int, time_seconds : int):
device = get_iot_entity(db, iot_entity_id)
setattr(device, "open_request", True)
if time_seconds < 1:
time_seconds = 10 # Magic number move to global constant
setattr(door, "time_seconds", time_seconds)
db.add(door)
setattr(device, "time_seconds", time_seconds)
db.add(device)
db.commit()
db.refresh(door)
db.refresh(device)
return True
def clear_open_door_request(db: Session, room_id: int):
door : models.IotDoor = db.query(models.IotDoor).filter(models.IotDoor.room_id == room_id).first()
setattr(door, "open_request", False)
setattr(door, "time_seconds", 10)
db.add(door)
def set_close_door_request(db: Session, iot_id: int):
device : models.IotEntity = get_iot_entity(db, iot_id)
device.force_close = True
db.add(device)
db.commit()
db.refresh(door)
db.refresh(device)
return True
def clear_close_door_request(db: Session, iot_id: int):
device : models.IotEntity = get_iot_entity(db, iot_id)
device.force_close = False
db.add(device)
db.commit()
def set_user_last_token(db: Session, username: str, token: str):
user : models.User = get_user_by_username(db, username)
user.last_token = token
db.add(user)
db.commit()
db.refresh(user)
return True
def set_door_state(db: Session, iot_device: models.IotEntity, state: bool):
iot_device.state = state
db.add(iot_device)
db.commit()
db.refresh(iot_device)
def get_user_last_token(db: Session, username: str):
user : models.User = get_user_by_username(db, username)
return user.last_token # This method is bad security practice.
def clear_open_door_request(db: Session, iot_entity_id: int):
device = get_iot_entity(db, iot_entity_id)
setattr(device, "open_request", False)
setattr(device, "time_seconds", 10)
db.add(device)
db.commit()
db.refresh(device)
return True
def record_door_access_log(db: Session, entry: schemas.DoorAccessLog):
db_item = models.DoorAccessLog(user_id=entry.user_id,
room=entry.room_id,
iot_id=entry.iot_id,
command=entry.command,
timestamp=entry.timestamp)
db.add(db_item)
db.commit()
db.refresh(db_item)
def record_room_sensor_data(db: Session, entry: schemas.IotMonitorRoomInfo):
def record_room_sensor_data(db: Session, entry: schemas.MonitorUpdateReadings,
monitor :models.Monitors):
db_item = models.RoomSensorData(humidity=entry.humidity,
people=entry.people,
temperature=entry.temperature,
smoke=entry.smoke,
timestamp=datetime.now())
smoke_sensor_reading=entry.smoke_sensor_reading,
timestamp=datetime.now(),
monitor_id=monitor.id)
db.add(db_item)
db.commit()
db.refresh(db_item)
def on_access_list_change(db: Session, room_id: int, allow: bool):
# Use when a user is allowed or disallowed to a room.
door = get_door_for_room(db, room_id)
door.accesslist_counter = door.accesslist_counter + 1
db.add(door)
db.commit()
db.refresh(door)
monitor.humidity = entry.humidity
monitor.temperature = entry.temperature
monitor.people = entry.people
monitor.smoke_sensor_reading = entry.smoke_sensor_reading
# TODO: Get room sensor data
# TODO: Get room door status
db.add(monitor)
db.commit()
db.refresh(monitor)
def increment_door_access_list_counter(db: Session, iot_entity: models.IotEntity):
iot_entity.acces_list_counter = iot_entity.acces_list_counter + 1
db.add(iot_entity)
db.commit()
db.refresh(iot_entity)
def record_user_connection(db: Session, user: models.User, time: datetime):
entry = models.UserConnectionHistory(user_id=user.id, timestamp=time)
db.add(entry)
db.commit()
db.refresh(entry)
# def get_sensor_data_for_room(db: Session, monitor_id: int, count_last: int):
# data = db.query(models.RoomSensorData).all()
# if not data or len(data) == 0: return -1
# return data[-count_last]
def update_user_status(db: Session, user: models.User, state: bool):
user.is_active = state
db.add(user)
db.commit()
db.refresh(user)
def record_emergancy_entry(db: Session, monitor_data: schemas.MonitorUpdateReadings, monitor_id: int):
new_entry : models.EmergancyNotice = models.EmergancyNotice(
monitor_id=monitor_id,
people=monitor_data.people,
temperature=monitor_data.temperature,
smoke_sensor_reading=monitor_data.smoke_sensor_reading,
timestamp=datetime.now()
)
db.add(new_entry)
db.commit()
db.refresh(new_entry)

View File

@ -1,3 +1,5 @@
# March 2022
# Hesham T. Banafa <hishaminv@gmail.com>
import os
from hashlib import pbkdf2_hmac

View File

@ -1,8 +1,11 @@
# March 2022
# Hesham T. Banafa <hishaminv@gmail.com>
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "sqlite:///./data.db"
SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"
# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"
# For sqlite

45
sql_app/enroll_door.py Normal file
View File

@ -0,0 +1,45 @@
# Quick enroll new device
# Hesham T. Banafa
# Jun 12th, 2022
from decouple import config
import requests
# idk if this stays in memory...
headers = {
"accept": "application/json",
"Content-type": "application/json"
}
def main():
if len(sys.argv) != 4:
print_help()
exit(1)
device_type = sys.argv[1]
bluetooth_mac = sys.argv[2]
description = sys.argv[3]
if device_type == 'DOOR':
mkdoor(bluetooth_mac, description)
elif device_type == 'MONITOR':
mkmonitor(bluetooth_mac, description)
else:
print('Device type not DOOR or MONITOR', file=sys.stderr)
exit(1)
# gen print token of bluetooth_mac
print(create_iot_dev_token(bluetooth_mac))
def mkdoor(bluetooth_mac: str, description: str):
data = {
"bluetooth_mac": bluetooth_mac,
"description": description
}
#response = requests.post("")
def mkmonitor(bluetooth_mac: str, description: str):
pass
def print_help():
msg = 'usgae: enroll_iotdevice <DOOR|MONITOR> <bluetooth_mac> <description>'
print(msg)

18
sql_app/file_permissios.py Executable file
View File

@ -0,0 +1,18 @@
#!/bin/python
# Hesham T. Banafa
# Jun 12th, 2022
# Check enviorment file permissions and return -1 if fails or 0
import os
import stat
ENV_FILE='.env'
st = os.stat(ENV_FILE)
if st.st_mode & stat.S_IROTH or \
st.st_mode & stat.S_IWOTH or \
st.st_mode & stat.S_IXOTH:
exit(1)
exit(0)

137
sql_app/init_db.py Normal file
View File

@ -0,0 +1,137 @@
# June 2022
# Hesham T. Banafa <hishaminv@gmail.com>
from . import crud, main, schemas, auth_helper
from decouple import config
from .database import SessionLocal
from datetime import timedelta, datetime
from random import randint
db = SessionLocal()
def init_user():
user = schemas.UserCreate(email="hisham@banafa.com.sa",
username="Hesham",
password=config('first_user_pass'))
user_exists = crud.get_user_by_email(db, user.email)
if user_exists: return
crud.create_user(db, user)
token = auth_helper.create_access_token(data={"sub": user.username}, expires_delta=timedelta(minutes=15))
res = crud.set_user_last_token(db, user.username, token)
user_exists = None
user = schemas.UserCreate(email="osama@mail.none",
username="Osama",
password=config('first_user_pass'))
user_exists = crud.get_user_by_email(db, user.email)
if user_exists: return
crud.create_user(db, user)
token = auth_helper.create_access_token(data={"sub": user.username}, expires_delta=timedelta(minutes=15))
res = crud.set_user_last_token(db, user.username, token)
user_exists = None
user = schemas.UserCreate(email="Hussain@mail.none",
username="Hussain",
password=config('first_user_pass'))
user_exists = crud.get_user_by_email(db, user.email)
if user_exists: return
crud.create_user(db, user)
token = auth_helper.create_access_token(data={"sub": user.username}, expires_delta=timedelta(minutes=15))
res = crud.set_user_last_token(db, user.username, token)
user_exists = None
user = schemas.UserCreate(email="Assad@mail.none",
username="Assad",
password=config('first_user_pass'))
user_exists = crud.get_user_by_email(db, user.email)
if user_exists: return
crud.create_user(db, user)
token = auth_helper.create_access_token(data={"sub": user.username}, expires_delta=timedelta(minutes=15))
res = crud.set_user_last_token(db, user.username, token)
def init_door():
iot_door = schemas.IotEntityCreate(bluetooth_mac="94:b9:7e:fb:57:1a",
description="Iot Lab Door")
door_exists = crud.get_iot_entity_by_bluetooth_mac(db, iot_door.bluetooth_mac)
if door_exists: return
crud.create_iot_entity(db, iot_door)
def init_monitor():
iot_monitor = schemas.IotEntityCreate(bluetooth_mac="ff:ff:ff",
description="Iot Lab Monitor")
monitor_exists = crud.get_monitor_bluetooth(db, iot_monitor.bluetooth_mac)
if monitor_exists: return
crud.create_monitor(db, iot_monitor)
def init_allowance():
crud.create_user_link_to_iot(db, 1, 1)
def init_sensor_data():
monitor = crud.get_monitor(db, 1)
if monitor.sensor_history: return
for i in range(50):
room_data = \
schemas.\
IotMonitorRoomInfo\
(humidity=randint(20, 80),
people=randint(0, 10),
temperature=randint(18, 27),
smoke_sensor_reading=randint(150, 700),
token='dummy')
crud.record_room_sensor_data(db, room_data, monitor)
def init_open_close_requests():
user = crud.get_user_by_email(db, "hisham@banafa.com.sa")
if user.access_log: return
crud.set_open_door_request(db, 1, 10)
log_entry = schemas.DoorAccessLog(user_id=user.id,
iot_id=1,
command="OPEN",
timestamp=datetime.now())
crud.record_door_access_log(db, log_entry)
log_entry = schemas.DoorAccessLog(user_id=user.id,
iot_id=1,
command="OPEN",
timestamp=datetime.now())
crud.record_door_access_log(db, log_entry)
log_entry = schemas.DoorAccessLog(user_id=user.id,
iot_id=1,
command="OPEN",
timestamp=datetime.now())
crud.record_door_access_log(db, log_entry)
log_entry = schemas.DoorAccessLog(user_id=user.id,
iot_id=1,
command="CLOSE",
timestamp=datetime.now())
crud.record_door_access_log(db, log_entry)
def init_user_connections():
users = [ crud.get_user(db, 1),
crud.get_user(db, 2),
crud.get_user(db, 3)]
for i in range(3):
crud.record_user_connection(db, users[i], datetime.now())
crud.record_user_connection(db, users[i], datetime.now())
crud.record_user_connection(db, users[i], datetime.now())
def init_link_room_monitor():
monitor = crud.get_monitor(db, 1)
door = crud.get_iot_entity(db, 1)
monitor.door = door
crud.update_monitor(db, monitor)
def init():
init_user()
init_door()
init_monitor()
init_allowance()
init_sensor_data()
init_open_close_requests()
init_user_connections()
init_link_room_monitor()

View File

@ -4,3 +4,17 @@ echo "API_KEY=$(./gen_secret.sh)" >> .env
echo "API_KEY_NAME=big_boy" >> .env
echo "jwt_secret=$(./gen_secret.sh)" >> .env
echo "jwt_algorithm=HS256" >> .env
read -s -p "First User password: " firstpass
echo
read -s -p "Retype First User password: " secondpass
echo
if [ $firstpass != $secondpass ];
then
echo "Passwords dont match!"
exit 255
fi
echo "first_user_pass=$firstpass" >> .env
chmod 600 .env

View File

@ -1,26 +0,0 @@
log_config = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"access": {
"()": "uvicorn.logging.AccessFormatter",
"fmt": '%(levelprefix)s %(asctime)s :: %(client_addr)s - "%(request_line)s" %(status_code)s',
"use_colors": True
},
},
"handlers": {
"access": {
"formatter": "access",
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout",
},
},
"loggers": {
"uvicorn.access": {
"handlers": ["access"],
"level": "INFO",
"propagate": False
},
},
}

View File

@ -1,10 +1,15 @@
# March 2022
# Hesham T. Banafa <hishaminv@gmail.com>
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm, OAuth2AuthorizationCodeBearer
from fastapi.security.api_key import APIKey
from fastapi.responses import PlainTextResponse
from sqlalchemy.orm import Session
from . import crud, models, schemas, auth_helper
from . import crud, models, schemas, auth_helper, init_db
from .database import SessionLocal, engine
from .utils import get_db, EMERG_SMOKE, EMERG_TEMP, EMERG_OPEN_TIME_SEC
from typing import List
from datetime import timedelta, datetime
@ -15,229 +20,151 @@ oauth = OAuth2PasswordBearer(tokenUrl="tkn")
app = FastAPI(title="IoT Building System")
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"})
# Split into endpoints modules
#app.include_router(users.router,prefix="/users", tags=["User"])
init_db.init()
inactive_user_exception = HTTPException(status_code=status.HTTP_403_FORBIDDEN,
detail="Inactive user")
email_used_exception = HTTPException(status_code=400,
detail="Email already registered")
username_used_exception = HTTPException(status_code=400,
detail="Username already registerd")
user_not_found_exception = HTTPException(status_code=404,
detail="User not found")
room_not_found_exception = HTTPException(status_code=404,
detail="Room not found")
unauth_to_open = HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,
detail="Unauthrized to open")
str_not_implemented = 'Not Implemented'
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
def get_current_user(token: str = Depends(oauth), db: Session = Depends(get_db)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, auth_helper.JWT_SECRET, algorithms=[auth_helper.JWT_ALGO])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
#token_data = schemas.TokenData(username=username)
token_data = schemas.TokenData(username=username)
except jwt.PyJWTError:
raise credentials_exception
user = crud.get_user_by_username(db, username=username)
user = crud.get_user_by_username(db, username=token_data.username)
if user is None:
raise credentials_exception
return user
def get_current_active_user(current_user: schemas.User = Depends(get_current_user)):
if not current_user.is_active:
raise inactive_user_exception
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
def get_current_iot_device(current_device: schemas.IotBluetoothMac = Depends(),
db: Session = Depends(get_db)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
payload = jwt.decode(token, auth_helper.JWT_SECRET, algorithms=[auth_helper.JWT_ALGO])
mac_signed = payload.get("bluetooth_mac")
if (mac_signed != current_device): raise credentials_exception
device = crud.get_iot_entity_by_bluetooth_mac(db, mac_signed)
return device
@app.post("/users/reg", response_model=schemas.User, tags=['Users'])
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
db_user = crud.get_user_by_email(db, email=user.email)
if db_user:
raise email_used_exception
raise HTTPException(status_code=400, detail="Email/Username already registered")
db_user = crud.get_user_by_username(db, username=user.username)
if db_user:
raise username_used_exception
return crud.create_user(db=db, user=user)
raise HTTPException(status_code=400, detail="Email/Username already registered")
db_user = crud.create_user(db=db, user=user)
if not db_user:
raise HTTPException(status_code=500, detail="Failed to create user")
access_token = auth_helper.create_access_token(
data={"sub": db_user.username}, expires_delta=timedelta(minutes=15)
)
crud.set_user_last_token(db, db_user.username, access_token)
#crud.record_user_connection(db, db_user, datetime.now())
return db_user
@app.get("/users/me/", response_model=schemas.User, tags=['Users'])
def get_user_details(current_user: schemas.User = Depends(get_current_active_user)):
def get_user_details(db: Session = Depends(get_db),
current_user: schemas.User = Depends(get_current_active_user)):
#crud.record_user_connection(db, current_user, datetime.now())
return current_user
@app.get("/admin/users/", response_model=List[schemas.User], tags=['Admin'])
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
users = crud.get_users(db, skip=skip, limit=limit)
return users
@app.get("/admin/doors/", response_model=List[schemas.IotDoor], tags=['Admin'])
def read_iot_doors(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
doors = crud.get_doors(db, skip=skip, limit=limit)
return doors
@app.get("/admin/monitors/", response_model=List[schemas.IotMonitor], tags=['Admin'])
def read_iot_doors(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
monitors = crud.get_monitors(db, skip=skip, limit=limit)
return monitors
@app.get("/admin/rooms/", response_model=List[schemas.Room], tags=['Admin'])
def read_iot_doors(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
rooms = crud.get_rooms(db, skip=skip, limit=limit)
return rooms
@app.post("/admin/create/door/", response_model=schemas.IotDoor, tags=['Admin'])
def create_iot_entities(door: schemas.IotDoorCreate, db: Session = Depends(get_db)):
#iot_entities = crud.create_iot_entity(db, iot_entity)
new_door = crud.create_door(db, door)
return new_door
@app.post("/admin/create/monitor/", response_model=schemas.IotMonitor, tags=['Admin'])
def create_iot_entities(monitor: schemas.IotMonitorCreate, db: Session = Depends(get_db)):
#iot_entities = crud.create_iot_entity(db, iot_entity)
new_monitor = crud.create_monitor(db, monitor)
return new_monitor
@app.post("/admin/create/room/", response_model=schemas.Room, tags=['Admin'])
def create_iot_entities(room: schemas.RoomCreate, db: Session = Depends(get_db)):
new_room = crud.create_room(db, room)
return new_room
@app.get("/admin/users/{user_id}", response_model=schemas.User, tags=['Admin'])
def read_user(user_id: int, db: Session = Depends(get_db)):
db_user = crud.get_user(db, user_id=user_id)
if db_user is None:
raise user_not_found_exception
return db_user
@app.post("/admin/users/allowaccess/id", response_model=schemas.AllowRoomAccessRequestID, tags=['Admin'])
def allow_user_room_access_by_id(request: schemas.AllowRoomAccessRequestID,
db: Session = Depends(get_db)):
user = crud.get_user(db, request.user_id)
if not user:
raise user_not_found_exception
room = crud.get_room(db, request.room_id)
if not room:
raise room_not_found_exception
#res = crud.create_user_link_to_iot(db, request.user_id, request.iot_entity_id)
res = crud.allow_user_room_access(db, request.user_id, request.room_id)
if not res:
raise HTTPException(status_code=500, detail="Could not complete operation")
return request
@app.post("/admin/users/disallowaccess/id", tags=['Admin'])
def disallow_user_room_access_by_id(request: schemas.AllowRoomAccessRequestID,
db: Session = Depends(get_db)):
user = crud.get_user(db, request.user_id)
if not user:
raise user_not_found_exception
room = crud.get_room(db, request.room_id)
if not room:
raise room_not_found_exception
res = crud.disallow_user_room_access(db, request.user_id, request.room_id)
if not res:
raise HTTPException(status_code=500, detail="Could not complete operation")
return
@app.post("/admin/users/{user_id}/deactiveate", tags=['Admin'], description=str_not_implemented)
def deactiveate_user(user_id: int, db:Session = Depends(get_db)):
return
@app.post("/admin/users/{user_id}/activeate", tags=['Admin'], description=str_not_implemented)
def deactiveate_user(user_id: int, db:Session = Depends(get_db)):
return
@app.post("/admin/iotdevice/gentoken/", response_model=schemas.Token, tags=['Admin'])
def generate_token_for_iot_device(bluetooth_mac : schemas.IotBluetoothMac,
api_key: APIKey = Depends(auth_helper.valid_api_key)):
# We get here after a valid admin key, so send back permenant token
data = {"bluetooth_mac": bluetooth_mac.bluetooth_mac}
tkn = auth_helper.create_iot_dev_token(data)
return {"access_token": tkn, "token_type": "bearer"}
@app.post("/admin/room/accesslog/", tags=['Admin'])
def get_access_log_for_door(request : schemas.RoomAccessLogRequest,
db : Session = Depends(get_db)):
room = crud.get_room(db, request.room_id)
if not room: raise room_not_found_exception
return crud.get_access_log_for_room(db, request.room_id)
@app.post("/admin/room/authrizedusers/", tags=['Admin'])
def get_room_authrized_users(room: schemas.Room,
db: Session = Depends(get_db)):
room: models.Room = crud.get_room(db, room_id=room.id)
return room.authorized_users
@app.post("/admin/user/accesslog/room/", tags=['Admin'])
def get_room_access_log_history_for_user(request : schemas.RoomAccessLogRequestUserID,
db : Session = Depends(get_db)):
user = crud.get_user(db, request.user_id)
if not user: raise user_not_found_exception
room = crud.get_room(db, request.room_id)
if not room: raise room_not_found_exception
return crud.get_room_access_log_for_user(db, request.user_id, request.room_id)
@app.post("/admin/user/accesslog/room/username", tags=['Admin'])
def get_room_access_log_history_for_user_by_username(request : schemas.RoomAccessLogRequestUserUsername,
db : Session = Depends(get_db)):
user = crud.get_user_by_username(db, request.username)
if not user: raise user_not_found_exception
return crud.get_room_access_log_for_user(db, user.id, request.room_id)
@app.get("/users/acesslist/", response_model=List[schemas.Room], tags=['Users'])
def get_room_access_list_for_user(db: Session = Depends(get_db),
current_user: models.User = Depends(get_current_active_user)):
# Can use the ORM method current_user.authrized_rooms pulls from database on demand...
return current_user.authorized_rooms
@app.get("/admin/roominfo/now/", response_model=schemas.IotMonitor, tags=['Admin'])
def get_room_data(room : schemas.Room,
db: Session = Depends(get_db)):
monitor = crud.get_room_current_readings(db, room)
return monitor
@app.get("/admin/roominfo/now/all", response_model=List[schemas.IotMonitor], tags=['Admin'])
def get_all_rooms_data(db: Session = Depends(get_db)):
monitors = crud.get_monitors(db)
return monitors
@app.post("/users/open", tags=['Users'])
def issue_open_door_command(request: schemas.OpenRoomRequest,
def issue_open_door_command(command: schemas.OpenDoorRequestTime,
db: Session = Depends(get_db),
current_user: schemas.User = Depends(get_current_active_user)):
err = HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,
detail="Unauthrized to open")
device = crud.get_iot_entity_by_bluetooth_mac(db, command.bluetooth_mac)
if not device: raise err
# TODO: Use database search rather then this linear search
user = crud.get_user(db, current_user.id)
for dev in user.authorized_devices:
if dev.bluetooth_mac == device.bluetooth_mac:
crud.set_open_door_request(db, device.id, command.time_seconds)
log_entry = schemas.DoorAccessLog(user_id=current_user.id,
iot_id=device.id,
command="OPEN",
timestamp=datetime.now())
crud.record_door_access_log(db, log_entry)
#crud.record_user_connection(db, current_user, datetime.now())
#device = crud.open_door_request(db, request.room_id, request.time_seconds)
#if not device: raise unauth_to_open # To no leak info
user : models.User = crud.get_user(db, current_user.id)
log_entry = schemas.DoorAccessLog(user_id=current_user.id,
room_id=request.room_id,
timestamp=datetime.now())
res = crud.open_door_request(db, request=request)
if not res: raise unauth_to_open
crud.record_door_access_log(db, log_entry)
return device
raise err
@app.post("/users/close", tags=['Users'])
def issue_close_door_command(command: schemas.CloseDoorRequest,
db: Session = Depends(get_db),
current_user: schemas.User = Depends(get_current_active_user)):
err = HTTPException(status.HTTP_401_UNAUTHORIZED,
detail="Unaithrized to close")
device = crud.get_iot_entity_by_bluetooth_mac(db, command.bluetooth_mac)
if not device: raise err
user = crud.get_user(db, current_user.id)
for dev in user.authorized_devices:
if dev.bluetooth_mac == device.bluetooth_mac:
crud.set_close_door_request(db, device.id)
log_entry = schemas.DoorAccessLog(user_id=current_user.id,
iot_id=device.id,
command="CLOSE",
timestamp=datetime.now())
crud.record_door_access_log(db, log_entry)
#crud.record_user_connection(db, current_user, datetime.now())
return device
@app.get("/users/acesslist/", response_model=List[schemas.RoomOverview], tags=['Users'])
def get_iot_access_list_for_user(db: Session = Depends(get_db), current_user: schemas.User = Depends(get_current_active_user)):
user = crud.get_user_by_username(db, current_user.username)
access_list = list()
for device in user.authorized_devices:
door : models.IotEntity = device
monitor : models.Monitors = door.monitor
if not monitor: raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="No Room link")
entry : schemas.RoomOverview = schemas.RoomOverview(
id=door.id,
description=door.description,
bluetooth_mac=door.bluetooth_mac,
open_request=door.open_request,
time_seconds=door.time_seconds,
acces_list_counter=door.acces_list_counter,
humidity=monitor.humidity,
people=monitor.people,
temperature=monitor.temperature,
smoke_sensor_reading=monitor.smoke_sensor_reading,
force_close=door.force_close,
state=door.state
)
access_list.append(entry)
#crud.record_user_connection(db, user, datetime.now())
return access_list
@app.patch("/users/updatepassword", tags=['Users'])
def change_user_password(request: schemas.UserUpdatePassword,
current_user: models.User = Depends(get_current_active_user),
db: Session = Depends(get_db)):
crud.update_user_password(db, current_user, request)
return
@app.post("/users/tkn", response_model=schemas.Token, tags=['Users'])
@ -254,30 +181,273 @@ def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(), db:
access_token = auth_helper.create_access_token(
data={"sub": form_data.username}, expires_delta=timedelta(minutes=15)
)
crud.set_user_last_token(db, form_data.username, access_token)
#crud.record_user_connection(db, user, datetime.now())
return {"access_token": access_token, "token_type": "bearer"}
@app.get("/admin/users/", response_model=List[schemas.User], tags=['Admin'])
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
users = crud.get_users(db, skip=skip, limit=limit)
return users
@app.post("/iotdevice/door/status", response_model=schemas.IotDoor, tags=['Iot'])
def polling_method_for_door(request: schemas.IotDoorPollingRequest,
@app.get("/admin/iotentities/", response_model=List[schemas.IotEntity], tags=['Admin'])
def read_iot_entities(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
iot_entities = crud.get_iot_entities(db, skip=skip, limit=limit)
return iot_entities
@app.get("/admin/monitors/", response_model=List[schemas.Monitor], tags=['Admin'])
def read_iot_monitors(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
monitors = crud.get_monitors(db, skip=skip, limit=limit)
return monitors
# TODO: Can duplicate
@app.post("/admin/iotentities/create", response_model=schemas.IotEntity, tags=['Admin'])
def create_iot_entities(iot_entity: schemas.IotEntityCreate, db: Session = Depends(get_db)):
iot_entities = crud.create_iot_entity(db, iot_entity)
return iot_entities
@app.post("/admin/monitor/create", response_model=schemas.Monitor, tags=['Admin'])
def create_monitor(iot_entity: schemas.IotEntityBase,
db: Session = Depends(get_db)):
monitor = crud.create_monitor(db, iot_entity)
return monitor
@app.get("/admin/users/{user_id}", response_model=schemas.User, tags=['Admin'])
def read_user(user_id: int, db: Session = Depends(get_db)):
db_user = crud.get_user(db, user_id=user_id)
if db_user is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
detail="User not found")
return db_user
@app.patch("/admin/users/allowdevice/id", tags=['Admin'])
def allow_user_for_iot_entity_by_id(request: schemas.UserAllowForIotEntityRequestByID, db: Session = Depends(get_db)):
user = crud.get_user(db, request.user_id)
if not user:
raise HTTPException(status.HTTP_404_NOT_FOUND,
detail="User not found")
iot_entity = crud.get_iot_entity(db, request.iot_entity_id)
if not iot_entity:
raise HTTPException(status.HTTP_404_NOT_FOUND,
detail="Iot Entity not found")
res = crud.create_user_link_to_iot(db, request.user_id, request.iot_entity_id)
if not res:
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Could not complete operation")
crud.increment_door_access_list_counter(db, iot_entity)
return
@app.patch("/admin/users/disallowdevice/id", tags=['Admin'])
def disallow_user_for_iot_entity_by_id(request: schemas.UserAllowForIotEntityRequestByID, db: Session = Depends(get_db)):
user = crud.get_user(db, request.user_id)
if not user:
raise HTTPException(status.HTTP_404_NOT_FOUND,
detail="User not found")
iot_entity = crud.get_iot_entity(db, request.iot_entity_id)
if not iot_entity:
raise HTTPException(status.HTTP_404_NOT_FOUND,
detail="Iot Entity not found")
res = crud.remove_user_link_to_iot(db, request.user_id, request.iot_entity_id)
if not res:
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Could not complete operation")
crud.increment_door_access_list_counter(db, iot_entity)
return
@app.patch("/admin/users/allowdevice/name", tags=['Admin'])
def allow_user_for_iot_entity_by_name(request: schemas.UserAllowForIotEntityRequestByUsername, db: Session = Depends(get_db)):
user = crud.get_user_by_username(db, request.username)
if not user:
raise HTTPException(status.HTTP_404_NOT_FOUND,
detail="User not found")
iot_entity = crud.get_iot_entity_by_description(db, request.description)
if not iot_entity:
raise HTTPException(status.HTTP_404_NOT_FOUND,
detail="Iot Entity not found")
res = crud.create_user_link_to_iot(db, user.id, iot_entity.id)
if not res:
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Could not complete operation")
return
@app.patch("/admin/users/{user_id}/deactiveate", tags=['Admin'])
def deactiveate_user(user_id: int, db:Session = Depends(get_db)):
user = crud.get_user(db, user_id)
crud.update_user_status(db, user, False)
@app.patch("/admin/users/{user_id}/activeate", tags=['Admin'])
def deactiveate_user(user_id: int, db:Session = Depends(get_db)):
user = crud.get_user(db, user_id)
crud.update_user_status(db, user, True)
@app.post("/admin/iotdevice/gentoken/", response_model=schemas.Token, tags=['Admin'])
def generate_token_for_iot_device(bluetooth_mac : schemas.IotBluetoothMac):
# api_key: APIKey = Depends(auth_helper.valid_api_key)
# We get here after a valid admin key, so send back permenant token
data = {"bluetooth_mac": bluetooth_mac.bluetooth_mac}
tkn = auth_helper.create_iot_dev_token(data)
return {"access_token": tkn, "token_type": "bearer"}
@app.patch("/admin/link/monitor/{monitor_id}/door/{door_id}", tags=['Admin'])
def link_monitor_with_door(monitor_id: int, door_id: int,
db: Session = Depends(get_db)):
monitor = crud.get_monitor(db, monitor_id)
door = crud.get_iot_entity(db, door_id)
monitor.door = door
crud.update_monitor(db, monitor)
return monitor
@app.post("/admin/user/accesslog/email/", tags=['Admin'])
def get_access_log_history_for_user(request : schemas.UserAccessLogRequestEmail,
db : Session = Depends(get_db)):
user = crud.get_user_by_email(db, request.email)
if not user: raise HTTPException(status.HTTP_404_NOT_FOUND, detail="User not found")
return user.access_log
@app.post("/admin/user/accesslog/username/", tags=['Admin'])
def get_access_log_history_for_user(request : schemas.UserAccessLogRequestUsername,
db : Session = Depends(get_db)):
user = crud.get_user_by_username(db, request.username)
if not user: raise HTTPException(status.HTTP_404_NOT_FOUND, detail="User not found")
return user.access_log
@app.get("/admin/roominfo/{door_id}/now", tags=['Admin'])
def get_room_data(door_id: int, db: Session = Depends(get_db)):
door = crud.get_iot_entity(db, door_id)
if not door:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
detail="Door not found")
monitor : models.Monitors = door.monitor
if not monitor:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="No Room link")
data = monitor.sensor_history
if not data or len(data) == 0:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
detail="No Sensor data")
return data[-1]
@app.get("/admin/roominfo/{monitor_id}/now", tags=['Admin'])
def get_room_data(monitor_id: int, db: Session = Depends(get_db)):
monitor = crud.get_monitor(db, monitor_id)
if not monitor: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
detail="Monitor not found")
if not monitor.door_id:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Monitor not linked")
data = crud.get_room_data_now(db, monitor.door_id)
if data == -1: raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="No Room link")
if data == -2: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
detail="No Sensor data")
return data
@app.get("/admin/roominfo/{monitor_id}/last/{count}", tags=['Admin'])
def get_all_sensor_history(monitor_id: int, count: int,
db: Session = Depends(get_db)):
monitor = crud.get_monitor(db, monitor_id)
if not monitor: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
detail="Monitor not found")
data = monitor.sensor_history
if not data or len(data) == 0:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
detail="No Sensor data")
return data[-count:]
@app.post("/admin/roominfo/accesslog",response_model=List[schemas.DoorAccessLog], tags=['Admin'])
def get_access_log_for_door(request : schemas.AccessLogRequest,
db : Session = Depends(get_db)):
device: models.IotEntity = crud.get_iot_entity(db, request.iot_id)
if not device: raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Iot Entity not found")
return device.access_log
@app.post("/iotdevice/door/status", response_model=schemas.IotDoorPollingResponse, tags=['Iot'])
def polling_method_for_iot_entity(request: schemas.IotDoorPollingRequest,
db: Session = Depends(get_db)):
device: models.IotDoor = auth_helper.valid_iot_door_token(request.token, db)
device: models.IotEntity = auth_helper.valid_iot_token(request.token, db)
if not device:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials")
room : models.Room = crud.get_room_from_door(db, device)
response : schemas.IotDoorPollingResponse = schemas.IotDoorPollingResponse(
open_command=device.open_request,
acces_list_counter=device.acces_list_counter,
time_seconds=device.time_seconds,
force_close=device.force_close,
state=device.state)
# Reset open_request to False
#crud.clear_open_door_request(db, room_id=room.id) # Make response object to perserve values;
return device
crud.clear_open_door_request(db, device.id)
crud.clear_close_door_request(db, device.id)
crud.set_door_state(db, device, bool(request.state))
return response
@app.post("/iotdevice/monitor/status", tags=['Iot'])
def polling_method_for_room_monitor(request: schemas.IotMonitorRoomInfo,
def polling_method_for_room_monitor(request: schemas.MonitorUpdateReadings,
db: Session = Depends(get_db)):
device : schemas.IotMonitor = auth_helper.valid_iot_monitor_token(request.token, db)
device : models.Monitors = auth_helper.valid_monitor_token(request.token, db)
if not device:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials")
crud.record_room_sensor_data(db, request)
return request
crud.record_room_sensor_data(db, request, device)
if request.temperature >= EMERG_TEMP or request.smoke_sensor_reading >= EMERG_SMOKE:
print("********EMERGENCY AT %s********" % device.description)
door : models.IotEntity = device.door
print("********OPENING DOOR %s ID:%d********" % (door.description, door.id))
crud.set_open_door_request(db, door.id, EMERG_OPEN_TIME_SEC)
crud.record_emergancy_entry(db, request, device.id)
# Call into a hook to notify with room and people
print(request)
return request
@app.post("/iotdevice/door/users", response_class=PlainTextResponse, tags=['Iot'])
def get_allowed_usernames(request: schemas.IotDoorPollingRequest,
db: Session = Depends(get_db)):
iot_door : models.IotEntity = auth_helper.valid_iot_token(request.token, db)
if not iot_door:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials")
usernames = str()
for user in iot_door.authorized_users:
db_user : models.User = user
usernames = usernames + db_user.username + '\n'
return usernames
@app.post("/iotdevice/door/tkns", response_class=PlainTextResponse, tags=['Iot'])
def get_allowed_usernames(request: schemas.IotDoorPollingRequest,
db: Session = Depends(get_db)):
iot_door : models.IotEntity = auth_helper.valid_iot_token(request.token, db)
if not iot_door:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials")
tkns = str()
for user in iot_door.authorized_users:
db_user : models.User = user
tkns = tkns + db_user.last_token + '\n'
return tkns
@app.get("/test")
def get(db: Session = Depends(get_db)):
mon = crud.get_monitor(db, "ff:ff:ff:ff")
return mon.door

View File

@ -1,3 +1,6 @@
# March 2022
# Hesham T. Banafa <hishaminv@gmail.com>
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, DateTime
from sqlalchemy.orm import relationship
@ -13,122 +16,87 @@ class User(Base):
hashed_password = Column(String, nullable=False)
passwd_salt = Column(String, nullable=False)
is_active = Column(Boolean, default=True, nullable=False)
last_token = Column(String, nullable=True)
connections = relationship("UserConnectionHistory")
authorized_devices = relationship("IotEntity", secondary="user_iot_link", back_populates="authorized_users")
connections = relationship("UserConnectionHistory")
access_log = relationship("DoorAccessLog", back_populates="user")
authorized_rooms = relationship("Room", secondary= 'user_room_link', lazy='dynamic')
class Room(Base):
__tablename__ = 'rooms'
class IotEntity(Base):
__tablename__ = "iot_entities"
id = Column(Integer, primary_key=True, index=True)
#door_id = Column(Integer, ForeignKey('iot_doors.id'))
building_name = Column(String(512))
building_number = Column(Integer)
authorized_users = relationship("User", secondary='user_room_link')
class UserRoomAuth(Base):
__tablename__ = 'user_room_link'
user_id = Column(Integer, ForeignKey('user_accounts.id'), primary_key=True, index=True)
room_id = Column(Integer, ForeignKey('rooms.id'), primary_key=True, index=True)
class IotMonitor(Base):
__tablename__ = 'iot_monitors'
id = Column(Integer, primary_key=True, index=True)
room_id = Column(Integer, ForeignKey('rooms.id'))
temp_c = Column(Integer, default=0)
smoke = Column(Integer, default=0)
humidity = Column(Integer, default=0)
people = Column(Integer, default=0)
bluetooth_mac = Column(String(512), index=True, unique=True)
description = Column(String(512))
bluetooth_mac = Column(String(512))
class IotDoor(Base):
__tablename__ = 'iot_doors'
id = Column(Integer, primary_key=True, index=True)
room_id = Column(Integer, ForeignKey('rooms.id'))
open_request = Column(Boolean, default=False)
time_seconds = Column(Integer, default=10)
is_open = Column(Boolean, default=False)
acces_list_counter = Column(Integer, default=0)
force_close = Column(Boolean, default=False)
accesslist_counter = Column(Integer, default=0)
state = Column(Boolean, default=False) # True is open, False is closed
authorized_users = relationship("User", secondary="user_iot_link", back_populates="authorized_devices")
access_log = relationship("DoorAccessLog", back_populates="iot_device") # one-to-many
monitor = relationship("Monitors", back_populates="door", uselist=False) # one-to-one
class Monitors(Base):
__tablename__ = "monitors"
id = Column(Integer, primary_key=True)
bluetooth_mac = Column(String(512), index=True, unique=True)
description = Column(String(512))
bluetooth_mac = Column(String(512))
humidity = Column(Integer, default=0)
people = Column(Integer, default=0)
temperature = Column(Integer, default=0)
smoke_sensor_reading = Column(Integer, default=0)
door_id = Column(Integer, ForeignKey("iot_entities.id"))
door = relationship("IotEntity", back_populates="monitor")
sensor_history = relationship("RoomSensorData", back_populates="monitor")
class MonitorReadings(Base):
__tablename__ = 'monitor_readings'
class UserAuthToIoTDev(Base):
__tablename__ = "user_iot_link"
reading_id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("user_accounts.id"), primary_key=True)
iot_id = Column(Integer, ForeignKey("iot_entities.id"), primary_key=True)
timestamp = Column(DateTime)
temp_c = Column(Integer)
smoke = Column(Integer)
humidity = Column(Integer)
people = Column(Integer)
class DoorAccessLog(Base):
__tablename__ = 'door_access_log'
__tablename__ = "door_access_log"
entry_id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey('user_accounts.id'), index=True)
room = Column(Integer, ForeignKey('rooms.id'), index=True)
user = relationship("User", back_populates="access_log")
iot_id = Column(Integer, ForeignKey('iot_entities.id'), index=True)
iot_device = relationship("IotEntity", back_populates="access_log")
command = Column(String(16))
timestamp = Column(DateTime)
class UserConnections(Base):
__tablename__ = 'user_connection_history'
class RoomSensorData(Base):
__tablename__ = "room_sensor_data"
entry_id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey('user_accounts.id'))
reading_id = Column(Integer, primary_key=True, index=True)
humidity = Column(Integer)
people = Column(Integer)
temperature = Column(Integer)
smoke_sensor_reading = Column(Integer)
timestamp = Column(DateTime)
monitor_id = Column(Integer, ForeignKey("monitors.id"), index=True)
monitor = relationship("Monitors", back_populates="sensor_history")
class UserConnectionHistory(Base):
__tablename__ = "user_connection_history"
reading_id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey("user_accounts.id"), index=True)
timestamp = Column(DateTime)
# TODO: add ip
class EmergancyNotice(Base):
__tablename__ = "emergency_notice"
id = Column(Integer, primary_key=True)
monitor_id = Column(Integer, ForeignKey("monitors.id"), index=True)
people = Column(Integer)
temperature = Column(Integer)
smoke_sensor_reading = Column(Integer)
timestamp = Column(DateTime)
class IotDoorConnections(Base):
__tablename__ = 'door_connection_history'
entry_id = Column(Integer, primary_key=True, index=True)
door_id = Column(Integer, ForeignKey('iot_doors.id'))
timestamp = Column(DateTime)
class IotMonitorConnections(Base):
__tablename__ = 'monitor_connection_history'
entry_id = Column(Integer, primary_key=True, index=True)
monitor_id = Column(Integer, ForeignKey('iot_monitors.id'))
timestamp = Column(DateTime)
# class IotEntity(Base):
# __tablename__ = "iot_entities"
# id = Column(Integer, primary_key=True, index=True)
# bluetooth_mac = Column(String(512))
# description = Column(String(512))
# open_request = Column(Boolean, default=False)
# time_seconds = Column(Integer, default=10)
# authorized_users = relationship("User", secondary= 'user_iot_link')
# class UserAuthToIoTDev(Base):
# __tablename__ = "user_iot_link"
# user_id = Column(Integer, ForeignKey('user_accounts.id'), primary_key=True, index=True)
# iot_entity_id = Column(Integer, ForeignKey('iot_entities.id'), primary_key=True, index=True)
# class DoorAccessLog(Base):
# __tablename__ = "door_access_log"
# entry_id = Column(Integer, primary_key=True, index=True)
# user_id = Column(Integer, ForeignKey('user_accounts.id'))
# iot_dev_bluetooth_mac = Column(Integer, ForeignKey('iot_entities.id'))
# timestamp = Column(DateTime)
# class RoomSensorData(Base):
# __tablename__ = "room_sensor_data"
# # Data is now not related to a room. We should have a construct for rooms
# reading_id = Column(Integer, primary_key=True, index=True)
# humidity = Column(Integer)
# people = Column(Integer)
# temperature = Column(Integer)
# smoke_sensor_reading = Column(Integer)
# timestamp = Column(DateTime)
# TODO: Add table to store active sessions. May periodically clear.
# TODO: Add table to store active sessions. May periodically clear.

View File

@ -1,3 +1,6 @@
# March 2022
# Hesham T. Banafa <hishaminv@gmail.com>
from typing import Any, List, Optional
from pydantic import BaseModel
@ -5,8 +8,6 @@ from datetime import datetime
class IotEntityBase(BaseModel):
# Common attributes for all Iot devices
room_id: int # Used to link with sensors and other devices
bluetooth_mac: str
description: str
@ -14,180 +15,136 @@ class UserBase(BaseModel):
email: str
username: str
class IotEntityCreate(IotEntityBase):
pass
class UserCreate(UserBase):
# For when creating users (needs password once to store)
password: str
class IotDoor(IotEntityBase):
id: int # Door ID
open_request: bool # Standing flag to open
time_seconds: int # Used when open request
force_close: bool # Force a close mid timed open request
is_open: bool
accesslist_counter: int
class Config:
orm_mode = True
class IotDoorPollingRequest(BaseModel):
class IotEntity(IotEntityBase):
id: int
description: str
bluetooth_mac: str
token: str
class IotMonitor(IotEntityBase):
# Info of room monitor and current readings
id: int
room_id: int
people: int
temp_c: int
smoke: int
humidity: int
#authorized_users: List[User] = []
open_request: bool # Flag to open
time_seconds: int
force_close: bool
acces_list_counter: int
state: bool
class Config:
orm_mode = True
class IotMonitorRoomInfo(BaseModel):
humidity : int
people : int
temperature : int
smoke : int
token: str
class IotDoorCreate(IotEntityBase):
room_id: int
class IotMonitorCreate(IotEntityBase):
room_id: int
class Room(BaseModel):
id: int
building_name: str
building_number: int
class Config:
orm_mode = True
class RoomCreate(BaseModel):
building_name: str
building_number: int
class IotBluetoothMac(BaseModel):
bluetooth_mac : str
class Monitor(IotEntityBase):
# bluetooth_mac: str
# description: str
id: int
bluetooth_mac: str
description: str
humidity: int
people: int
temperature: int
smoke_sensor_reading: int
class Config:
orm_mode = True
class MonitorUpdateReadings(BaseModel):
humidity : int
people : int
temperature : int
smoke_sensor_reading : int
token: str # Contains mac
class User(UserBase):
id: int
is_active: bool
authorized_rooms: List[Room] = [] # TODO: Change to auth rooms not devs
authorized_devices: List[IotEntity] = []
class Config:
orm_mode = True
class Token(BaseModel):
access_token: str
token_type: str
access_token : str
token_type : str
class UserTokenData(BaseModel):
class TokenData(BaseModel):
username : str
# Token can conatin information. But we are already recording this in a database
# for scalability.
class UserAllowForIotEntityRequestByID(BaseModel):
user_id: int
iot_entity_id: int
class UserAllowForIotEntityRequestByUsername(BaseModel):
username: str
description: str
class IotDeviceTokenData(BaseModel):
class UserUpdatePassword(BaseModel):
password: str
class OpenDoorRequestBase(BaseModel):
username: str
bluetooth_mac: str
class DoorAccessLog(BaseModel):
user_id: int
room_id: int
timestamp: datetime
#token: str
class OpenDoorRequestTime(OpenDoorRequestBase):
time_seconds: int
class CloseDoorRequest(OpenDoorRequestBase):
pass
# Device sends this periodcally
class IotDoorPollingRequest(BaseModel):
bluetooth_mac : str
state: int
token : str
class IotMonitorRoomEntry(BaseModel):
class IotDoorPollingResponse(BaseModel):
open_command : bool
acces_list_counter : int
time_seconds : int
force_close: bool
state: bool
class IotMonitorRoomInfo(BaseModel):
humidity : int
people : int
temperature : int
smoke: int
smoke_sensor_reading : int
token: str
# class Config:
# orm_mode = True
class IotMonitorRoomInfoTimestamped(IotMonitorRoomInfo):
time: datetime
class Config:
orm_mode = True
class AllowRoomAccessRequestID(BaseModel):
class DoorAccessLog(BaseModel):
user_id: int
room_id: int
iot_id: str
command: str
timestamp: datetime
class Config:
orm_mode = True
class AllowRoomAccessRequestName(BaseModel):
username: str
description: int
class AccessLogRequest(BaseModel):
iot_id : int
class UserRoomAccessLogRequest(BaseModel):
username: str
class UserAccessLogRequestUsername(BaseModel):
username : str
class RoomAccessLogRequest(BaseModel):
room_id: int
class UserAccessLogRequestEmail(BaseModel):
email : str
class RoomAccessLogRequestUserID(BaseModel):
room_id: int
user_id: int
class UserAccessLogRequestID(BaseModel):
id : int
class RoomAccessLogRequestUserUsername(BaseModel):
username: str
room_id: int
class OpenRoomRequest(BaseModel):
user_id: int
room_id: int
time_seconds: int
# class UserAllowForIotEntityRequestByID(BaseModel):
# user_id: int
# iot_entity_id: int
# class UserAllowForIotEntityRequestByUsername(BaseModel):
# username: str
# description: str
# class OpenDoorRequestBase(BaseModel):
# username: str
# bluetooth_mac: str
# class OpenDoorRequestTime(OpenDoorRequestBase):
# time_seconds: int
# # Device sends this periodcally
# class IotDoorPollingRequest(BaseModel):
# bluetooth_mac : str
# token : str
# class Config:
# orm_mode = True
# class IotDoorPollingResponse(BaseModel):
# open_command : bool
# acces_list_counter : int
# time_seconds : int
# class IotMonitorRoomInfo(BaseModel):
# humidity : int
# people : int
# temperature : int
# smoke_sensor_reading : int
# token: str
# class Config:
# orm_mode = True
# class IotMonitorRoomInfoTimestamped(IotMonitorRoomInfo):
# time: datetime
# class Config:
# orm_mode = True
# class DoorAccessLog(BaseModel):
# user_id: int
# door_bluetooth_mac: str
# time: datetime
# class Config:
# orm_mode = True
# class AccessLogRequest(BaseModel):
# bluetooth_mac : str
# class UserAccessLogRequestUsername(BaseModel):
# username : str
# class UserAccessLogRequestEmail(BaseModel):
# email : str
# class UserAccessLogRequestID(BaseModel):
# id : int
class RoomOverview(IotEntity):
humidity : int
people : int
temperature : int
smoke_sensor_reading : int

View File

4
sql_app/tests/run-tests Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
source ../../venv/bin/activate
pytest

View File

@ -0,0 +1,92 @@
from sys import path
import random
import string
from fastapi.testclient import TestClient
from requests import Response
from ..main import app
from ..schemas import UserCreate
MAX_ASCII = 255
RAND_CHAR_SET = string.ascii_letters
client = TestClient(app)
def gen_rand_str(size: int) -> str:
return ''.join(random.choice(RAND_CHAR_SET) for x in range(size))
def gen_new_user_dict() -> UserCreate:
noune = gen_rand_str(16)
new_user = UserCreate(email=f"testuser{noune}@mail.none",
username=f"testuser{noune}",
password=noune)
return new_user
def get_user_json(user: UserCreate) -> dict:
if type(user) != UserCreate: assert False
new_user_json = {
'email' : user.email,
'username': user.username,
'password': user.password
}
return new_user_json
def post_request(response: Response):
print(response.text)
print(response.reason)
test_user : UserCreate = gen_new_user_dict()
common_headres = {
'accept': 'application/json',
'Content-Type': 'application/json'
}
def test_create_user():
response = client.request("POST", "/users/reg",
json=get_user_json(test_user),
headers=common_headres)
assert response.status_code == 200
post_request(response)
def test_create_user_duplicate_fields():
# Assumed that this test runs after test_create_user()
response = client.request("POST", "/users/reg",
json=get_user_json(test_user),
headers=common_headres)
assert response.status_code == 400
post_request(response)
def test_obtain_user_token():
headers = {
'accept': 'application/json',
'Content-type': 'application/x-www-form-urlencoded'
}
data = f"grant_type=&username={test_user.username}&password={test_user.password}&scope=&client_id=&client_secret="
response = client.request("POST", "/users/tkn", headers=headers, data=data)
# if response.status_code == 200 and 'application/json' in response.headers.get('Content-Type',''):
# print(response.json())
assert response.status_code == 200
post_request(response)
def test_reject_false_creds():
headers = {
'accept': 'application/json',
'Content-type': 'application/x-www-form-urlencoded'
}
data = f"grant_type=&username={test_user.username}flaty&password=badpass{test_user.password}&scope=&client_id=&client_secret="
response = client.request("POST", "/users/tkn", headers=headers, data=data)
assert response.status_code == 401
post_request(response)
def test_create_iot_entity():
pass

View File

@ -0,0 +1,40 @@
# EE495
# Hesham T. Banafa
# Jun 11th, 2022
from time import sleep
import requests
def poll(poll_url: str, data: dict, headers: dict) -> dict:
res : requests.Response = \
requests.post(poll_url, json=data, headers=headers)
#print('sent ', data)
print(res.text, res, res.reason)
if res.status_code != 200: return None
return res.json()
def emulate(poll_url, token_in: str):
mac = "94:b9:7e:fb:57:1a"
polling_interval_secons = 1
polling_headers = {
'accept' : 'application/json',
'Content-Type': 'application/json'
}
stop = False
state = False
while (not stop):
sleep(polling_interval_secons)
data = {
'bluetooth_mac': mac,
'state': state,
'token': token_in
}
data_dict = poll(poll_url, data, polling_headers)
if not data_dict: continue
if data_dict['open_command']: state = True
if __name__ == '__main__':
emulate("https://ibs.cronos.typedef.cf:4040/iotdevice/door/status",
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJibHVldG9vdGhfbWFjIjoiOTQ6Yjk6N2U6ZmI6NTc6MWEifQ.oRbL0U70g8HGkKIOnwkesDiB40VWTPmwIWiysvP-hXA")

31
sql_app/tools/ibs.service Normal file
View File

@ -0,0 +1,31 @@
# This is templte to use for python venv applications
# from https://broplanner.com/wp-content/webpc-passthru.php?src=https://broplanner.com/wp-content/uploads/2022/01/Screenshot-2022-01-25-224223-1536x237.png&nocache=1
[Unit]
After = network.target
[Service]
User=ibs
Group=ibs
WorkingDirectory=/srv/ibs/ibs
ExecStart=/srv/ibs/ibs/run-tls
Restart=on-failure
RestartSec=15
# Security
ReadWritePaths=/srv/ibs/ibs
PrivateDevices=yes
PrivateMounts=yes
PrivateTmp=yes
PrivateUsers=yes
ProtectClock=yes
ProtectControlGroups=yes
ProtectHome=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectProc=yes
ProtectHostname=yes
[Install]
WantedBy=multi-user.target

16
sql_app/utils.py Normal file
View File

@ -0,0 +1,16 @@
# May 2022
# Hesham T. Banafa <hishaminv@gmail.com>
from .database import SessionLocal
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
EMERG_TEMP = 50
EMERG_SMOKE = 1000
EMERG_OPEN_TIME_SEC = 500