From 0b825254a60cb0dfc30dae69218dbb0699ee8831 Mon Sep 17 00:00:00 2001 From: Tiago Yamamoto Date: Sat, 27 Dec 2025 12:26:02 -0300 Subject: [PATCH] Add identity gateway scaffold --- identity-gateway/go.mod | 8 +++ identity-gateway/internal/auth/provider.go | 64 ++++++++++++++++++++++ identity-gateway/migrations/migration.sql | 17 ++++++ 3 files changed, 89 insertions(+) create mode 100644 identity-gateway/go.mod create mode 100644 identity-gateway/internal/auth/provider.go create mode 100644 identity-gateway/migrations/migration.sql diff --git a/identity-gateway/go.mod b/identity-gateway/go.mod new file mode 100644 index 0000000..4d952ed --- /dev/null +++ b/identity-gateway/go.mod @@ -0,0 +1,8 @@ +module identity-gateway + +go 1.22 + +require ( + github.com/appwrite/sdk-for-go v1.0.4 + github.com/gin-gonic/gin v1.10.0 +) diff --git a/identity-gateway/internal/auth/provider.go b/identity-gateway/internal/auth/provider.go new file mode 100644 index 0000000..8408de3 --- /dev/null +++ b/identity-gateway/internal/auth/provider.go @@ -0,0 +1,64 @@ +package auth + +import ( + "database/sql" + "net/http" + "strings" + + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/users" + "github.com/gin-gonic/gin" +) + +type MyUser struct { + ID string + Role string +} + +type AuthProvider struct { + AppwriteClient client.Client + DB *sql.DB +} + +func (p *AuthProvider) ValidateSession() gin.HandlerFunc { + return func(c *gin.Context) { + tokenHeader := c.GetHeader("Authorization") + token := strings.TrimSpace(strings.TrimPrefix(tokenHeader, "Bearer")) + if token == "" { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Token ausente"}) + return + } + + p.AppwriteClient.SetJWT(token) + appwriteUsers := users.New(p.AppwriteClient) + remoteUser, err := appwriteUsers.Get() + if err != nil { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Sessão inválida no IDP"}) + return + } + + var userLocal MyUser + err = p.DB.QueryRow( + "SELECT id, role FROM users WHERE appwrite_id = $1", + remoteUser.Id, + ).Scan(&userLocal.ID, &userLocal.Role) + + if err == sql.ErrNoRows { + err = p.DB.QueryRow( + "INSERT INTO users (appwrite_id, email, full_name) VALUES ($1, $2, $3) RETURNING id, role", + remoteUser.Id, + remoteUser.Email, + remoteUser.Name, + ).Scan(&userLocal.ID, &userLocal.Role) + } + + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "Erro ao sincronizar usuário"}) + return + } + + c.Set("user_id", userLocal.ID) + c.Set("user_role", userLocal.Role) + c.Next() + } +} diff --git a/identity-gateway/migrations/migration.sql b/identity-gateway/migrations/migration.sql new file mode 100644 index 0000000..c26549c --- /dev/null +++ b/identity-gateway/migrations/migration.sql @@ -0,0 +1,17 @@ +CREATE TABLE users ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + appwrite_id VARCHAR(255) UNIQUE NOT NULL, + email VARCHAR(255) UNIQUE NOT NULL, + full_name VARCHAR(255), + role VARCHAR(20) DEFAULT 'user' CHECK (role IN ('admin', 'manager', 'user')), + is_active BOOLEAN DEFAULT true, + last_login TIMESTAMP WITH TIME ZONE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE user_security_configs ( + user_id UUID REFERENCES users(id) ON DELETE CASCADE, + master_key_hint VARCHAR(100), + encryption_version INT DEFAULT 1, + PRIMARY KEY (user_id) +);