diff --git a/.github/workflows/badges.yml b/.github/workflows/badges.yml new file mode 100644 index 0000000..e65b76e --- /dev/null +++ b/.github/workflows/badges.yml @@ -0,0 +1,52 @@ +name: Badges + +on: + push: + branches: + - main + release: + types: + - created + +jobs: + create_badges: + runs-on: ubuntu-latest + name: Create Badges + steps: + - name: Checkout Repository + uses: actions/checkout@v1 + + - name: Generate Badges + uses: thewerthon/RepoBadges@v1.0.0 + id: badges + with: + directory: ./ + patterns: "**" + ignore: "node_modules" + version_prefix: "v" + version_fallback: "v0.0.0" + version_badge: ./output/version.svg + updated_badge: ./output/updated.svg + files_badge: ./output/files.svg + lines_badge: ./output/lines.svg + version_badge_label: "Current Version" + updated_badge_label: "Last Updated" + files_badge_label: "Total of Files" + lines_badge_label: "Lines of Code" + version_badge_style: "classic" + updated_badge_style: "classic" + files_badge_style: "classic" + lines_badge_style: "classic" + version_badge_color: "green" + updated_badge_color: "blue" + files_badge_color: "blue" + lines_badge_color: "blue" + + - name: Deploy to Branch + uses: peaceiris/actions-gh-pages@v3 + with: + publish_dir: ./output + publish_branch: badges + github_token: ${{ secrets.GITHUB_TOKEN }} + user_name: 'github-actions[bot]' + user_email: 'github-actions[bot]@users.noreply.github.com' diff --git a/README.md b/README.md index 4f647f9..01a1d51 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@

High performance self-hosted and fully customizable authentication service

+![Last Updated](https://raw.githubusercontent.com/JohnGrubba/ezauth/badges/updated.svg) +![Lines of Code](https://raw.githubusercontent.com/JohnGrubba/ezauth/badges/lines.svg) +![Total Files](https://raw.githubusercontent.com/JohnGrubba/ezauth/badges/files.svg) + ## Disclaimer - ⚠️ The project is under **very active** development. @@ -30,7 +34,7 @@ docker compose -f .\docker-compose.dev.yml up -d --build ## Testing To be able to perform tests, that represent a real environment, the following technologies are used: -- [MongoMock](https://github.com/mongomock/mongomock) +- [MongoMock](https://github.com/JohnGrubba/mongomock) - [FakeRedis](https://github.com/cunla/fakeredis-py) Those Libraries automatically get used instead of `pymongo` and `redis` when testing, to avoid the need of an additional Redis and MongoDB instance. @@ -39,4 +43,4 @@ To run the tests, you can use the following command: ```sh docker exec ezauth-api pytest -``` \ No newline at end of file +``` diff --git a/docs/configuration/configuration.md b/docs/configuration/configuration.md index b9dd7f6..6e6ac7e 100644 --- a/docs/configuration/configuration.md +++ b/docs/configuration/configuration.md @@ -19,7 +19,8 @@ Make sure that all parameters are set correctly before starting the service. | `signup.enable_welcome_email` | **Datatype:** Boolean
**Default:** `false`
Enable or disable the welcome E-Mail for new users. | | `signup.oauth.providers_enabled` | **Datatype:** List
**Default:** `[]`
Enabled OAuth Providers.
**Possible Providers** | | `signup.oauth.base_url` | **Datatype:** String
**Default:** `"http://localhost:3250/"`
The Base URL for the callback URL from OAuth Providers. When you host the service somewhere, you may want to change this to the official Domain instead of an IP. This is also the value you set when setting up your OAuth Providers. Make sure those values match. | - +| `signup.password_complexity` | **Datatype:** Integer
**Default:** `4`
Password Complexity Requirement. Every higher value, includes all the previous ones too.
| +| `signup.username_complexity` | **Datatype:** Integer
**Default:** `2`
Username Complexity Requirement. Every higher value, includes all the previous ones too.
| ### E-Mail Configuration diff --git a/docs/getting-started/setup.md b/docs/getting-started/setup.md index 235a26b..d17f361 100644 --- a/docs/getting-started/setup.md +++ b/docs/getting-started/setup.md @@ -34,6 +34,6 @@ For an explanation of the configuration options, see the [Configuration](../conf After you have configured the service you can start it by running the following command: ``` bash -docker compose up -d +docker compose up -d --build ``` -The service should now be running and you can access the API by navigating to `http://localhost:3250` in your browser. The API Documentation is available at `http://localhost:3250/docs`. \ No newline at end of file +The service should now be running and you can access the API by navigating to `http://localhost:3250` in your browser. The API Documentation is available at `http://localhost:3250/docs`. diff --git a/src/api/model.py b/src/api/model.py index fcbd1b7..8381b18 100644 --- a/src/api/model.py +++ b/src/api/model.py @@ -1,5 +1,6 @@ from pydantic import BaseModel, field_validator, EmailStr, SecretStr, ConfigDict, Field from typing import Optional, List +from tools import SignupConfig import re import bcrypt @@ -56,13 +57,15 @@ class PasswordHashed(BaseModel): def password_check_hash(cls, password: SecretStr) -> str: # Validate Password pswd = password.get_secret_value() - if len(pswd) < 8: - raise ValueError("Make sure your password is at least 8 letters") - elif re.search("[0-9]", pswd) is None: + if len(pswd) == 0: + raise ValueError("Password cannot be empty") + if len(pswd) < 8 and SignupConfig.password_complexity >= 1: + raise ValueError("Make sure your password has at least 8 letters") + elif re.search("[0-9]", pswd) is None and SignupConfig.password_complexity >= 2: raise ValueError("Make sure your password has a number in it") - elif re.search("[A-Z]", pswd) is None: + elif re.search("[A-Z]", pswd) is None and SignupConfig.password_complexity >= 3: raise ValueError("Make sure your password has a capital letter in it") - elif re.search("[^a-zA-Z0-9]", pswd) is None: + elif re.search("[^a-zA-Z0-9]", pswd) is None and SignupConfig.password_complexity >= 4: raise ValueError("Make sure your password has a special character in it") elif len(pswd) > 50: raise ValueError("Make sure your password is at most 50 characters") @@ -92,10 +95,12 @@ class UserSignupRequest(PasswordHashed): @field_validator("username") @classmethod def username_check(cls, username: str) -> str: + if len(username) == 0: + raise ValueError("Username cannot be empty") if len(username) < 4: - raise ValueError("Username must be at least 4 characters long") + if SignupConfig.username_complexity >= 1: raise ValueError("Username must be at least 4 characters long") if len(username) > 20: - raise ValueError("Username must be at most 20 characters long") + if SignupConfig.username_complexity >= 2: raise ValueError("Username must be at most 20 characters long") elif re.search("[^a-zA-Z0-9]", username) is not None: raise ValueError("Username must only contain letters and numbers") return username diff --git a/src/api/profile.py b/src/api/profile.py index 5cba226..9bfd504 100644 --- a/src/api/profile.py +++ b/src/api/profile.py @@ -104,7 +104,12 @@ async def reset_password( return Response(status_code=200) -@router.post("/confirm-password", status_code=204) +@router.post("/confirm-password", status_code=204, responses={ + 204: {"description": "Password Reset Successfully"}, + 403: {"description": "Resetting Password is disabled."}, + 404: {"description": "No Password Reset Request found."}, + 401: {"description": "Invalid Code"}, +}) async def confirm_password(code: ConfirmEmailRequest, user=Depends(get_user_dep)): """ # Confirm Password Reset diff --git a/src/api/signup_test.py b/src/api/signup_test.py index ec3cffb..580a25d 100644 --- a/src/api/signup_test.py +++ b/src/api/signup_test.py @@ -191,10 +191,7 @@ def test_create_account_invalid_email(): "username": "TEST", }, ) - assert ( - response.json()["detail"][0]["msg"] - == "value is not a valid email address: An email address must have an @-sign." - ) + assert "email" in response.json()["detail"][0]["msg"] assert response.status_code == 422