diff --git a/sql_app/auth_helper.py b/sql_app/auth_helper.py index 5f41f4a..d1c531d 100644 --- a/sql_app/auth_helper.py +++ b/sql_app/auth_helper.py @@ -47,12 +47,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_token(token : str, db: Session): +def valid_iot_door_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_iot_entity_by_bluetooth_mac(db, mac_signed) + device = crud.get_door_by_bluetooth_mac(db, mac_signed) + return device + +def valid_iot_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 \ No newline at end of file diff --git a/sql_app/crud.py b/sql_app/crud.py index 8b5f22f..6cb7dc9 100644 --- a/sql_app/crud.py +++ b/sql_app/crud.py @@ -7,22 +7,23 @@ from . import models, schemas, crypto, auth_helper from datetime import datetime # 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): return db.query(models.User).get(user_id) -def get_iot_entity(db: Session, id: int): - return db.query(models.IotEntity).get(id) +def get_door_by_id(door_id: int): + return db.query(models.IotDoor).get(door_id) -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_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_bluetooth_mac(db: Session, bluetooth_mac: str): - return db.query(models.IotEntity).filter(models.IotEntity.bluetooth_mac == bluetooth_mac).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_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): return db.query(models.User).filter(models.User.email == email).first() @@ -33,14 +34,56 @@ def get_user_by_username(db: Session, username: str): 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_door_by_door_mac(db: Session, bluetooth_mac : str): - return db.query(models.DoorAccessLog).filter(models.DoorAccessLog.iot_dev_bluetooth_mac == bluetooth_mac).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_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): - return db.query(models.RoomSensorData)[-1] +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 create_user(db: Session, user: schemas.UserCreate): key = crypto.gen_new_key(user.password) @@ -52,62 +95,77 @@ def create_user(db: Session, user: schemas.UserCreate): db.refresh(db_user) return db_user -def get_iot_entities(db: Session, skip: int = 0, limit: int = 100): - return db.query(models.IotEntity).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) +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) db.add(db_item) db.commit() db.refresh(db_item) return db_item -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_entity_id == iot_dev_id).first() - if link: return True - new_link = models.UserAuthToIoTDev(user_id=user_id, iot_entity_id=iot_dev_id) +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_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) db.add(new_link) db.commit() db.refresh(new_link) - return True + return link -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_entity_id == iot_dev_id).first() +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) if not link: return True db.delete(link) db.flush() db.commit() - #db.refresh(link) return 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) +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) if time_seconds < 1: time_seconds = 10 # Magic number move to global constant - setattr(device, "time_seconds", time_seconds) - db.add(device) + setattr(door, "time_seconds", time_seconds) + db.add(door) db.commit() - db.refresh(device) + db.refresh(door) return True -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) +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) db.commit() - db.refresh(device) + db.refresh(door) return True def record_door_access_log(db: Session, entry: schemas.DoorAccessLog): db_item = models.DoorAccessLog(user_id=entry.user_id, - iot_dev_bluetooth_mac=entry.door_bluetooth_mac, - timestamp=entry.time) + room=entry.room_id, + timestamp=entry.timestamp) db.add(db_item) db.commit() db.refresh(db_item) @@ -116,8 +174,19 @@ def record_room_sensor_data(db: Session, entry: schemas.IotMonitorRoomInfo): db_item = models.RoomSensorData(humidity=entry.humidity, people=entry.people, temperature=entry.temperature, - smoke_sensor_reading=entry.smoke_sensor_reading, + smoke=entry.smoke, timestamp=datetime.now()) db.add(db_item) db.commit() - db.refresh(db_item) \ No newline at end of file + 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) + +# TODO: Get room sensor data +# TODO: Get room door status \ No newline at end of file diff --git a/sql_app/database.py b/sql_app/database.py index c3c1892..f407a23 100644 --- a/sql_app/database.py +++ b/sql_app/database.py @@ -2,7 +2,7 @@ from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker -SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db" +SQLALCHEMY_DATABASE_URL = "sqlite:///./data.db" # SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db" # For sqlite diff --git a/sql_app/main.py b/sql_app/main.py index ebdc86c..5cd50f0 100644 --- a/sql_app/main.py +++ b/sql_app/main.py @@ -15,6 +15,30 @@ 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"}) + +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() @@ -24,51 +48,33 @@ def get_db(): 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=token_data.username) + user = crud.get_user_by_username(db, username=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 HTTPException(status_code=400, detail="Inactive user") + raise inactive_user_exception 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 HTTPException(status_code=400, detail="Email already registered") + raise email_used_exception db_user = crud.get_user_by_username(db, username=user.username) if db_user: - raise HTTPException(status_code=400, detail="Username already registerd") + raise username_used_exception return crud.create_user(db=db, user=user) @@ -81,77 +87,85 @@ 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/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/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 -# 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.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 HTTPException(status_code=404, detail="User not found") + raise user_not_found_exception return db_user -@app.post("/admin/users/allowdevice/id", tags=['Admin']) -def allow_user_for_iot_entity_by_id(request: schemas.UserAllowForIotEntityRequestByID, db: Session = Depends(get_db)): +@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 HTTPException(status_code=404, detail="User not found") + raise user_not_found_exception - iot_entity = crud.get_iot_entity(db, request.iot_entity_id) - if not iot_entity: - raise HTTPException(status_code=404, detail="Iot Entity not found") + 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.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 + return request -@app.post("/admin/users/disallowdevice/id", tags=['Admin']) -def disallow_user_for_iot_entity_by_id(request: schemas.UserAllowForIotEntityRequestByID, db: Session = Depends(get_db)): +@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 HTTPException(status_code=404, detail="User not found") + raise user_not_found_exception - iot_entity = crud.get_iot_entity(db, request.iot_entity_id) - if not iot_entity: - raise HTTPException(status_code=404, detail="Iot Entity not found") + room = crud.get_room(db, request.room_id) + if not room: + raise room_not_found_exception - res = crud.remove_user_link_to_iot(db, request.user_id, request.iot_entity_id) + 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/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_code=404, detail="User not found") - - iot_entity = crud.get_iot_entity_by_description(db, request.description) - if not iot_entity: - raise HTTPException(status_code=404, detail="Iot Entity not found") - - res = crud.create_user_link_to_iot(db, user.id, iot_entity.id) - if not res: - raise HTTPException(status_code=500, detail="Could not complete operation") - - return - -@app.post("/admin/users/{user_id}/deactiveate", tags=['Admin']) +@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']) +@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 @@ -163,55 +177,68 @@ def generate_token_for_iot_device(bluetooth_mac : schemas.IotBluetoothMac, tkn = auth_helper.create_iot_dev_token(data) return {"access_token": tkn, "token_type": "bearer"} -@app.post("/admin/iotdevice/accesslog/", tags=['Admin']) -def get_access_log_for_door(request : schemas.AccessLogRequest, +@app.post("/admin/room/accesslog/", tags=['Admin']) +def get_access_log_for_door(request : schemas.RoomAccessLogRequest, db : Session = Depends(get_db)): - device = crud.get_iot_entity_by_bluetooth_mac(db, request.bluetooth_mac) - if not device: raise HTTPException(status_code=404, detail="Iot Entity not found") - return crud.get_access_log_for_door_by_door_mac(db, request.bluetooth_mac) + 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/user/accesslog/email/", tags=['Admin']) -def get_access_log_history_for_user(request : schemas.UserAccessLogRequestEmail, +@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_by_email(db, request.email) - if not user: raise HTTPException(status_code=404, detail="User not found") - return crud.get_access_log_for_user_by_id(db, user.id) + 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/username/", tags=['Admin']) -def get_access_log_history_for_user(request : schemas.UserAccessLogRequestUsername, +@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 HTTPException(status_code=404, detail="User not found") - return crud.get_access_log_for_user_by_id(db, user.id) + 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.IotEntity], 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) - return user.authorized_devices +@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)): -@app.get("/admin/roominfo/now/", tags=['Admin']) -def get_room_data(db: Session = Depends(get_db)): - return crud.get_room_data_now(db) + # 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(command: schemas.OpenDoorRequestTime, +def issue_open_door_command(request: schemas.OpenRoomRequest, 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, - door_bluetooth_mac=command.bluetooth_mac, - time=datetime.now()) - crud.record_door_access_log(db, log_entry) - return device - raise err + + #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 @app.post("/users/tkn", response_model=schemas.Token, tags=['Users']) @app.post("/tkn", response_model=schemas.Token, tags=['Users']) @@ -230,28 +257,24 @@ def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(), db: return {"access_token": access_token, "token_type": "bearer"} -@app.post("/iotdevice/door/status", response_model=schemas.IotDoorPollingResponse, tags=['Iot']) -def polling_method_for_iot_entity(request: schemas.IotDoorPollingRequest, +@app.post("/iotdevice/door/status", response_model=schemas.IotDoor, tags=['Iot']) +def polling_method_for_door(request: schemas.IotDoorPollingRequest, db: Session = Depends(get_db)): - device: schemas.IotEntity = auth_helper.valid_iot_token(request.token, db) + device: models.IotDoor = auth_helper.valid_iot_door_token(request.token, db) if not device: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials") - - response : schemas.IotDoorPollingResponse = schemas.IotDoorPollingResponse( - open_command=device.open_request, - acces_list_counter=0, - time_seconds=device.time_seconds) + room : models.Room = crud.get_room_from_door(db, device) # Reset open_request to False - crud.clear_open_door_request(db, device.id) - return response + #crud.clear_open_door_request(db, room_id=room.id) # Make response object to perserve values; + return device @app.post("/iotdevice/monitor/status", tags=['Iot']) def polling_method_for_room_monitor(request: schemas.IotMonitorRoomInfo, db: Session = Depends(get_db)): - device : schemas.IotEntity = auth_helper.valid_iot_token(request.token, db) + device : schemas.IotMonitor = auth_helper.valid_iot_monitor_token(request.token, db) if not device: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, diff --git a/sql_app/models.py b/sql_app/models.py index 0a327d0..bea5b34 100644 --- a/sql_app/models.py +++ b/sql_app/models.py @@ -14,42 +14,121 @@ class User(Base): passwd_salt = Column(String, nullable=False) is_active = Column(Boolean, default=True, nullable=False) - authorized_devices = relationship("IotEntity", secondary= 'user_iot_link') + authorized_rooms = relationship("Room", secondary= 'user_room_link', lazy='dynamic') - -class IotEntity(Base): - __tablename__ = "iot_entities" +class Room(Base): + __tablename__ = 'rooms' 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') + #door_id = Column(Integer, ForeignKey('iot_doors.id')) + building_name = Column(String(512)) + building_number = Column(Integer) -class UserAuthToIoTDev(Base): - __tablename__ = "user_iot_link" + 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) - iot_entity_id = Column(Integer, ForeignKey('iot_entities.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) + 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) + force_close = Column(Boolean, default=False) + accesslist_counter = Column(Integer, default=0) + description = Column(String(512)) + bluetooth_mac = Column(String(512)) + +class MonitorReadings(Base): + __tablename__ = 'monitor_readings' + + reading_id = Column(Integer, primary_key=True, index=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) + timestamp = Column(DateTime) + +class UserConnections(Base): + __tablename__ = 'user_connection_history' 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" +class IotDoorConnections(Base): + __tablename__ = 'door_connection_history' - # 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) + 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. \ No newline at end of file diff --git a/sql_app/schemas.py b/sql_app/schemas.py index 79aa70b..0aa6eb8 100644 --- a/sql_app/schemas.py +++ b/sql_app/schemas.py @@ -5,6 +5,8 @@ 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 @@ -12,100 +14,180 @@ 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 IotEntity(IotEntityBase): - id: int - description: str - bluetooth_mac: str - #authorized_users: List[User] = [] - open_request: bool # Flag to open - time_seconds: int +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): + 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 + 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 User(UserBase): id: int is_active: bool - authorized_devices: List[IotEntity] = [] - + authorized_rooms: List[Room] = [] # TODO: Change to auth rooms not devs class Config: orm_mode = True class Token(BaseModel): - access_token : str - token_type : str + access_token: str + token_type: str -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): +class UserTokenData(BaseModel): username: str - description: str -class OpenDoorRequestBase(BaseModel): - username: str +class IotDeviceTokenData(BaseModel): bluetooth_mac: str -class OpenDoorRequestTime(OpenDoorRequestBase): - time_seconds: int +class DoorAccessLog(BaseModel): + user_id: int + room_id: int + timestamp: datetime + #token: str -# 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): +class IotMonitorRoomEntry(BaseModel): humidity : int people : int temperature : int - smoke_sensor_reading : int + smoke: int token: str class Config: orm_mode = True -class IotMonitorRoomInfoTimestamped(IotMonitorRoomInfo): - time: datetime - class Config: - orm_mode = True - -class DoorAccessLog(BaseModel): +class AllowRoomAccessRequestID(BaseModel): user_id: int - door_bluetooth_mac: str - time: datetime - class Config: - orm_mode = True + room_id: int -class AccessLogRequest(BaseModel): - bluetooth_mac : str +class AllowRoomAccessRequestName(BaseModel): + username: str + description: int -class UserAccessLogRequestUsername(BaseModel): - username : str +class UserRoomAccessLogRequest(BaseModel): + username: str -class UserAccessLogRequestEmail(BaseModel): - email : str +class RoomAccessLogRequest(BaseModel): + room_id: int -class UserAccessLogRequestID(BaseModel): - id : int +class RoomAccessLogRequestUserID(BaseModel): + room_id: int + user_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