Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Input data validation and optional field handling #10

Merged
merged 4 commits into from
Dec 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 41 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
A simple API to authenticate PESU credentials using PESU Academy

The API is secure and protects user privacy by not storing any user credentials. It only validates credentials and
returns the user's profile information.
returns the user's profile information. No personal data is stored or logged.

### PESUAuth LIVE Deployment

Expand Down Expand Up @@ -58,16 +58,19 @@ your system.

# How to use pesu-auth

You can send a request to the `/authenticate` endpoint with the user's credentials and the API will return a JSON object,
You can send a request to the `/authenticate` endpoint with the user's credentials and the API will return a JSON
object,
with the user's profile information if requested.

### Request Parameters

| **Parameter** | **Optional** | **Type** | **Default** | **Description** |
|---------------|--------------|-----------|-------------|--------------------------------------|
| `username` | No | `str` | | The user's SRN or PRN |
| `password` | No | `str` | | The user's password |
| `profile` | Yes | `boolean` | `False` | Whether to fetch profile information |
| **Parameter** | **Optional** | **Type** | **Default** | **Description** |
|-------------------------------|--------------|-------------|-------------|-------------------------------------------------------------------------------------------------|
| `username` | No | `str` | | The user's SRN or PRN |
| `password` | No | `str` | | The user's password |
| `profile` | Yes | `boolean` | `False` | Whether to fetch profile information |
| `know_your_class_and_section` | Yes | `boolean` | `False` | Whether to fetch Know Your Class and Section information |
| `fields` | Yes | `list[str]` | `None` | Which fields to fetch from the profile information. If not provided, all fields will be fetched |

### Response Object

Expand Down Expand Up @@ -100,6 +103,7 @@ profile data was requested, the response's `profile` key will store a dictionary
| `phone` | Phone number of the user registered with PESU |
| `campus_code` | The integer code of the campus (1 for RR and 2 for EC) |
| `campus` | Abbreviation of the user's campus name |
| `error` | The error name and stack trace, if an error occurs |

#### KnowYourClassAndSectionObject

Expand All @@ -114,8 +118,13 @@ profile data was requested, the response's `profile` key will store a dictionary
| `department` | Abbreviation of the branch along with the campus of the user |
| `branch` | Abbreviation of the branch that the user is pursuing |
| `institute_name` | The name of the campus that the user is studying in |
| `error` | The error name and stack trace, if an error occurs |

<details><summary>Here is an example using Python</summary>
## Integrating your application with pesu-auth

Here are some examples of how you can integrate your application with the PESUAuth API using Python and cURL.

### Python

#### Request

Expand All @@ -125,8 +134,9 @@ import requests
data = {
'username': 'your SRN or PRN here',
'password': 'your password here',
'profile': True # Optional, defaults to False
# Set to True if you want to retrieve the user's profile information
'profile': True, # Optional, defaults to False
'know_your_class_and_section': True, # Optional, defaults to False
'fields': None, # Optional, defaults to None to represent all fields
}

response = requests.post("http://localhost:5000/authenticate", json=data)
Expand Down Expand Up @@ -168,5 +178,25 @@ print(response.json())
}
```

</details>
### cURL

#### Request

```bash
curl -X POST http://localhost:5000/authenticate \
-H "Content-Type: application/json" \
-d '{
"username": "your SRN or PRN here",
"password": "your password here"
}'
```

#### Response

```json
{
"status": true,
"message": "Login successful.",
"timestamp": "2024-07-28 22:30:10.103368+05:30"
}
```
78 changes: 66 additions & 12 deletions app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,39 @@ def convert_readme_to_html():
f.write(html)


def validate_input(
username: str,
password: str,
profile: bool,
know_your_class_and_section: bool,
fields: list[str],
):
"""
Validate the input provided by the user.
:param username: str: The username of the user.
:param password: str: The password of the user.
:param profile: bool: Whether to fetch the profile details of the user.
:param know_your_class_and_section: bool: Whether to fetch the class and section details of the user.
:param fields: dict: The fields to fetch from the user's profile.
"""
assert username is not None, "Username not provided."
assert isinstance(username, str), "Username should be a string."
assert password is not None, "Password not provided."
assert isinstance(password, str), "Password should be a string."
assert isinstance(profile, bool), "Profile should be a boolean."
assert isinstance(
know_your_class_and_section, bool
), "know_your_class_and_section should be a boolean."
assert fields is None or (
isinstance(fields, list) and fields
), "Fields should be a non-empty list or None."
if fields is not None:
for field in fields:
assert (
isinstance(field, str) and field in pesu_academy.DEFAULT_FIELDS
), f"Invalid field: '{field}'. Valid fields are: {pesu_academy.DEFAULT_FIELDS}."


@app.route("/")
def index():
"""
Expand All @@ -57,24 +90,45 @@ def authenticate():
"""
Authenticate the user with the provided username and password.
"""
# Extract the input provided by the user
current_time = datetime.datetime.now(IST)
username = request.json.get("username")
password = request.json.get("password")
profile = request.json.get("profile", False)
current_time = datetime.datetime.now(IST)
know_your_class_and_section = request.json.get("know_your_class_and_section", False)
fields = request.json.get("fields")

# Validate the input provided by the user
try:
validate_input(username, password, profile, know_your_class_and_section, fields)
except Exception as e:
stacktrace = traceback.format_exc()
logging.error(f"Could not validate request data: {e}: {stacktrace}")
return (
json.dumps(
{
"status": False,
"message": f"Could not validate request data: {e}",
"timestamp": str(current_time),
}
),
400,
)

# try to log in only if both username and password are provided
if username and password:
username = username.strip()
password = password.strip()
authentication_result = pesu_academy.authenticate(username, password, profile)
# Authenticate the user
try:
authentication_result = pesu_academy.authenticate(
username, password, profile, know_your_class_and_section, fields
)
authentication_result["timestamp"] = str(current_time)
return json.dumps(authentication_result), 200

# if either username or password is not provided, we return an error
return (
json.dumps({"status": False, "message": "Username or password not provided."}),
400,
)
except Exception as e:
stacktrace = traceback.format_exc()
logging.error(f"Error authenticating user: {e}: {stacktrace}")
return (
json.dumps({"status": False, "message": f"Error authenticating user: {e}"}),
500,
)


if __name__ == "__main__":
Expand Down
Loading
Loading