forked from Nixius/authelia
115 lines
2.6 KiB
Go
115 lines
2.6 KiB
Go
package ldap
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type gqlClient struct {
|
|
baseURL string
|
|
username string
|
|
password string
|
|
token string
|
|
mu sync.Mutex
|
|
client *http.Client
|
|
}
|
|
|
|
type gqlRequest struct {
|
|
Query string `json:"query"`
|
|
Variables map[string]any `json:"variables,omitempty"`
|
|
}
|
|
|
|
type gqlResponse struct {
|
|
Data json.RawMessage `json:"data"`
|
|
Errors []struct {
|
|
Message string `json:"message"`
|
|
} `json:"errors"`
|
|
}
|
|
|
|
func newGQLClient(baseURL, username, password string) *gqlClient {
|
|
return &gqlClient{
|
|
baseURL: baseURL,
|
|
username: username,
|
|
password: password,
|
|
client: &http.Client{Timeout: 10 * time.Second},
|
|
}
|
|
}
|
|
|
|
func (g *gqlClient) authenticate() error {
|
|
body, _ := json.Marshal(map[string]string{
|
|
"username": g.username,
|
|
"password": g.password,
|
|
})
|
|
resp, err := g.client.Post(g.baseURL+"/auth/simple/login", "application/json", bytes.NewReader(body))
|
|
if err != nil {
|
|
return fmt.Errorf("lldap auth: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
var result struct {
|
|
Token string `json:"token"`
|
|
}
|
|
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
|
return fmt.Errorf("lldap auth decode: %w", err)
|
|
}
|
|
if result.Token == "" {
|
|
return fmt.Errorf("lldap auth: empty token")
|
|
}
|
|
g.token = result.Token
|
|
return nil
|
|
}
|
|
|
|
func (g *gqlClient) exec(query string, variables map[string]any) (json.RawMessage, error) {
|
|
g.mu.Lock()
|
|
defer g.mu.Unlock()
|
|
|
|
if g.token == "" {
|
|
if err := g.authenticate(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
data, err := g.doRequest(query, variables)
|
|
if err != nil {
|
|
if err := g.authenticate(); err != nil {
|
|
return nil, err
|
|
}
|
|
data, err = g.doRequest(query, variables)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
func (g *gqlClient) doRequest(query string, variables map[string]any) (json.RawMessage, error) {
|
|
reqBody, _ := json.Marshal(gqlRequest{Query: query, Variables: variables})
|
|
req, err := http.NewRequest("POST", g.baseURL+"/api/graphql", bytes.NewReader(reqBody))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("Authorization", "Bearer "+g.token)
|
|
|
|
resp, err := g.client.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("lldap graphql: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
respBody, _ := io.ReadAll(resp.Body)
|
|
var gqlResp gqlResponse
|
|
if err := json.Unmarshal(respBody, &gqlResp); err != nil {
|
|
return nil, fmt.Errorf("lldap graphql decode: %w", err)
|
|
}
|
|
if len(gqlResp.Errors) > 0 {
|
|
return nil, fmt.Errorf("lldap graphql: %s", gqlResp.Errors[0].Message)
|
|
}
|
|
return gqlResp.Data, nil
|
|
}
|