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 }