NextZen-UserService/service/authentik.go
2024-08-14 18:52:21 +07:00

154 lines
4.9 KiB
Go

package service
import (
"encoding/base64"
"encoding/json"
"fmt"
"log"
"net/http"
"net/url"
"strings"
model2 "github.com/IceWhaleTech/CasaOS-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)
CreateCredential(m model2.AuthentikCredentialsDBModel) model2.AuthentikCredentialsDBModel
UpdateCredential(m model2.AuthentikCredentialsDBModel) model2.AuthentikCredentialsDBModel
GetCredential(id int) model2.AuthentikCredentialsDBModel
ValidateToken(clientId string, clientSecret string, accessToken string, baseURL string) (model2.AuthentikToken, error)
}
type authentikService struct {
db *gorm.DB
}
var (
APICorePrefix = "/api/v3/core"
)
func (a *authentikService) CreateCredential(m model2.AuthentikCredentialsDBModel) model2.AuthentikCredentialsDBModel {
a.db.Create(&m)
return m
}
func (a *authentikService) UpdateCredential(m model2.AuthentikCredentialsDBModel) model2.AuthentikCredentialsDBModel {
a.db.Model(&m).Where("id = ?", m.Id).Updates(m)
return m
}
func (a *authentikService) GetCredential(id int) model2.AuthentikCredentialsDBModel {
var m model2.AuthentikCredentialsDBModel
a.db.Limit(1).Where("id = ?", id).First(&m)
return m
}
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)
responseBody := strings.NewReader(formData.Encode())
req, err := http.NewRequest("POST", path, responseBody)
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,
}
}