Skip to content
Draft
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
218 changes: 218 additions & 0 deletions USER_PROFILES_API.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
# User Profile CRUD Endpoints

This document describes the user profile management endpoints that have been added to the vulnerable-node application.

## Overview

The user profile endpoints provide complete CRUD (Create, Read, Update, Delete) functionality for managing user profiles in the system. All endpoints require authentication.

## Database Schema

The `user_profiles` table contains the following fields:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| username | VARCHAR(100) | Yes | Primary key, unique identifier |
| email | VARCHAR(100) | Yes | User's email address |
| full_name | VARCHAR(200) | No | User's full name |
| bio | TEXT | No | User biography |
| avatar_url | VARCHAR(500) | No | URL to user's avatar image |

## API Endpoints

### 1. Create User Profile

Creates a new user profile.

**Endpoint:** `POST /api/profiles`

**Request Body:**
```json
{
"username": "johndoe",
"email": "john@example.com",
"full_name": "John Doe",
"bio": "Software developer",
"avatar_url": "https://example.com/avatar.jpg"
}
```

**Success Response:**
- **Code:** 201 Created
- **Content:** The created profile object

**Error Response:**
- **Code:** 400 Bad Request - Missing required fields
- **Code:** 500 Internal Server Error - Database error

**Example:**
```bash
curl -X POST http://localhost:3000/api/profiles \
-H "Content-Type: application/json" \
-H "Cookie: connect.sid=YOUR_SESSION_COOKIE" \
-d '{
"username": "johndoe",
"email": "john@example.com",
"full_name": "John Doe",
"bio": "Software developer"
}'
```

### 2. List All User Profiles

Retrieves all user profiles.

**Endpoint:** `GET /api/profiles`

**Success Response:**
- **Code:** 200 OK
- **Content:** Array of profile objects

**Error Response:**
- **Code:** 500 Internal Server Error

**Example:**
```bash
curl http://localhost:3000/api/profiles \
-H "Cookie: connect.sid=YOUR_SESSION_COOKIE"
```

### 3. Get User Profile

Retrieves a specific user profile by username.

**Endpoint:** `GET /api/profiles/:username`

**URL Parameters:**
- `username` - The username of the profile to retrieve

**Success Response:**
- **Code:** 200 OK
- **Content:** Profile object

**Error Response:**
- **Code:** 404 Not Found - Profile does not exist

**Example:**
```bash
curl http://localhost:3000/api/profiles/johndoe \
-H "Cookie: connect.sid=YOUR_SESSION_COOKIE"
```

### 4. Update User Profile

Updates an existing user profile.

**Endpoint:** `PUT /api/profiles/:username`

**URL Parameters:**
- `username` - The username of the profile to update

**Request Body:**
```json
{
"email": "newemail@example.com",
"full_name": "John Smith",
"bio": "Senior software developer",
"avatar_url": "https://example.com/new-avatar.jpg"
}
```

**Success Response:**
- **Code:** 200 OK
- **Content:** Updated profile object

**Error Response:**
- **Code:** 400 Bad Request - Missing required fields
- **Code:** 404 Not Found - Profile does not exist

**Example:**
```bash
curl -X PUT http://localhost:3000/api/profiles/johndoe \
-H "Content-Type: application/json" \
-H "Cookie: connect.sid=YOUR_SESSION_COOKIE" \
-d '{
"email": "newemail@example.com",
"full_name": "John Smith",
"bio": "Senior software developer"
}'
```

### 5. Delete User Profile

Deletes a user profile.

**Endpoint:** `DELETE /api/profiles/:username`

**URL Parameters:**
- `username` - The username of the profile to delete

**Success Response:**
- **Code:** 200 OK
- **Content:**
```json
{
"message": "Profile deleted successfully",
"data": { /* deleted profile object */ }
}
```

**Error Response:**
- **Code:** 404 Not Found - Profile does not exist

**Example:**
```bash
curl -X DELETE http://localhost:3000/api/profiles/johndoe \
-H "Cookie: connect.sid=YOUR_SESSION_COOKIE"
```

## Authentication

All endpoints require authentication. Users must be logged in with a valid session cookie. If not authenticated, the endpoints will redirect to the login page.

To authenticate, first login using:

```bash
curl -X POST http://localhost:3000/login/auth \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin&password=admin" \
-c cookies.txt

# Then use the session cookie for subsequent requests
curl http://localhost:3000/api/profiles \
-b cookies.txt
```

## Implementation Details

### Security Features

1. **Parameterized Queries**: All database operations use parameterized queries to prevent SQL injection attacks.
2. **Authentication Required**: All endpoints check for authenticated sessions before processing requests.
3. **Input Validation**: Required fields are validated before database operations.

### Code Structure

- **Model**: `/model/user_profiles.js` - Contains database operations
- **Routes**: `/routes/user_profiles.js` - Defines API endpoints
- **Database Init**: `/model/init_db.js` - Creates the user_profiles table

### Error Handling

The endpoints include comprehensive error handling:
- Validation errors return 400 Bad Request
- Missing resources return 404 Not Found
- Database errors return 500 Internal Server Error with descriptive messages

## Testing

A verification script is available at `/tmp/test_profile_endpoints.js` that validates:
- Model function exports
- Route definitions
- Database table creation
- App.js integration

Run with:
```bash
node /tmp/test_profile_endpoints.js
```
2 changes: 2 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
var init_db = require('./model/init_db');
var login = require('./routes/login');
var products = require('./routes/products');
var user_profiles = require('./routes/user_profiles');

Check warning on line 15 in app.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

app.js#L15

Require statement not part of import statement.

var app = express();

Expand Down Expand Up @@ -53,6 +54,7 @@
*/
app.use('', products);
app.use('', login);
app.use('', user_profiles);


// catch 404 and forward to error handler
Expand Down
7 changes: 7 additions & 0 deletions model/init_db.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ function init_db() {
.catch(function (err) {
});

db.one('CREATE TABLE user_profiles(username VARCHAR(100) PRIMARY KEY, email VARCHAR(100) not null, full_name VARCHAR(200), bio TEXT, avatar_url VARCHAR(500))')
.then(function () {

})
.catch(function (err) {
});


}

Expand Down
38 changes: 38 additions & 0 deletions model/user_profiles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
var config = require("../config"),

Check warning on line 1 in model/user_profiles.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

model/user_profiles.js#L1

Require statement not part of import statement.
pgp = require('pg-promise')(),

Check warning on line 2 in model/user_profiles.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

model/user_profiles.js#L2

Require statement not part of import statement.
db = pgp(config.db.connectionString);

function createProfile(profile) {

Check warning on line 5 in model/user_profiles.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

model/user_profiles.js#L5

`meta.messages` must contain at least one violation message.

Check warning on line 5 in model/user_profiles.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

model/user_profiles.js#L5

`meta.schema` is required (use [] if rule has no schema).

Check warning on line 5 in model/user_profiles.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

model/user_profiles.js#L5

`meta.type` is required (must be either `problem`, `suggestion`, or `layout`).
var q = "INSERT INTO user_profiles(username, email, full_name, bio, avatar_url) VALUES($1, $2, $3, $4, $5) RETURNING *;";
return db.one(q, [profile.username, profile.email, profile.full_name, profile.bio, profile.avatar_url]);
}

function getProfile(username) {
var q = "SELECT * FROM user_profiles WHERE username = $1;";
return db.one(q, [username]);
}

function updateProfile(username, profile) {
var q = "UPDATE user_profiles SET email = $2, full_name = $3, bio = $4, avatar_url = $5 WHERE username = $1 RETURNING *;";
return db.one(q, [username, profile.email, profile.full_name, profile.bio, profile.avatar_url]);
}

function deleteProfile(username) {
var q = "DELETE FROM user_profiles WHERE username = $1 RETURNING *;";
return db.one(q, [username]);
}

function listProfiles() {
var q = "SELECT * FROM user_profiles;";
return db.many(q);
}

var actions = {
"create": createProfile,
"get": getProfile,
"update": updateProfile,
"delete": deleteProfile,
"list": listProfiles
};

module.exports = actions;
Loading
Loading