264 lines
11 KiB
Markdown
264 lines
11 KiB
Markdown
# 🧩 SOA – Service-Oriented Architecture Project
|
||
|
||
## 🏗️ Architecture Overview
|
||
|
||
This project implements a **Service-Oriented Architecture (SOA)** for an art gallery management system using Docker containerization and microservices architecture.
|
||
|
||
### System Components
|
||
|
||
```
|
||
🌐 HTTPS (Port 443)
|
||
│
|
||
┌──────────────────────────▼──────────────────────────────┐
|
||
│ Apache Reverse Proxy │
|
||
│ (SSL Termination + OIDC Validation) │
|
||
│ │
|
||
│ /api/public/* ──────┐ ┌────── /api/private/* │
|
||
│ (No Auth) │ │ (OIDC Required) │
|
||
└───────────────────────┼──────┼──────────────────────────┘
|
||
│ │ |
|
||
│ │ (+ OIDC Headers) |
|
||
│ │ |
|
||
┌─────────────▼──┐ ┌─▼──────────────┐ |
|
||
│ │ │ │ |
|
||
│ Public API │ │ Private API │ |
|
||
│ (Laravel) │ │ (Flask) │ |
|
||
│ Port 5001 │ │ Port 5002 │ |
|
||
└─────────┬──────┘ └─┬──────────────┘ |
|
||
│ │ |
|
||
┌─────────▼──────────▼─────────┐ ┌───────▼──────┐
|
||
│ MySQL │ │ Keycloak │
|
||
│ (Application DB) │ │ + Postgres │
|
||
└──────────────────────────────┘ └──────────────┘
|
||
│
|
||
┌─────────────────▼─────────────────┐
|
||
│ Redis │
|
||
│ (Cache + Events) │
|
||
└───────────────────────────────────┘
|
||
```
|
||
|
||
### Technology Stack
|
||
|
||
| Component | Technology | Purpose |
|
||
|-----------|------------|---------|
|
||
| **Reverse Proxy** | Apache HTTP Server | SSL termination, request routing |
|
||
| **Authentication** | Keycloak | OIDC/OAuth2 identity provider |
|
||
| **Public API** | Laravel 12 (PHP) | Public galleries and artworks API |
|
||
| **Private API** | Flask (Python) | User management and private content |
|
||
| **Databases** | PostgreSQL + MySQL | Data persistence |
|
||
| **Cache/Queue** | Redis | Caching and event messaging | // NOT IMPLEMENTED YET
|
||
|
||
### Service Architecture
|
||
|
||
#### 🔓 **Public API Service** (Laravel)
|
||
- **Port**: Internal 5001 (via Apache)
|
||
- **Database**: MySQL
|
||
- **Purpose**: Public access to galleries and artworks
|
||
- **Features**:
|
||
- RESTful API for public galleries
|
||
- Artist directory
|
||
- Public artwork browsing
|
||
- Pagination and filtering
|
||
|
||
#### 🔒 **Private API Service** (Flask)
|
||
- **Port**: Internal 5002 (via Apache)
|
||
- **Database**: MySQL
|
||
- **Authentication**: OIDC via Apache mod_auth_openidc
|
||
- **Purpose**: Authenticated user operations
|
||
- **Features**:
|
||
- User profile management
|
||
- Gallery creation and management
|
||
- Artwork upload and editing
|
||
- Review system for galleries/artworks
|
||
- Invitation system for gallery members
|
||
|
||
#### 🔐 **Authentication Service** (Keycloak)
|
||
- **Port**: 8080
|
||
- **Database**: PostgreSQL
|
||
- **Purpose**: Centralized identity and access management
|
||
- **Features**:
|
||
- OIDC/OAuth2 provider
|
||
- User registration and login
|
||
- Token-based authentication
|
||
- Single Sign-On (SSO)
|
||
|
||
### Request Flow
|
||
|
||
All requests enter through **Apache on port 443** (HTTPS) and are processed as follows:
|
||
|
||
1. **SSL Termination**: Apache handles SSL/TLS encryption
|
||
2. **Request Routing**: Based on URL path:
|
||
- `/api/public/*` → Routes to **Public API** (Laravel on port 5001)
|
||
- `/api/private/*` → Routes to **Private API** (Flask on port 5002)
|
||
3. **OIDC Authentication Check**:
|
||
- **Public API**: No authentication required
|
||
- **Private API**: Apache mod_auth_openidc validates OIDC tokens with Keycloak
|
||
4. **Header Injection**: Apache injects user info headers (OIDC_email, OIDC_user) for authenticated requests
|
||
5. **API Processing**: Backend services handle business logic
|
||
6. **Data Persistence**: MySQL stores application data
|
||
7. **Event Publishing**: Redis handles inter-service communication
|
||
|
||
### Security Model
|
||
|
||
- **SSL/TLS**: All external communication encrypted
|
||
- **OIDC Authentication**: Industry standard OAuth2/OIDC flow
|
||
- **Token-based Authorization**: JWT tokens for API access
|
||
- **Network Isolation**: Services communicate via internal network
|
||
- **Database Security**: Separate databases for auth and application data
|
||
|
||
## 🚀 Quick Start
|
||
|
||
1. **Start the application stack:**
|
||
|
||
```bash
|
||
docker compose up -d --build
|
||
```
|
||
|
||
2. **Initialize Keycloak configuration:**
|
||
|
||
```bash
|
||
./setup-keycloak.sh
|
||
```
|
||
|
||
3. **Update your `/etc/hosts` file:**
|
||
|
||
```
|
||
127.0.0.1 api.local auth.local
|
||
```
|
||
|
||
---
|
||
|
||
## 🔐 Credentials
|
||
|
||
### Keycloak Admin Panel
|
||
|
||
* 📍 URL: [http://auth.local:8080](http://auth.local:8080)
|
||
* 👤 **Username:** `admin`
|
||
* 🔑 **Password:** `admin`
|
||
|
||
### Private API User
|
||
|
||
* 👤 **Username:** `alexis`
|
||
* 🔑 **Password:** `password`
|
||
|
||
### Getting Bearer Token for API Testing
|
||
|
||
To test the private API with Bearer token authentication, get an access token from:
|
||
|
||
**Endpoint:** `http://auth.local:8080/realms/master/protocol/openid-connect/token`
|
||
**Method:** POST
|
||
**Content-Type:** `application/x-www-form-urlencoded`
|
||
|
||
**Required fields:**
|
||
- `grant_type`: `password`
|
||
- `client_id`: `soa`
|
||
- `client_secret`: `mysecret`
|
||
- `username`: `alexis`
|
||
- `password`: `password`
|
||
|
||
**Example curl command:**
|
||
```bash
|
||
curl -X POST http://auth.local:8080/realms/master/protocol/openid-connect/token \
|
||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||
-d "grant_type=password" \
|
||
-d "client_id=soa" \
|
||
-d "client_secret=mysecret" \
|
||
-d "username=alexis" \
|
||
-d "password=password"
|
||
```
|
||
|
||
Use the returned `access_token` in the Authorization header:
|
||
```bash
|
||
curl -H "Authorization: Bearer <access_token>" https://api.local/api/private/me
|
||
```
|
||
|
||
---
|
||
|
||
## 🗂️ Public API Endpoints Overview
|
||
|
||
All routes are prefixed with `/api/public`.
|
||
|
||
| Method | Endpoint | Description |
|
||
| ------ | ------------------------------- | ---------------------------------- |
|
||
| GET | `/artists` | List all public artists |
|
||
| GET | `/galleries` | List all public galleries |
|
||
| GET | `/galleries/{gallery}/artworks` | List artworks for a public gallery |
|
||
|
||
---
|
||
|
||
## 🗂️ Private API Endpoints Overview
|
||
|
||
All routes are prefixed with `/api/private` and require a **Bearer token**.
|
||
|
||
### 👤 User
|
||
|
||
| Method | Endpoint | Description |
|
||
| ------ | -------- | ----------------------------- |
|
||
| GET | `/me` | Get current user's profile |
|
||
| PUT | `/me` | Update current user's profile |
|
||
|
||
### 🖼️ Galleries
|
||
|
||
| Method | Endpoint | Description |
|
||
| ------ | ----------------------- | --------------------------------- |
|
||
| GET | `/galleries` | List all accessible galleries |
|
||
| GET | `/galleries/mine` | List galleries owned by the user |
|
||
| POST | `/gallery` | Create a new gallery |
|
||
| GET | `/gallery/{gallery_id}` | Get details of a specific gallery |
|
||
| PUT | `/gallery/{gallery_id}` | Update a gallery (owner only) |
|
||
|
||
### 👥 Members
|
||
|
||
| Method | Endpoint | Description |
|
||
| ------ | ------------------------------- | ------------------------- |
|
||
| GET | `/gallery/{gallery_id}/members` | List members of a gallery |
|
||
|
||
### 📩 Invitations
|
||
|
||
| Method | Endpoint | Description |
|
||
| ------ | ----------------------------------- | --------------------------- |
|
||
| POST | `/gallery/{gallery_id}/invite` | Invite user to a gallery |
|
||
| PUT | `/invitations/{gallery_id}/respond` | Accept or reject invitation |
|
||
| GET | `/invitations/received` | List received invitations |
|
||
|
||
### 🖼️ Artworks
|
||
|
||
| Method | Endpoint | Description |
|
||
| ------ | -------------------------------- | ----------------------------------- |
|
||
| GET | `/gallery/{gallery_id}/artworks` | List artworks in a gallery |
|
||
| POST | `/gallery/{gallery_id}/artwork` | Add artwork to gallery (owner only) |
|
||
| GET | `/artwork/{artwork_id}` | Get details of an artwork |
|
||
| PUT | `/artwork/{artwork_id}` | Update an artwork (creator only) |
|
||
| GET | `/artworks/mine` | List artworks created by the user |
|
||
|
||
### 📝 Gallery Reviews
|
||
|
||
| Method | Endpoint | Description |
|
||
| ------ | ------------------------------- | ------------------------------------- |
|
||
| GET | `/gallery/{gallery_id}/reviews` | List reviews for a gallery |
|
||
| POST | `/gallery/{gallery_id}/review` | Submit a review for a gallery |
|
||
| PUT | `/galleries/review/{review_id}` | Update a gallery review (author only) |
|
||
| GET | `/galleries/reviews/given` | Reviews written by the user |
|
||
| GET | `/galleries/reviews/received` | Reviews received on user’s galleries |
|
||
|
||
### 📝 Artwork Reviews
|
||
|
||
| Method | Endpoint | Description |
|
||
| ------ | ------------------------------- | -------------------------------------- |
|
||
| GET | `/artwork/{artwork_id}/reviews` | List reviews for an artwork |
|
||
| POST | `/artwork/{artwork_id}/review` | Submit a review for an artwork |
|
||
| PUT | `/artworks/review/{review_id}` | Update an artwork review (author only) |
|
||
| GET | `/artworks/reviews/given` | Reviews written by the user |
|
||
| GET | `/artworks/reviews/received` | Reviews received on user’s artworks |
|
||
|
||
---
|
||
|
||
# Public API:
|
||
|
||
## Routes :
|
||
```
|
||
GET|HEAD api/artists
|
||
GET|HEAD api/galleries
|
||
GET|HEAD api/galleries/{gallery}/artworks
|
||
```
|