222 lines
4.8 KiB
Go
222 lines
4.8 KiB
Go
// Copyright (C) 2023 Marius Schellenberger
|
|
|
|
package schema
|
|
|
|
import (
|
|
"cmp"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"slices"
|
|
|
|
"git.giftfish.de/ston1th/keyctl/pkg/api/types"
|
|
"git.giftfish.de/ston1th/keyctl/pkg/core"
|
|
)
|
|
|
|
const (
|
|
Version = "v1"
|
|
KeyName = "key"
|
|
ReqName = "req"
|
|
KeyPath = "/" + Version + "/" + KeyName
|
|
ReqPath = "/" + Version + "/" + ReqName
|
|
SSH = "ssh"
|
|
AccessPath = "/" + Version + "/" + SSH
|
|
)
|
|
|
|
type NewKey struct {
|
|
Name string `json:"name"`
|
|
Existing string `json:"existing,omitempty"`
|
|
Size int `json:"size"`
|
|
Encoding core.Encoding `json:"encoding"`
|
|
Type core.Type `json:"type"`
|
|
}
|
|
|
|
var (
|
|
ErrEmptyKeyName = errors.New("empty key name")
|
|
ErrExistingKeyType = errors.New("invalid key type for existing key")
|
|
ErrExistingKeyEncoding = errors.New("invalid key encoding for existing key")
|
|
ErrInvalidKeyName = errors.New("invalid key name")
|
|
ErrInvalidKeySize = errors.New("invalid key size")
|
|
)
|
|
|
|
const (
|
|
MinKeySize = 1
|
|
MaxKeySize = 1024
|
|
)
|
|
|
|
func (nk NewKey) Validate() error {
|
|
if nk.Name == "" {
|
|
return ErrEmptyKeyName
|
|
}
|
|
if nk.Existing != "" {
|
|
if nk.Type == core.Xor {
|
|
return ErrExistingKeyType
|
|
}
|
|
_, err := hex.DecodeString(nk.Existing)
|
|
if err != nil {
|
|
return ErrExistingKeyEncoding
|
|
}
|
|
}
|
|
if core.NameRe.MatchString(nk.Name) {
|
|
return ErrInvalidKeyName
|
|
}
|
|
if nk.Size > MaxKeySize || nk.Size < MinKeySize {
|
|
return ErrInvalidKeySize
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ApproveURL(id, aid string) string {
|
|
return KeyPath + "/" + id + "/" + aid
|
|
}
|
|
|
|
func AccessURL(host, user, aid string) string {
|
|
return AccessPath + "/" + host + "/" + user + "/" + aid
|
|
}
|
|
|
|
type Key struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Size int `json:"size"`
|
|
Encoding core.Encoding `json:"encoding"`
|
|
Type core.Type `json:"type"`
|
|
Key string `json:"key,omitempty"`
|
|
}
|
|
|
|
func (k Key) Print(share string) (string, error) {
|
|
switch k.Type {
|
|
case core.Shamir:
|
|
part1, err := k.Encoding.Decode(k.Key)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
part2, err := hex.DecodeString(share)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
dec, err := k.Type.Decode(append(part1, part2...))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return k.Encoding.EncodeToString(dec), nil
|
|
}
|
|
return k.Key, nil
|
|
}
|
|
|
|
type Keys []Key
|
|
|
|
func (Keys) Less(a, b Key) int { return cmp.Compare(a.Name, b.Name) }
|
|
func (k Keys) Sort() { slices.SortStableFunc(k, k.Less) }
|
|
func (k Keys) Len() int { return len(k) }
|
|
func (k Keys) Fields() string {
|
|
return "Key ID\t Name\t Size\t Encoding\t Type"
|
|
}
|
|
func (k Keys) Values() []string {
|
|
vals := make([]string, len(k))
|
|
for i, v := range k {
|
|
vals[i] = fmt.Sprintf("%s\t %s\t %d\t %s\t %s",
|
|
v.ID,
|
|
v.Name,
|
|
v.Size,
|
|
v.Encoding,
|
|
v.Type,
|
|
)
|
|
}
|
|
return vals
|
|
}
|
|
|
|
func NewKeys(keys core.Keys) (k Keys) {
|
|
for _, key := range keys {
|
|
k = append(k, Key(key))
|
|
}
|
|
k.Sort()
|
|
return
|
|
}
|
|
|
|
type Approval struct {
|
|
ID string `json:"id"`
|
|
AID string `json:"aid"`
|
|
Name string `json:"name"`
|
|
IP string `json:"ip"`
|
|
Status types.Status `json:"status"`
|
|
}
|
|
|
|
type Approvals []Approval
|
|
|
|
func (Approvals) Less(a, b Approval) int { return cmp.Compare(a.Name, b.Name) }
|
|
func (a Approvals) Sort() { slices.SortStableFunc(a, a.Less) }
|
|
func (a Approvals) Len() int { return len(a) }
|
|
func (a Approvals) Fields() string {
|
|
return "Key ID\t Approval ID\t Name\t Status\t IP"
|
|
}
|
|
func (a Approvals) Values() []string {
|
|
vals := make([]string, len(a))
|
|
for i, v := range a {
|
|
vals[i] = fmt.Sprintf("%s\t %s\t %s\t %s\t %s",
|
|
v.ID,
|
|
v.AID,
|
|
v.Name,
|
|
v.Status,
|
|
v.IP,
|
|
)
|
|
}
|
|
return vals
|
|
}
|
|
|
|
func NewApprovals(m types.ApprovalMap) (a Approvals) {
|
|
for k, v := range m {
|
|
a = append(a, Approval{
|
|
ID: v.ID,
|
|
AID: k,
|
|
Name: v.Name,
|
|
IP: v.IP,
|
|
Status: v.Status,
|
|
})
|
|
}
|
|
a.Sort()
|
|
return
|
|
}
|
|
|
|
type Access struct {
|
|
Host string `json:"host"`
|
|
User string `json:"user"`
|
|
AID string `json:"aid"`
|
|
IP string `json:"ip"`
|
|
Status types.Status `json:"status"`
|
|
}
|
|
|
|
type AccessList []Access
|
|
|
|
func (AccessList) Less(a, b Access) int { return cmp.Compare(a.Host, b.Host) }
|
|
func (a AccessList) Sort() { slices.SortStableFunc(a, a.Less) }
|
|
func (a AccessList) Len() int { return len(a) }
|
|
func (a AccessList) Fields() string {
|
|
return "Host\t User\t Approval ID\t IP\t Status"
|
|
}
|
|
func (a AccessList) Values() []string {
|
|
vals := make([]string, len(a))
|
|
for i, v := range a {
|
|
vals[i] = fmt.Sprintf("%s\t %s\t %s\t %s\t %s",
|
|
v.Host,
|
|
v.User,
|
|
v.AID,
|
|
v.IP,
|
|
v.Status,
|
|
)
|
|
}
|
|
return vals
|
|
}
|
|
|
|
func NewAccessList(m types.SSHMap) (a AccessList) {
|
|
for k, v := range m {
|
|
a = append(a, Access{
|
|
Host: v.Host,
|
|
User: v.User,
|
|
AID: k,
|
|
IP: v.IP,
|
|
Status: v.Status,
|
|
})
|
|
}
|
|
a.Sort()
|
|
return
|
|
}
|