diff --git a/api/v1/routes/auth.py b/api/v1/routes/auth.py index 3a7317fd7..0d19c455c 100644 --- a/api/v1/routes/auth.py +++ b/api/v1/routes/auth.py @@ -85,6 +85,12 @@ def register_as_super_admin(user: UserCreate, db: Session = Depends(get_db)): """Endpoint for super admin creation""" user = user_service.create_admin(db=db, schema=user) + # create an organization for the user + org = CreateUpdateOrganisation( + name=f"{user.email}'s Organisation", + email=user.email + ) + organisation_service.create(db=db, schema=org, user=user) user_organizations = organisation_service.retrieve_user_organizations(user, db) # Create access and refresh tokens diff --git a/api/v1/schemas/user.py b/api/v1/schemas/user.py index 696aa993c..3201d416a 100644 --- a/api/v1/schemas/user.py +++ b/api/v1/schemas/user.py @@ -128,6 +128,26 @@ class AdminCreateUserResponse(BaseModel): class LoginRequest(BaseModel): email: EmailStr password: str + + @model_validator(mode='before') + @classmethod + def validate_password(cls, values: dict): + """ + Validates passwords + """ + password = values.get('password') + + # constraints for password + if not any(c.islower() for c in password): + raise ValueError("password must include at least one lowercase character") + if not any(c.isupper() for c in password): + raise ValueError("password must include at least one uppercase character") + if not any(c.isdigit() for c in password): + raise ValueError("password must include at least one digit") + if not any(c in ['!','@','#','$','%','&','*','?','_','-'] for c in password): + raise ValueError("password must include at least one special character") + + return values class EmailRequest(BaseModel): diff --git a/api/v1/services/google_oauth.py b/api/v1/services/google_oauth.py index ba5106c95..bdf7da1f7 100644 --- a/api/v1/services/google_oauth.py +++ b/api/v1/services/google_oauth.py @@ -3,9 +3,8 @@ from api.core.dependencies.email_sender import send_email from api.db.database import get_db from api.v1.models.organisation import Organisation -from api.v1.models.user import User from api.v1.models.oauth import OAuth -from api.v1.models.user import User +from api.v1.models import User, DataPrivacySetting, Region, NewsletterSubscriber from api.v1.models.profile import Profile from api.core.base.services import Service from sqlalchemy.orm import Session @@ -14,6 +13,12 @@ from api.v1.schemas.google_oauth import Tokens from api.v1.services.profile import profile_service from api.v1.models.associations import user_organisation_association +from api.v1.models.permissions.role_permissions import role_permissions +from api.v1.models.permissions.permissions import Permission +from api.v1.models.permissions.role import Role +from api.v1.models.permissions.user_org_role import user_organisation_roles +from api.v1.services.notification_settings import notification_setting_service +from api.v1.services.newsletter import NewsletterService, EmailSchema class GoogleOauthServices(Service): @@ -199,27 +204,40 @@ def create_new_user( db.commit() db.refresh(new_user) - profile = Profile(user_id=new_user.id, avatar_url=google_response.get("picture")) + profile = Profile(user_id=new_user.id, + avatar_url=google_response.get("picture")) oauth_data = OAuth( provider="google", user_id=new_user.id, - sub=google_response.get("sub"), - access_token=user_service.create_access_token(new_user.id), - refresh_token=user_service.create_refresh_token(new_user.id) + sub=google_response.get("sub") ) organisation = Organisation( name = f'{new_user.email} {new_user.last_name} Organisation' ) - db.add_all([profile, oauth_data, organisation]) - db.commit() + + region = Region( + user_id=new_user.id, + region='Empty' + ) + # Create notification settings directly for the user + notification_setting_service.create(db=db, user=new_user) + + # create data privacy setting + data_privacy = DataPrivacySetting(user_id=new_user.id) + + db.add_all([profile, oauth_data, organisation, region, data_privacy]) + news_letter = db.query(NewsletterSubscriber).filter_by(email=new_user.email) + if not news_letter: + news_letter = NewsletterService.create(db, EmailSchema(email=new_user.email)) + # TODO: Ensure to update this later stmt = user_organisation_association.insert().values( user_id=new_user.id, organisation_id=organisation.id, role="owner" ) db.execute(stmt) - db.commit() + db.commit() return new_user except Exception as e: raise HTTPException(status_code=500, detail=f'Error {e}') diff --git a/api/v1/services/organisation.py b/api/v1/services/organisation.py index a050502d8..74a63c070 100644 --- a/api/v1/services/organisation.py +++ b/api/v1/services/organisation.py @@ -4,7 +4,7 @@ from typing import Any, Optional, Annotated from fastapi import HTTPException, Depends, status from sqlalchemy.orm import Session -from sqlalchemy import or_ +from sqlalchemy import or_, and_ from fastapi import HTTPException, status from sqlalchemy import select from api.core.base.services import Service @@ -348,8 +348,11 @@ def retrieve_user_organizations(self, user: User, Role.id == user_organisation_roles.c.role_id, ).filter( or_( - user_organisation_roles.c.user_id == user.id, - Role.is_builtin == True, + and_( + user_organisation_roles.c.user_id == user.id, + Role.is_builtin == False + ), + Role.is_builtin == True ) ).all() diff --git a/api/v1/services/user.py b/api/v1/services/user.py index e9412f012..d9fd15787 100644 --- a/api/v1/services/user.py +++ b/api/v1/services/user.py @@ -17,12 +17,13 @@ from api.utils.settings import settings from api.utils.db_validators import check_model_existence from api.v1.models.associations import user_organisation_association -from api.v1.models import User, Profile, Region +from api.v1.models import User, Profile, Region, NewsletterSubscriber from api.v1.models.data_privacy import DataPrivacySetting from api.v1.models.token_login import TokenLogin from api.v1.schemas import user from api.v1.schemas import token from api.v1.services.notification_settings import notification_setting_service +from api.v1.services.newsletter import NewsletterService, EmailSchema oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login") pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") @@ -160,6 +161,10 @@ def create(self, db: Session, schema: user.UserCreate): user_id=user.id, region='Empty' ) + + news_letter = db.query(NewsletterSubscriber).filter_by(email=user.email) + if not news_letter: + news_letter = NewsletterService.create(db, EmailSchema(email=user.email)) db.add_all([data_privacy, profile, region]) db.commit() @@ -195,6 +200,23 @@ def super_admin_create_user( db.add(new_user) db.commit() db.refresh(new_user) + + # Create notification settings directly for the user + notification_setting_service.create(db=db, user=new_user) + + # create data privacy setting + data_privacy = DataPrivacySetting(user_id=new_user.id) + profile = Profile( + user_id=new_user.id + ) + region = Region( + user_id=new_user.id, + region='Empty' + ) + + db.add_all([data_privacy, profile, region]) + db.commit() + user_schema = user.UserData.model_validate(new_user, from_attributes=True) return user.AdminCreateUserResponse( message="User created successfully", @@ -220,14 +242,29 @@ def create_admin(self, db: Session, schema: user.UserCreate): # Create user object with hashed password and other attributes from schema user = User(**schema.model_dump()) + + user.is_superadmin = True db.add(user) db.commit() - db.refresh(user) - # Set user to super admin - user.is_superadmin = True + # # Create notification settings directly for the user + notification_setting_service.create(db=db, user=user) + + # create data privacy setting + data_privacy = DataPrivacySetting(user_id=user.id) + profile = Profile( + user_id=user.id + ) + region = Region( + user_id=user.id, + region='Empty' + ) + + db.add_all([data_privacy, profile, region]) db.commit() + db.refresh(user) + return user def update(self, db: Session, current_user: User, schema: user.UserUpdate, id=None):