From a12327cc5407546a9158041e6a3d78223bf100e6 Mon Sep 17 00:00:00 2001 From: Eugene Michkov Date: Sat, 10 Aug 2024 22:14:44 +0300 Subject: [PATCH] feature: change password service --- .github/workflows/release.yml | 88 ++++++++++++++-------------- src/enums/errors/external/auth.rs | 8 --- src/enums/errors/internal/auth.rs | 5 +- src/services/user/change_password.rs | 50 ++++++++++++++-- 4 files changed, 88 insertions(+), 63 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index feb5d35..0f29e2b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,53 +42,51 @@ jobs: uses: softprops/action-gh-release@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - deploy: - runs-on: ubuntu-latest - needs: build - steps: - - uses: actions/checkout@v4 - - name: Add SSH key - run: | - mkdir -p ~/.ssh - echo "${{ secrets.HELIOS_SSH }}" > ~/.ssh/helios_ssh - chmod 600 ~/.ssh/helios_ssh + # deploy: + # runs-on: ubuntu-latest + # needs: build + # steps: + # - uses: actions/checkout@v4 - - name: Form environment variables - run: | - echo "GHCR_PAT=\"${{ secrets.GHCR_PAT }}\"" >> .env - echo "MASTER_BACKEND_PORT=\"${{ secrets.MASTER_BACKEND_PORT }}\"" >> .env - echo "MASTER_BACKEND_URL=\"${{ secrets.MASTER_BACKEND_URL }}\"" >> .env - echo "MASTER_METRICS_PORT=\"${{ secrets.MASTER_METRICS_PORT }}\"" >> .env - echo "MASTER_METRICS_URL=\"${{ secrets.MASTER_METRICS_URL }}\"" >> .env - echo "DATABASE_PORT=\"${{ secrets.DATABASE_PORT }}\"" >> .env - echo "DATABASE_NAME=\"${{ secrets.DATABASE_NAME }}\"" >> .env - echo "DATABASE_USER=\"${{ secrets.DATABASE_USER }}\"" >> .env - echo "DATABASE_PASSWORD=\"${{ secrets.DATABASE_PASSWORD }}\"" >> .env - echo "DATABASE_HOST=\"${{ secrets.DATABASE_HOST }}\"" >> .env - echo "DATABASE_URL=\"${{ secrets.DATABASE_URL }}\"" >> .env - echo "JWT_ACCESS_SECRET=\"${{ secrets.JWT_ACCESS_SECRET }}\"" >> .env - echo "JWT_REFRESH_SECRET=\"${{ secrets.JWT_REFRESH_SECRET }}\"" >> .env - echo "RUST_ENV=\"${{ secrets.RUST_ENV }}\"" >> .env - echo "HELIOS_DOMAIN=\"${{ secrets.HELIOS_DOMAIN }}\"" >> .env - echo "DO_AUTH_TOKEN=\"${{ secrets.DO_AUTH_TOKEN }}\"" >> .env - echo "TRAEFIK_AUTH=\"${{ secrets.TRAEFIK_AUTH }}\"" >> .env - echo "GRAFANA_USER=\"${{ secrets.GRAFANA_USER }}\"" >> .env - echo "GRAFANA_PASSWORD=\"${{ secrets.GRAFANA_PASSWORD }}\"" >> .env + # - name: Add SSH key + # run: | + # mkdir -p ~/.ssh + # echo "${{ secrets.HELIOS_SSH }}" > ~/.ssh/helios_ssh + # chmod 600 ~/.ssh/helios_ssh - - name: Setup to DigitalOcean - run: | - ssh -o StrictHostKeyChecking=no -i ~/.ssh/helios_ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "echo ${{ secrets.GHCR_PAT }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin" - ssh -o StrictHostKeyChecking=no -i ~/.ssh/helios_ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "rm -rf docker-compose.yml .env compose/" - scp -o StrictHostKeyChecking=no -i ~/.ssh/helios_ssh -P ${{ secrets.SSH_PORT }} docker-compose.yml ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:~/docker-compose.yml - scp -o StrictHostKeyChecking=no -i ~/.ssh/helios_ssh -P ${{ secrets.SSH_PORT }} .env ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:~/.env - scp -o StrictHostKeyChecking=no -i ~/.ssh/helios_ssh -P ${{ secrets.SSH_PORT }} -r compose/ ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:~/compose/ - - - name: Deploy to DigitalOcean - run: | - ssh -o StrictHostKeyChecking=no -i ~/.ssh/helios_ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "docker compose down" - ssh -o StrictHostKeyChecking=no -i ~/.ssh/helios_ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "docker compose pull" - ssh -o StrictHostKeyChecking=no -i ~/.ssh/helios_ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "docker compose up -d --build" + # - name: Form environment variables + # run: | + # echo "GHCR_PAT=\"${{ secrets.GHCR_PAT }}\"" >> .env + # echo "MASTER_BACKEND_PORT=\"${{ secrets.MASTER_BACKEND_PORT }}\"" >> .env + # echo "MASTER_BACKEND_URL=\"${{ secrets.MASTER_BACKEND_URL }}\"" >> .env + # echo "MASTER_METRICS_PORT=\"${{ secrets.MASTER_METRICS_PORT }}\"" >> .env + # echo "MASTER_METRICS_URL=\"${{ secrets.MASTER_METRICS_URL }}\"" >> .env + # echo "DATABASE_PORT=\"${{ secrets.DATABASE_PORT }}\"" >> .env + # echo "DATABASE_NAME=\"${{ secrets.DATABASE_NAME }}\"" >> .env + # echo "DATABASE_USER=\"${{ secrets.DATABASE_USER }}\"" >> .env + # echo "DATABASE_PASSWORD=\"${{ secrets.DATABASE_PASSWORD }}\"" >> .env + # echo "DATABASE_HOST=\"${{ secrets.DATABASE_HOST }}\"" >> .env + # echo "DATABASE_URL=\"${{ secrets.DATABASE_URL }}\"" >> .env + # echo "JWT_ACCESS_SECRET=\"${{ secrets.JWT_ACCESS_SECRET }}\"" >> .env + # echo "JWT_REFRESH_SECRET=\"${{ secrets.JWT_REFRESH_SECRET }}\"" >> .env + # echo "RUST_ENV=\"${{ secrets.RUST_ENV }}\"" >> .env + # echo "HELIOS_DOMAIN=\"${{ secrets.HELIOS_DOMAIN }}\"" >> .env + # echo "DO_AUTH_TOKEN=\"${{ secrets.DO_AUTH_TOKEN }}\"" >> .env + # echo "TRAEFIK_AUTH=\"${{ secrets.TRAEFIK_AUTH }}\"" >> .env + # echo "GRAFANA_USER=\"${{ secrets.GRAFANA_USER }}\"" >> .env + # echo "GRAFANA_PASSWORD=\"${{ secrets.GRAFANA_PASSWORD }}\"" >> .env + # - name: Setup to DigitalOcean + # run: | + # ssh -o StrictHostKeyChecking=no -i ~/.ssh/helios_ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "echo ${{ secrets.GHCR_PAT }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin" + # ssh -o StrictHostKeyChecking=no -i ~/.ssh/helios_ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "rm -rf docker-compose.yml .env compose/" + # scp -o StrictHostKeyChecking=no -i ~/.ssh/helios_ssh -P ${{ secrets.SSH_PORT }} docker-compose.yml ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:~/docker-compose.yml + # scp -o StrictHostKeyChecking=no -i ~/.ssh/helios_ssh -P ${{ secrets.SSH_PORT }} .env ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:~/.env + # scp -o StrictHostKeyChecking=no -i ~/.ssh/helios_ssh -P ${{ secrets.SSH_PORT }} -r compose/ ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:~/compose/ + # - name: Deploy to DigitalOcean + # run: | + # ssh -o StrictHostKeyChecking=no -i ~/.ssh/helios_ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "docker compose down" + # ssh -o StrictHostKeyChecking=no -i ~/.ssh/helios_ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "docker compose pull" + # ssh -o StrictHostKeyChecking=no -i ~/.ssh/helios_ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "docker compose up -d --build" diff --git a/src/enums/errors/external/auth.rs b/src/enums/errors/external/auth.rs index a592d8f..0ffad85 100644 --- a/src/enums/errors/external/auth.rs +++ b/src/enums/errors/external/auth.rs @@ -11,8 +11,6 @@ use crate::{dto::response::error::Response, enums::errors::internal}; pub enum Auth { WrongToken, WrongPassword, - WrongEmail, - TokenCreation, UserNotFound, UserAlreadyExists, PasswordIsSame, @@ -26,8 +24,6 @@ impl std::fmt::Display for Auth { match self { Auth::WrongToken => write!(f, "WrongToken"), Auth::WrongPassword => write!(f, "WrongPassword"), - Auth::WrongEmail => write!(f, "WrongEmail"), - Auth::TokenCreation => write!(f, "TokenCreation"), Auth::UserNotFound => write!(f, "UserNotFound"), Auth::UserAlreadyExists => write!(f, "UserAlreadyExists"), Auth::PasswordIsSame => write!(f, "PasswordIsSame"), @@ -43,8 +39,6 @@ impl IntoResponse for Auth { let (status, message) = match self { Auth::WrongToken => (StatusCode::UNAUTHORIZED, "Wrong token"), Auth::WrongPassword => (StatusCode::UNAUTHORIZED, "Wrong password"), - Auth::WrongEmail => (StatusCode::UNAUTHORIZED, "Wrong email"), - Auth::TokenCreation => (StatusCode::INTERNAL_SERVER_ERROR, "Token creation error"), Auth::UserNotFound => (StatusCode::NOT_FOUND, "User not found"), Auth::UserAlreadyExists => (StatusCode::CONFLICT, "User already exists"), Auth::PasswordIsSame => (StatusCode::CONFLICT, "Password is the same"), @@ -69,8 +63,6 @@ impl From for Auth { fn from(error: internal::Auth) -> Self { match error { internal::Auth::WrongPassword => Auth::WrongPassword, - internal::Auth::WrongEmail => Auth::WrongEmail, - internal::Auth::TokenCreation => Auth::TokenCreation, internal::Auth::UserNotFound => Auth::UserNotFound, internal::Auth::UserAlreadyExists => Auth::UserAlreadyExists, internal::Auth::PasswordIsSame => Auth::PasswordIsSame, diff --git a/src/enums/errors/internal/auth.rs b/src/enums/errors/internal/auth.rs index d18c445..aa48fd9 100644 --- a/src/enums/errors/internal/auth.rs +++ b/src/enums/errors/internal/auth.rs @@ -1,8 +1,7 @@ #[derive(Debug, Clone)] +#[allow(clippy::enum_variant_names)] pub enum Auth { WrongPassword, - WrongEmail, - TokenCreation, UserNotFound, UserAlreadyExists, PasswordIsSame, @@ -15,8 +14,6 @@ impl std::fmt::Display for Auth { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Auth::WrongPassword => write!(f, "Wrong password"), - Auth::WrongEmail => write!(f, "Wrong email"), - Auth::TokenCreation => write!(f, "Token creation error"), Auth::UserNotFound => write!(f, "User not found"), Auth::UserAlreadyExists => write!(f, "User already exists"), Auth::PasswordIsSame => write!(f, "Password is the same"), diff --git a/src/services/user/change_password.rs b/src/services/user/change_password.rs index 3b11a5b..4a84e60 100644 --- a/src/services/user/change_password.rs +++ b/src/services/user/change_password.rs @@ -1,11 +1,49 @@ +use diesel::prelude::*; +use tracing::{error, info}; use uuid::Uuid; -use crate::{data::models::User, enums::errors::internal::Result}; +use crate::{ + data::schema, + enums::errors::internal::{Auth, Error, Result}, + utils::hash, +}; + +use super::get_by_id; pub async fn change_password( - _pool: &deadpool_diesel::postgres::Pool, - _user_id: &Uuid, - _new_password: &str, -) -> Result { - todo!() + pool: &deadpool_diesel::postgres::Pool, + user_id: &Uuid, + new_password: &str, +) -> Result<()> { + let conn = pool.get().await?; + let user = get_by_id(pool, user_id).await?; + + if user.classic_auth.is_none() { + return Err(Error::Auth(Auth::NoClassicAuth)); + } + + if hash::verify_password(new_password, &user.classic_auth.unwrap().password_hash) + .await + .is_ok() + { + error!("Password is the same for user: {}", user_id); + return Err(Error::Auth(Auth::PasswordIsSame)); + } + + let new_password_hash = hash::hash_password(new_password).await?; + let user_id = *user_id; + + conn.interact(move |_| { + let _ = diesel::update(schema::user::table.find(user_id)) + .set(schema::user::updated_at.eq(diesel::dsl::now)); + let _ = diesel::update( + schema::classic_auth::table.filter(schema::classic_auth::user_id.eq(user_id)), + ) + .set(schema::classic_auth::password_hash.eq(new_password_hash)); + }) + .await?; + + info!("Password changed successfully for user: {}", user_id); + + Ok(()) }