mirror of
https://gitea.com/gitea/tea
synced 2026-08-05 08:22:56 +02:00
- Embed the minimal credstore subset used by tea (SecureStore, EncryptedFileStore, KeyringStore, FileStore) as modules/credstore so external SDK renames can no longer break the build - Keep the on-disk format fully compatible: AES-256-GCM values with the v1: prefix, credentials.json / credentials.json.enc paths, and the Token JSON field names are unchanged, verified by a ciphertext fixture generated with sdk-go v1.1.0 - Store the keyring master key under a tea-owned account name - Reuse the existing kernel-level filelock module instead of the upstream lockfile protocol, removing a stale-lock race - Cover roundtrip, keyring-unavailable fallback, and fixture decryption with tests using a mocked keyring - Remove github.com/go-signet/sdk-go and promote github.com/zalando/go-keyring to a direct dependency Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
58 lines
2.0 KiB
Go
58 lines
2.0 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package credstore
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
)
|
|
|
|
// ErrNotFound indicates that no data was found for the given client ID.
|
|
var ErrNotFound = errors.New("not found")
|
|
|
|
// ErrEmptyClientID is returned when an empty client ID is passed to Save.
|
|
var ErrEmptyClientID = errors.New("client ID cannot be empty")
|
|
|
|
// Store defines the interface for loading, saving, and deleting data by client ID.
|
|
type Store[T any] interface {
|
|
Load(clientID string) (T, error)
|
|
Save(clientID string, data T) error
|
|
Delete(clientID string) error
|
|
String() string
|
|
}
|
|
|
|
// Token represents saved tokens for a specific client.
|
|
type Token struct {
|
|
AccessToken string `json:"access_token"`
|
|
RefreshToken string `json:"refresh_token"`
|
|
TokenType string `json:"token_type"`
|
|
Scope string `json:"scope,omitempty"`
|
|
IDToken string `json:"id_token,omitempty"`
|
|
ExpiresAt time.Time `json:"expires_at"`
|
|
ClientID string `json:"client_id"`
|
|
}
|
|
|
|
// IsExpired reports whether the token has expired.
|
|
// Returns false if ExpiresAt is zero (token has no expiry).
|
|
func (t *Token) IsExpired() bool {
|
|
return !t.ExpiresAt.IsZero() && time.Now().After(t.ExpiresAt)
|
|
}
|
|
|
|
// IsValid reports whether the token has a non-empty access token and is not expired.
|
|
func (t *Token) IsValid() bool {
|
|
return t.AccessToken != "" && !t.IsExpired()
|
|
}
|
|
|
|
// NewStringKeyringStore creates a KeyringStore for plain string values.
|
|
func NewStringKeyringStore(serviceName string) *KeyringStore[string] {
|
|
return NewKeyringStore[string](serviceName, StringCodec{})
|
|
}
|
|
|
|
// DefaultTokenSecureStore creates a SecureStore for Token values with sensible defaults.
|
|
// Tokens are AES-256-GCM-encrypted to filePath+".enc" with the master key in
|
|
// the OS keyring; see DefaultSecureStore for details.
|
|
func DefaultTokenSecureStore(serviceName, filePath string) *SecureStore[Token] {
|
|
return DefaultSecureStore[Token](serviceName, filePath, JSONCodec[Token]{})
|
|
}
|