mirror of
https://github.com/KaySar12/NextZen-UserService.git
synced 2025-03-15 23:25:35 +07:00
234 lines
7.0 KiB
Go
234 lines
7.0 KiB
Go
package service
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
model2 "github.com/KaySar12/NextZen-UserService/service/model"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type AuthentikService interface {
|
|
GetUserInfo(accessToken string, baseURL string) (model2.AuthentikUser, error)
|
|
GetUserApp(accessToken string, baseURL string) (model2.AuthentikApplication, error)
|
|
CreateSettings(m model2.AuthentikCredentialsDBModel) model2.AuthentikCredentialsDBModel
|
|
UpdateSettings(m model2.AuthentikCredentialsDBModel) (model2.AuthentikCredentialsDBModel, error)
|
|
GetSettings() (model2.AuthentikCredentialsDBModel, error)
|
|
ValidateToken(clientId string, clientSecret string, accessToken string, baseURL string) (model2.AuthentikToken, error)
|
|
HealthCheck(baseURL string) (string, error)
|
|
}
|
|
|
|
type authentikService struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
var (
|
|
APICorePrefix = "/api/v3/core"
|
|
)
|
|
|
|
func (a *authentikService) CreateSettings(m model2.AuthentikCredentialsDBModel) model2.AuthentikCredentialsDBModel {
|
|
a.db.Create(&m)
|
|
return m
|
|
}
|
|
func (a *authentikService) UpdateSettings(m model2.AuthentikCredentialsDBModel) (model2.AuthentikCredentialsDBModel, error) {
|
|
// Find the first matching record
|
|
var existing model2.AuthentikCredentialsDBModel
|
|
result := a.db.First(&existing)
|
|
if result.Error != nil {
|
|
if result.Error.Error() == "record not found" {
|
|
a.db.Create(&m)
|
|
return existing, nil
|
|
}
|
|
return existing, result.Error
|
|
}
|
|
|
|
// Update the existing record
|
|
existing.ClientID = m.ClientID
|
|
existing.ClientSecret = m.ClientSecret
|
|
existing.Issuer = m.Issuer
|
|
existing.AuthUrl = m.AuthUrl
|
|
existing.CallbackUrl = m.CallbackUrl
|
|
|
|
// Save the updated record
|
|
result = a.db.Save(&existing)
|
|
if result.Error != nil {
|
|
return existing, result.Error
|
|
}
|
|
|
|
return existing, nil
|
|
}
|
|
func (a *authentikService) GetSettings() (model2.AuthentikCredentialsDBModel, error) {
|
|
var m model2.AuthentikCredentialsDBModel
|
|
result := a.db.First(&m)
|
|
if result.Error != nil {
|
|
return model2.AuthentikCredentialsDBModel{}, result.Error
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
func (a *authentikService) HealthCheck(baseURL string) (string, error) {
|
|
// Check health/live first
|
|
pathLive := baseURL + "/-/health/live/"
|
|
reqLive, err := http.NewRequest("GET", pathLive, nil)
|
|
if err != nil {
|
|
log.Println("Error creating health/live request:", err)
|
|
return "Offline", err
|
|
}
|
|
|
|
client := &http.Client{
|
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
|
return nil // Always follow redirects
|
|
},
|
|
}
|
|
|
|
respLive, err := client.Do(reqLive)
|
|
if err != nil {
|
|
log.Println("Error on health/live request:", err)
|
|
return "Offline", err // Exit if the request fails
|
|
}
|
|
defer respLive.Body.Close()
|
|
|
|
// Check if health/live is 204 before proceeding
|
|
if respLive.StatusCode == http.StatusNoContent {
|
|
// Now check health/ready
|
|
pathReady := baseURL + "/-/health/ready/"
|
|
reqReady, err := http.NewRequest("GET", pathReady, nil)
|
|
if err != nil {
|
|
log.Println("Error creating health/ready request:", err)
|
|
return "Offline", err
|
|
}
|
|
|
|
respReady, err := client.Do(reqReady)
|
|
if err != nil {
|
|
log.Println("Error on health/ready request:", err)
|
|
return "Offline", err
|
|
}
|
|
defer respReady.Body.Close()
|
|
|
|
if respReady.StatusCode != http.StatusNoContent {
|
|
log.Println("HTTP error on health/ready:", respReady.Status)
|
|
return "Starting", nil
|
|
} else {
|
|
log.Println("Authentik is fully healthy!")
|
|
return "Live", nil
|
|
}
|
|
|
|
} else {
|
|
log.Println("HTTP error on health/live:", respLive.Status)
|
|
return "Offline", err
|
|
}
|
|
}
|
|
func (a *authentikService) ValidateToken(clientId string, clientSecret string, accessToken string, baseURL string) (model2.AuthentikToken, error) {
|
|
auth := clientId + ":" + clientSecret
|
|
basicAuth := "Basic " + base64.StdEncoding.EncodeToString([]byte(auth))
|
|
path := baseURL + "/application/o/introspect/"
|
|
formData := url.Values{}
|
|
formData.Set("token", accessToken)
|
|
reqBody := strings.NewReader(formData.Encode())
|
|
req, err := http.NewRequest("POST", path, reqBody)
|
|
if err != nil {
|
|
return model2.AuthentikToken{}, fmt.Errorf("error creating request: %v", err)
|
|
}
|
|
req.Header.Set("Authorization", basicAuth)
|
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
|
req.Header.Add("Accept", "application/json")
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return model2.AuthentikToken{}, fmt.Errorf("error making request: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
|
log.Println("HTTP error:", resp.Status)
|
|
return model2.AuthentikToken{}, fmt.Errorf("HTTP error: %s", resp.Status)
|
|
}
|
|
var token model2.AuthentikToken
|
|
if err := json.NewDecoder(resp.Body).Decode(&token); err != nil {
|
|
log.Println("Error decoding response:", err)
|
|
return model2.AuthentikToken{}, err
|
|
}
|
|
return token, nil
|
|
}
|
|
func (a *authentikService) GetUserApp(accessToken string, baseURL string) (model2.AuthentikApplication, error) {
|
|
bearer := "Bearer " + accessToken
|
|
path := baseURL + APICorePrefix + "/applications/"
|
|
req, err := http.NewRequest("GET", path, nil)
|
|
if err != nil {
|
|
return model2.AuthentikApplication{}, err
|
|
}
|
|
req.Header.Set("Authorization", bearer)
|
|
req.Header.Add("Accept", "application/json")
|
|
client := &http.Client{
|
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
|
// Always follow redirects
|
|
return nil
|
|
},
|
|
}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
log.Println("Error on request:", err)
|
|
return model2.AuthentikApplication{}, err
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
|
log.Println("HTTP error:", resp.Status)
|
|
return model2.AuthentikApplication{}, fmt.Errorf("HTTP error: %s", resp.Status)
|
|
}
|
|
|
|
var app model2.AuthentikApplication
|
|
if err := json.NewDecoder(resp.Body).Decode(&app); err != nil {
|
|
log.Println("Error decoding response:", err)
|
|
return model2.AuthentikApplication{}, err
|
|
}
|
|
|
|
return app, nil
|
|
|
|
}
|
|
func (a *authentikService) GetUserInfo(accessToken string, baseURL string) (model2.AuthentikUser, error) {
|
|
bearer := "Bearer " + accessToken
|
|
path := baseURL + APICorePrefix + "/users/me/"
|
|
req, err := http.NewRequest("GET", path, nil) // No need for bytes.NewBuffer(nil) for GET requests without a body
|
|
if err != nil {
|
|
return model2.AuthentikUser{}, err
|
|
}
|
|
req.Header.Set("Authorization", bearer)
|
|
req.Header.Add("Accept", "application/json")
|
|
|
|
client := &http.Client{
|
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
|
// Always follow redirects
|
|
return nil
|
|
},
|
|
}
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
log.Println("Error on request:", err)
|
|
return model2.AuthentikUser{}, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
|
log.Println("HTTP error:", resp.Status)
|
|
return model2.AuthentikUser{}, fmt.Errorf("HTTP error: %s", resp.Status)
|
|
}
|
|
|
|
var user model2.AuthentikUser
|
|
if err := json.NewDecoder(resp.Body).Decode(&user); err != nil {
|
|
log.Println("Error decoding response:", err)
|
|
return model2.AuthentikUser{}, err
|
|
}
|
|
|
|
return user, nil
|
|
}
|
|
func NewAuthentikService(db *gorm.DB) AuthentikService {
|
|
return &authentikService{
|
|
db: db,
|
|
}
|
|
}
|