- Updated .env.example to include new configuration options for the UltiAI branding and API endpoints. - Enhanced Nginx configuration to support new API routes for the MCP and WebSocket connections. - Introduced sub-filters for branding adjustments in Nginx responses. - Added new JavaScript patch for API endpoint adjustments. - Implemented tests for new API functionalities and improved error handling in the AI gateway.
94 lines
2.3 KiB
Go
94 lines
2.3 KiB
Go
package authentik
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
type akUser struct {
|
|
PK int `json:"pk"`
|
|
UUID string `json:"uuid"`
|
|
Attributes map[string]any `json:"attributes"`
|
|
}
|
|
|
|
const avatarAttributeKey = "avatar"
|
|
|
|
// FindUserByUUID returns the Authentik core user for an OIDC subject (uuid).
|
|
func (c *Client) FindUserByUUID(ctx context.Context, userUUID string) (*akUser, bool, error) {
|
|
userUUID = strings.TrimSpace(userUUID)
|
|
if userUUID == "" {
|
|
return nil, false, nil
|
|
}
|
|
q := url.Values{}
|
|
q.Set("uuid", userUUID)
|
|
var out listResponse[akUser]
|
|
if err := c.getJSON(ctx, "/api/v3/core/users/?"+q.Encode(), &out); err != nil {
|
|
return nil, false, err
|
|
}
|
|
if len(out.Results) == 0 {
|
|
return nil, false, nil
|
|
}
|
|
user := out.Results[0]
|
|
if user.Attributes == nil {
|
|
user.Attributes = map[string]any{}
|
|
}
|
|
return &user, true, nil
|
|
}
|
|
|
|
// GetUserAvatarAttribute reads attributes.avatar from Authentik.
|
|
func (c *Client) GetUserAvatarAttribute(ctx context.Context, userUUID string) (string, error) {
|
|
user, found, err := c.FindUserByUUID(ctx, userUUID)
|
|
if err != nil || !found {
|
|
return "", err
|
|
}
|
|
return avatarAttributeString(user.Attributes[avatarAttributeKey]), nil
|
|
}
|
|
|
|
// SetUserAvatarAttribute writes attributes.avatar on the Authentik user.
|
|
func (c *Client) SetUserAvatarAttribute(ctx context.Context, userUUID, avatarURL string) error {
|
|
user, found, err := c.FindUserByUUID(ctx, userUUID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !found {
|
|
return fmt.Errorf("authentik user not found: %s", userUUID)
|
|
}
|
|
attrs := cloneAttributes(user.Attributes)
|
|
avatarURL = strings.TrimSpace(avatarURL)
|
|
if avatarURL == "" {
|
|
delete(attrs, avatarAttributeKey)
|
|
} else {
|
|
attrs[avatarAttributeKey] = avatarURL
|
|
}
|
|
return c.patchJSON(ctx, fmt.Sprintf("/api/v3/core/users/%d/", user.PK), map[string]any{
|
|
"attributes": attrs,
|
|
})
|
|
}
|
|
|
|
func cloneAttributes(src map[string]any) map[string]any {
|
|
if len(src) == 0 {
|
|
return map[string]any{}
|
|
}
|
|
dst := make(map[string]any, len(src))
|
|
for k, v := range src {
|
|
dst[k] = v
|
|
}
|
|
return dst
|
|
}
|
|
|
|
func avatarAttributeString(raw any) string {
|
|
switch v := raw.(type) {
|
|
case string:
|
|
return strings.TrimSpace(v)
|
|
case []byte:
|
|
return strings.TrimSpace(string(v))
|
|
default:
|
|
if raw == nil {
|
|
return ""
|
|
}
|
|
return strings.TrimSpace(fmt.Sprint(raw))
|
|
}
|
|
}
|