init
This commit is contained in:
parent
6019f0e1c0
commit
d940ccced8
18
apache/site.conf
Normal file
18
apache/site.conf
Normal file
@ -0,0 +1,18 @@
|
||||
<VirtualHost *:443>
|
||||
ServerName app.local
|
||||
|
||||
SSLEngine on
|
||||
SSLCertificateFile "/etc/apache2/ssl/cert.pem"
|
||||
SSLCertificateKeyFile "/etc/apache2/ssl/key.pem"
|
||||
|
||||
ProxyPreserveHost On
|
||||
|
||||
# API Publique
|
||||
ProxyPass /api/pub/ http://localhost:5001/
|
||||
ProxyPassReverse /api/pub/ http://localhost:5001/
|
||||
|
||||
# API Utilisateur
|
||||
ProxyPass /api/user/ http://localhost:5002/
|
||||
ProxyPassReverse /api/user/ http://localhost:5002/
|
||||
</VirtualHost>
|
||||
|
51
docker-compose.yml
Normal file
51
docker-compose.yml
Normal file
@ -0,0 +1,51 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
keycloak-db:
|
||||
image: postgres:15
|
||||
environment:
|
||||
POSTGRES_DB: keycloak
|
||||
POSTGRES_USER: keycloak
|
||||
POSTGRES_PASSWORD: password
|
||||
volumes:
|
||||
- keycloak-db:/var/lib/postgresql/data
|
||||
|
||||
keycloak:
|
||||
image: quay.io/keycloak/keycloak:24.0.3
|
||||
command: start-dev
|
||||
environment:
|
||||
KC_DB: postgres
|
||||
KC_DB_URL_HOST: keycloak-db
|
||||
KC_DB_URL_DATABASE: keycloak
|
||||
KC_DB_USERNAME: keycloak
|
||||
KC_DB_PASSWORD: password
|
||||
KEYCLOAK_ADMIN: admin
|
||||
KEYCLOAK_ADMIN_PASSWORD: admin
|
||||
ports:
|
||||
- "8080:8080"
|
||||
depends_on:
|
||||
- keycloak-db
|
||||
|
||||
public_api:
|
||||
build:
|
||||
context: ./public
|
||||
volumes:
|
||||
- ./db.sqlite:/app/db.sqlite
|
||||
ports:
|
||||
- "5001:5001"
|
||||
depends_on:
|
||||
- keycloak
|
||||
|
||||
user_api:
|
||||
build:
|
||||
context: ./private
|
||||
volumes:
|
||||
- ./db.sqlite:/app/db.sqlite
|
||||
ports:
|
||||
- "5002:5002"
|
||||
depends_on:
|
||||
- keycloak
|
||||
|
||||
volumes:
|
||||
keycloak-db:
|
||||
|
151
keyclock-setup.sh
Executable file
151
keyclock-setup.sh
Executable file
@ -0,0 +1,151 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Variables
|
||||
KC_HOST="http://localhost:8080"
|
||||
REALM="myrealm"
|
||||
CLIENT_ID="myclient"
|
||||
CLIENT_SECRET="mysecret"
|
||||
USERNAME="alexis"
|
||||
PASSWORD="password"
|
||||
|
||||
# Fonction d'attente
|
||||
wait_for_keycloak() {
|
||||
echo "⏳ Attente de Keycloak..."
|
||||
until curl -s "$KC_HOST" > /dev/null; do
|
||||
sleep 2
|
||||
done
|
||||
echo "✅ Keycloak est prêt."
|
||||
}
|
||||
|
||||
# Obtenir un token admin
|
||||
get_admin_token() {
|
||||
curl -s -X POST "$KC_HOST/realms/master/protocol/openid-connect/token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "username=admin" \
|
||||
-d "password=admin" \
|
||||
-d "grant_type=password" \
|
||||
-d "client_id=admin-cli" |
|
||||
jq -r .access_token
|
||||
}
|
||||
|
||||
# Créer un realm, client et utilisateur
|
||||
setup_keycloak() {
|
||||
TOKEN=$(get_admin_token)
|
||||
|
||||
echo "🛠️ Création du realm $REALM..."
|
||||
curl -s -X POST "$KC_HOST/admin/realms" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"realm\":\"$REALM\",\"enabled\":true}" > /dev/null
|
||||
|
||||
echo "🛠️ Création du client $CLIENT_ID..."
|
||||
curl -s -X POST "$KC_HOST/admin/realms/$REALM/clients" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"clientId\": \"$CLIENT_ID\",
|
||||
\"enabled\": true,
|
||||
\"publicClient\": false,
|
||||
\"secret\": \"$CLIENT_SECRET\",
|
||||
\"redirectUris\": [\"*\"],
|
||||
\"standardFlowEnabled\": true
|
||||
}" > /dev/null
|
||||
|
||||
echo "👤 Création de l'utilisateur $USERNAME..."
|
||||
curl -s -X POST "$KC_HOST/admin/realms/$REALM/users" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"username\": \"$USERNAME\",
|
||||
\"enabled\": true,
|
||||
\"credentials\": [{
|
||||
\"type\": \"password\",
|
||||
\"value\": \"$PASSWORD\",
|
||||
\"temporary\": false
|
||||
}]
|
||||
}" > /dev/null
|
||||
|
||||
echo "✅ Configuration terminée !"
|
||||
echo "🔐 Utilisateur: $USERNAME / $PASSWORD"
|
||||
echo "🪪 Client secret: $CLIENT_SECRET"
|
||||
}
|
||||
|
||||
# Lancer le setup
|
||||
wait_for_keycloak
|
||||
setup_keycloak
|
||||
#!/bin/bash
|
||||
|
||||
# Variables
|
||||
KC_HOST="http://localhost:8080"
|
||||
REALM="myrealm"
|
||||
CLIENT_ID="myclient"
|
||||
CLIENT_SECRET="mysecret"
|
||||
USERNAME="alexis"
|
||||
PASSWORD="password"
|
||||
|
||||
# Fonction d'attente
|
||||
wait_for_keycloak() {
|
||||
echo "⏳ Attente de Keycloak..."
|
||||
until curl -s "$KC_HOST" > /dev/null; do
|
||||
sleep 2
|
||||
done
|
||||
echo "✅ Keycloak est prêt."
|
||||
}
|
||||
|
||||
# Obtenir un token admin
|
||||
get_admin_token() {
|
||||
curl -s -X POST "$KC_HOST/realms/master/protocol/openid-connect/token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "username=admin" \
|
||||
-d "password=admin" \
|
||||
-d "grant_type=password" \
|
||||
-d "client_id=admin-cli" |
|
||||
jq -r .access_token
|
||||
}
|
||||
|
||||
# Créer un realm, client et utilisateur
|
||||
setup_keycloak() {
|
||||
TOKEN=$(get_admin_token)
|
||||
|
||||
echo "🛠️ Création du realm $REALM..."
|
||||
curl -s -X POST "$KC_HOST/admin/realms" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"realm\":\"$REALM\",\"enabled\":true}" > /dev/null
|
||||
|
||||
echo "🛠️ Création du client $CLIENT_ID..."
|
||||
curl -s -X POST "$KC_HOST/admin/realms/$REALM/clients" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"clientId\": \"$CLIENT_ID\",
|
||||
\"enabled\": true,
|
||||
\"publicClient\": false,
|
||||
\"secret\": \"$CLIENT_SECRET\",
|
||||
\"redirectUris\": [\"*\"],
|
||||
\"standardFlowEnabled\": true
|
||||
}" > /dev/null
|
||||
|
||||
echo "👤 Création de l'utilisateur $USERNAME..."
|
||||
curl -s -X POST "$KC_HOST/admin/realms/$REALM/users" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"username\": \"$USERNAME\",
|
||||
\"enabled\": true,
|
||||
\"credentials\": [{
|
||||
\"type\": \"password\",
|
||||
\"value\": \"$PASSWORD\",
|
||||
\"temporary\": false
|
||||
}]
|
||||
}" > /dev/null
|
||||
|
||||
echo "✅ Configuration terminée !"
|
||||
echo "🔐 Utilisateur: $USERNAME / $PASSWORD"
|
||||
echo "🪪 Client secret: $CLIENT_SECRET"
|
||||
}
|
||||
|
||||
# Lancer le setup
|
||||
wait_for_keycloak
|
||||
setup_keycloak
|
||||
|
9
private/Dockerfile
Normal file
9
private/Dockerfile
Normal file
@ -0,0 +1,9 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
|
||||
RUN pip install flask flask_sqlalchemy pyjwt requests
|
||||
|
||||
CMD ["python", "app.py"]
|
||||
|
83
private/app.py
Normal file
83
private/app.py
Normal file
@ -0,0 +1,83 @@
|
||||
--- user_api/app.py ---
|
||||
from flask import Flask, jsonify, request, abort
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
import requests
|
||||
import jwt
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///../db.sqlite'
|
||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||
|
||||
db = SQLAlchemy(app)
|
||||
|
||||
# Configuration Keycloak
|
||||
KEYCLOAK_URL = "http://localhost:8080"
|
||||
REALM = "myrealm"
|
||||
CLIENT_ID = "myclient"
|
||||
|
||||
class Visite(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
galerie_id = db.Column(db.Integer, nullable=False)
|
||||
|
||||
class Critique(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
oeuvre_id = db.Column(db.Integer, nullable=False)
|
||||
texte = db.Column(db.Text, nullable=False)
|
||||
username = db.Column(db.String(100), nullable=False)
|
||||
|
||||
def verify_token(auth_header):
|
||||
if not auth_header.startswith("Bearer "):
|
||||
return None
|
||||
token = auth_header.split(" ")[1]
|
||||
try:
|
||||
jwks_url = f"{KEYCLOAK_URL}/realms/{REALM}/protocol/openid-connect/certs"
|
||||
jwks = requests.get(jwks_url).json()
|
||||
public_keys = {k['kid']: jwt.algorithms.RSAAlgorithm.from_jwk(k) for k in jwks['keys']}
|
||||
unverified = jwt.decode(token, options={"verify_signature": False})
|
||||
kid = unverified['kid']
|
||||
decoded = jwt.decode(token, key=public_keys[kid], algorithms=['RS256'], audience=CLIENT_ID)
|
||||
return decoded['preferred_username']
|
||||
except Exception as e:
|
||||
print(f"JWT verification failed: {e}")
|
||||
return None
|
||||
|
||||
@app.before_request
|
||||
def authenticate():
|
||||
auth = request.headers.get("Authorization", "")
|
||||
user = verify_token(auth)
|
||||
if not user:
|
||||
return jsonify({"error": "Unauthorized"}), 403
|
||||
request.user = user
|
||||
|
||||
@app.route("/", methods=["GET"])
|
||||
def index():
|
||||
return f"User API - Authenticated as {request.user}", 200
|
||||
|
||||
@app.route("/galerie/<int:galerie_id>/entrer", methods=["POST"])
|
||||
def entrer_galerie(galerie_id):
|
||||
visite = Visite(galerie_id=galerie_id)
|
||||
db.session.add(visite)
|
||||
db.session.commit()
|
||||
return jsonify({"message": "Entré dans la galerie"}), 201
|
||||
|
||||
@app.route("/galerie/<int:galerie_id>/sortir", methods=["POST"])
|
||||
def sortir_galerie(galerie_id):
|
||||
Visite.query.filter_by(galerie_id=galerie_id).delete()
|
||||
db.session.commit()
|
||||
return jsonify({"message": "Sorti de la galerie"}), 200
|
||||
|
||||
@app.route("/oeuvre/<int:oeuvre_id>/critiquer", methods=["POST"])
|
||||
def critiquer_oeuvre(oeuvre_id):
|
||||
data = request.get_json()
|
||||
if not data or not data.get("texte"):
|
||||
abort(400)
|
||||
critique = Critique(oeuvre_id=oeuvre_id, texte=data["texte"], username=request.user)
|
||||
db.session.add(critique)
|
||||
db.session.commit()
|
||||
return jsonify({"message": "Critique ajoutée"}), 201
|
||||
|
||||
if __name__ == "__main__":
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
app.run(port=5002)
|
||||
|
9
public/Dockerfile
Normal file
9
public/Dockerfile
Normal file
@ -0,0 +1,9 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
|
||||
RUN pip install flask flask_sqlalchemy pyjwt requests
|
||||
|
||||
CMD ["python", "app.py"]
|
||||
|
47
public/app.py
Normal file
47
public/app.py
Normal file
@ -0,0 +1,47 @@
|
||||
--- public_api/app.py ---
|
||||
from flask import Flask, jsonify
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///../db.sqlite'
|
||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||
|
||||
db = SQLAlchemy(app)
|
||||
|
||||
class Artiste(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
nom = db.Column(db.String(100), nullable=False)
|
||||
|
||||
class Galerie(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
nom = db.Column(db.String(100), nullable=False)
|
||||
|
||||
class Oeuvre(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
titre = db.Column(db.String(200), nullable=False)
|
||||
exposee = db.Column(db.Boolean, default=False)
|
||||
|
||||
@app.route("/", methods=["GET"])
|
||||
def index():
|
||||
return "Public API", 200
|
||||
|
||||
@app.route("/artistes", methods=["GET"])
|
||||
def get_artistes():
|
||||
artistes = Artiste.query.all()
|
||||
return jsonify([{"id": a.id, "nom": a.nom} for a in artistes]), 200
|
||||
|
||||
@app.route("/galeries", methods=["GET"])
|
||||
def get_galeries():
|
||||
galeries = Galerie.query.all()
|
||||
return jsonify([{"id": g.id, "nom": g.nom} for g in galeries]), 200
|
||||
|
||||
@app.route("/oeuvres", methods=["GET"])
|
||||
def get_oeuvres():
|
||||
oeuvres = Oeuvre.query.filter_by(exposee=True).all()
|
||||
return jsonify([{"id": o.id, "titre": o.titre} for o in oeuvres]), 200
|
||||
|
||||
if __name__ == "__main__":
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
app.run(port=5001)
|
||||
|
Loading…
x
Reference in New Issue
Block a user