Files
2026-08-29 17:02:42 +03:00

69 lines
2.7 KiB
Go

package domain
import (
"crypto/hmac"
"crypto/sha256"
"crypto/subtle"
"encoding/base64"
"encoding/json"
"errors"
"strings"
"time"
"git.cin.su/m/Assistant/contracts/identity"
)
var ErrInvalidCreatorSession = errors.New("invalid creator session")
func IssueCreatorSession(secret []byte, now time.Time, ttl time.Duration) (string, identity.CreatorSession, error) {
return IssueCreatorSessionForBinding(secret, identity.ActorID("domovoy-creator-actor"), identity.PrincipalID("domovoy-creator-principal"), now, ttl)
}
func IssueCreatorSessionForBinding(secret []byte, actorID identity.ActorID, principalID identity.PrincipalID, now time.Time, ttl time.Duration) (string, identity.CreatorSession, error) {
if len(secret) < 32 || actorID == "" || principalID == "" || ttl <= 0 {
return "", identity.CreatorSession{}, ErrInvalidCreatorSession
}
session := identity.CreatorSession{ActorID: actorID, PrincipalID: principalID, ExpiresAtUnix: now.Add(ttl).Unix()}
payload, err := json.Marshal(session)
if err != nil {
return "", identity.CreatorSession{}, err
}
encoded := base64.RawURLEncoding.EncodeToString(payload)
return encoded + "." + signature(secret, encoded), session, nil
}
func ValidateCreatorSession(secret []byte, token string, now time.Time) (identity.CreatorSession, error) {
parts := strings.Split(token, ".")
if len(parts) != 2 || len(secret) < 32 || subtle.ConstantTimeCompare([]byte(signature(secret, parts[0])), []byte(parts[1])) != 1 {
return identity.CreatorSession{}, ErrInvalidCreatorSession
}
payload, err := base64.RawURLEncoding.DecodeString(parts[0])
if err != nil {
return identity.CreatorSession{}, ErrInvalidCreatorSession
}
var session identity.CreatorSession
if json.Unmarshal(payload, &session) != nil || session.ActorID == "" || session.PrincipalID == "" || now.Unix() >= session.ExpiresAtUnix {
return identity.CreatorSession{}, ErrInvalidCreatorSession
}
return session, nil
}
func BootstrapMatches(expected, supplied string) bool {
return expected != "" && supplied != "" && subtle.ConstantTimeCompare([]byte(expected), []byte(supplied)) == 1
}
func CreatorCredentialsMatch(expectedUsername, expectedPassword string, supplied identity.CreatorCredentials) bool {
if expectedUsername == "" || expectedPassword == "" || supplied.Username == "" || supplied.Password == "" {
return false
}
usernameMatches := subtle.ConstantTimeCompare([]byte(expectedUsername), []byte(supplied.Username))
passwordMatches := subtle.ConstantTimeCompare([]byte(expectedPassword), []byte(supplied.Password))
return usernameMatches&passwordMatches == 1
}
func signature(secret []byte, payload string) string {
mac := hmac.New(sha256.New, secret)
mac.Write([]byte(payload))
return base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
}