136 lines
4.1 KiB
Go
136 lines
4.1 KiB
Go
package state
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
)
|
|
|
|
const FileName = "identity.json"
|
|
|
|
type Identity struct {
|
|
NodeID string `json:"node_id"`
|
|
ClusterID string `json:"cluster_id"`
|
|
NodeName string `json:"node_name"`
|
|
NodeFingerprint string `json:"node_fingerprint"`
|
|
PublicKey string `json:"public_key"`
|
|
IdentityStatus string `json:"identity_status"`
|
|
PendingJoinRequestID string `json:"pending_join_request_id,omitempty"`
|
|
ClusterAuthorityPublicKey string `json:"cluster_authority_public_key,omitempty"`
|
|
ClusterAuthorityFingerprint string `json:"cluster_authority_fingerprint,omitempty"`
|
|
ClusterAuthorityQuorum json.RawMessage `json:"cluster_authority_quorum,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
func LoadOrCreate(dir, clusterID, nodeName string) (Identity, error) {
|
|
path := filepath.Join(dir, FileName)
|
|
existing, err := Load(path)
|
|
if err == nil {
|
|
return existing, nil
|
|
}
|
|
if !errors.Is(err, os.ErrNotExist) {
|
|
return Identity{}, err
|
|
}
|
|
now := time.Now().UTC()
|
|
fingerprint, err := randomToken("rap-node-fp")
|
|
if err != nil {
|
|
return Identity{}, err
|
|
}
|
|
publicKey, err := randomToken("rap-node-pub")
|
|
if err != nil {
|
|
return Identity{}, err
|
|
}
|
|
identity := Identity{
|
|
ClusterID: clusterID,
|
|
NodeName: nodeName,
|
|
NodeFingerprint: fingerprint,
|
|
PublicKey: publicKey,
|
|
IdentityStatus: "new",
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
if err := Save(path, identity); err != nil {
|
|
return Identity{}, err
|
|
}
|
|
return identity, nil
|
|
}
|
|
|
|
func Load(path string) (Identity, error) {
|
|
payload, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return Identity{}, err
|
|
}
|
|
var identity Identity
|
|
if err := json.Unmarshal(payload, &identity); err != nil {
|
|
return Identity{}, err
|
|
}
|
|
return identity, nil
|
|
}
|
|
|
|
func Save(path string, identity Identity) error {
|
|
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
|
|
return err
|
|
}
|
|
identity.UpdatedAt = time.Now().UTC()
|
|
payload, err := json.MarshalIndent(identity, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return os.WriteFile(path, payload, 0o600)
|
|
}
|
|
|
|
func MarkEnrollmentSubmitted(dir, clusterID, joinRequestID string) (Identity, error) {
|
|
path := filepath.Join(dir, FileName)
|
|
identity, err := Load(path)
|
|
if err != nil {
|
|
return Identity{}, err
|
|
}
|
|
identity.ClusterID = clusterID
|
|
identity.PendingJoinRequestID = joinRequestID
|
|
identity.IdentityStatus = "pending_approval"
|
|
if err := Save(path, identity); err != nil {
|
|
return Identity{}, err
|
|
}
|
|
return identity, nil
|
|
}
|
|
|
|
func MarkApproved(dir string, nodeID, clusterID, status string) (Identity, error) {
|
|
return MarkApprovedWithAuthority(dir, nodeID, clusterID, status, "", "")
|
|
}
|
|
|
|
func MarkApprovedWithAuthority(dir string, nodeID, clusterID, status, authorityPublicKey, authorityFingerprint string) (Identity, error) {
|
|
return MarkApprovedWithAuthorityAndQuorum(dir, nodeID, clusterID, status, authorityPublicKey, authorityFingerprint, nil)
|
|
}
|
|
|
|
func MarkApprovedWithAuthorityAndQuorum(dir string, nodeID, clusterID, status, authorityPublicKey, authorityFingerprint string, authorityQuorum json.RawMessage) (Identity, error) {
|
|
path := filepath.Join(dir, FileName)
|
|
identity, err := Load(path)
|
|
if err != nil {
|
|
return Identity{}, err
|
|
}
|
|
identity.NodeID = nodeID
|
|
identity.ClusterID = clusterID
|
|
identity.IdentityStatus = status
|
|
identity.PendingJoinRequestID = ""
|
|
identity.ClusterAuthorityPublicKey = authorityPublicKey
|
|
identity.ClusterAuthorityFingerprint = authorityFingerprint
|
|
identity.ClusterAuthorityQuorum = authorityQuorum
|
|
if err := Save(path, identity); err != nil {
|
|
return Identity{}, err
|
|
}
|
|
return identity, nil
|
|
}
|
|
|
|
func randomToken(prefix string) (string, error) {
|
|
var random [32]byte
|
|
if _, err := rand.Read(random[:]); err != nil {
|
|
return "", err
|
|
}
|
|
return prefix + "_" + base64.RawURLEncoding.EncodeToString(random[:]), nil
|
|
}
|