NextZen-UserService/service/authentik.go

230 lines
6.9 KiB
Go
Raw Permalink Normal View History

package service
2024-08-13 12:19:14 +07:00
import (
2024-08-14 18:52:21 +07:00
"encoding/base64"
2024-08-14 10:00:32 +07:00
"encoding/json"
2024-08-13 12:19:14 +07:00
"fmt"
"log"
"net/http"
2024-08-14 18:52:21 +07:00
"net/url"
"strings"
2024-08-13 12:19:14 +07:00
model2 "github.com/KaySar12/NextZen-UserService/service/model"
2024-08-14 12:06:18 +07:00
"gorm.io/gorm"
2024-08-13 12:19:14 +07:00
)
type AuthentikService interface {
2024-08-14 10:00:32 +07:00
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)
2024-08-14 18:52:21 +07:00
ValidateToken(clientId string, clientSecret string, accessToken string, baseURL string) (model2.AuthentikToken, error)
HealthCheck(baseURL string) (string, error)
}
type authentikService struct {
2024-08-14 12:06:18 +07:00
db *gorm.DB
}
2024-08-14 10:00:32 +07:00
var (
APICorePrefix = "/api/v3/core"
)
func (a *authentikService) CreateSettings(m model2.AuthentikCredentialsDBModel) model2.AuthentikCredentialsDBModel {
2024-08-14 12:06:18 +07:00
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 {
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
2024-08-14 12:06:18 +07:00
}
func (a *authentikService) GetSettings() (model2.AuthentikCredentialsDBModel, error) {
2024-08-14 12:06:18 +07:00
var m model2.AuthentikCredentialsDBModel
result := a.db.First(&m)
if result.Error != nil {
return model2.AuthentikCredentialsDBModel{}, result.Error
}
return m, nil
2024-08-14 12:06:18 +07:00
}
2024-10-02 10:49:27 +07:00
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
}
}
2024-08-14 18:52:21 +07:00
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)
2024-08-14 18:52:21 +07:00
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
}
2024-08-14 10:00:32 +07:00
func (a *authentikService) GetUserApp(accessToken string, baseURL string) (model2.AuthentikApplication, error) {
2024-08-13 12:19:14 +07:00
bearer := "Bearer " + accessToken
2024-08-14 10:00:32 +07:00
path := baseURL + APICorePrefix + "/applications/"
req, err := http.NewRequest("GET", path, nil)
if err != nil {
return model2.AuthentikApplication{}, err
}
2024-08-13 12:19:14 +07:00
req.Header.Set("Authorization", bearer)
req.Header.Add("Accept", "application/json")
2024-08-14 10:00:32 +07:00
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
// Always follow redirects
return nil
},
2024-08-13 12:19:14 +07:00
}
resp, err := client.Do(req)
if err != nil {
2024-08-14 10:00:32 +07:00
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
2024-08-13 12:19:14 +07:00
}
2024-08-14 10:00:32 +07:00
return app, nil
2024-08-13 12:19:14 +07:00
}
2024-08-14 10:00:32 +07:00
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
}
2024-08-14 12:06:18 +07:00
func NewAuthentikService(db *gorm.DB) AuthentikService {
return &authentikService{
db: db,
}
}