All import paths updated to git.wntrmute.dev/mc/. Bumps mcdsl to v1.2.0. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
133 lines
4.2 KiB
Go
133 lines
4.2 KiB
Go
package grpcserver
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
|
|
pb "git.wntrmute.dev/mc/metacrypt/gen/metacrypt/v2"
|
|
internacme "git.wntrmute.dev/mc/metacrypt/internal/acme"
|
|
"git.wntrmute.dev/mc/metacrypt/internal/auth"
|
|
"git.wntrmute.dev/mc/metacrypt/internal/engine"
|
|
)
|
|
|
|
type acmeServer struct {
|
|
pb.UnimplementedACMEServiceServer
|
|
s *GRPCServer
|
|
}
|
|
|
|
func (as *acmeServer) CreateEAB(ctx context.Context, req *pb.CreateEABRequest) (*pb.CreateEABResponse, error) {
|
|
ti := auth.TokenInfoFromContext(ctx)
|
|
h, err := as.getOrCreateHandler(req.Mount)
|
|
if err != nil {
|
|
return nil, status.Error(codes.NotFound, "mount not found")
|
|
}
|
|
cred, err := h.CreateEAB(ctx, ti.Username)
|
|
if err != nil {
|
|
as.s.logger.Error("grpc: acme create EAB", "error", err)
|
|
return nil, status.Error(codes.Internal, "failed to create EAB credentials")
|
|
}
|
|
return &pb.CreateEABResponse{Kid: cred.KID, HmacKey: cred.HMACKey}, nil
|
|
}
|
|
|
|
func (as *acmeServer) SetConfig(ctx context.Context, req *pb.SetConfigRequest) (*pb.SetConfigResponse, error) {
|
|
if req.DefaultIssuer == "" {
|
|
return nil, status.Error(codes.InvalidArgument, "default_issuer is required")
|
|
}
|
|
// Verify mount exists.
|
|
if _, err := as.getOrCreateHandler(req.Mount); err != nil {
|
|
return nil, status.Error(codes.NotFound, "mount not found")
|
|
}
|
|
cfg := &internacme.ACMEConfig{DefaultIssuer: req.DefaultIssuer}
|
|
data, _ := json.Marshal(cfg)
|
|
barrierPath := "acme/" + req.Mount + "/config.json"
|
|
if err := as.s.sealMgr.Barrier().Put(ctx, barrierPath, data); err != nil {
|
|
as.s.logger.Error("grpc: acme set config", "error", err)
|
|
return nil, status.Error(codes.Internal, "failed to save config")
|
|
}
|
|
return &pb.SetConfigResponse{}, nil
|
|
}
|
|
|
|
func (as *acmeServer) ListAccounts(ctx context.Context, req *pb.ListAccountsRequest) (*pb.ListAccountsResponse, error) {
|
|
h, err := as.getOrCreateHandler(req.Mount)
|
|
if err != nil {
|
|
return nil, status.Error(codes.NotFound, "mount not found")
|
|
}
|
|
accounts, err := h.ListAccounts(ctx)
|
|
if err != nil {
|
|
as.s.logger.Error("grpc: acme list accounts", "error", err)
|
|
return nil, status.Error(codes.Internal, "internal error")
|
|
}
|
|
pbAccounts := make([]*pb.ACMEAccount, 0, len(accounts))
|
|
for _, a := range accounts {
|
|
contacts := make([]string, len(a.Contact))
|
|
copy(contacts, a.Contact)
|
|
pbAccounts = append(pbAccounts, &pb.ACMEAccount{
|
|
Id: a.ID,
|
|
Status: a.Status,
|
|
Contact: contacts,
|
|
MciasUsername: a.MCIASUsername,
|
|
CreatedAt: timestamppb.New(a.CreatedAt),
|
|
})
|
|
}
|
|
return &pb.ListAccountsResponse{Accounts: pbAccounts}, nil
|
|
}
|
|
|
|
func (as *acmeServer) ListOrders(ctx context.Context, req *pb.ListOrdersRequest) (*pb.ListOrdersResponse, error) {
|
|
h, err := as.getOrCreateHandler(req.Mount)
|
|
if err != nil {
|
|
return nil, status.Error(codes.NotFound, "mount not found")
|
|
}
|
|
orders, err := h.ListOrders(ctx)
|
|
if err != nil {
|
|
as.s.logger.Error("grpc: acme list orders", "error", err)
|
|
return nil, status.Error(codes.Internal, "internal error")
|
|
}
|
|
pbOrders := make([]*pb.ACMEOrder, 0, len(orders))
|
|
for _, o := range orders {
|
|
identifiers := make([]string, 0, len(o.Identifiers))
|
|
for _, id := range o.Identifiers {
|
|
identifiers = append(identifiers, id.Type+":"+id.Value)
|
|
}
|
|
pbOrders = append(pbOrders, &pb.ACMEOrder{
|
|
Id: o.ID,
|
|
AccountId: o.AccountID,
|
|
Status: o.Status,
|
|
Identifiers: identifiers,
|
|
CreatedAt: timestamppb.New(o.CreatedAt),
|
|
ExpiresAt: timestamppb.New(o.ExpiresAt),
|
|
})
|
|
}
|
|
return &pb.ListOrdersResponse{Orders: pbOrders}, nil
|
|
}
|
|
|
|
func (as *acmeServer) getOrCreateHandler(mountName string) (*internacme.Handler, error) {
|
|
as.s.mu.Lock()
|
|
defer as.s.mu.Unlock()
|
|
|
|
// Verify mount is a CA engine.
|
|
mount, err := as.s.engines.GetMount(mountName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if mount.Type != engine.EngineTypeCA {
|
|
return nil, engine.ErrMountNotFound
|
|
}
|
|
|
|
// Check handler cache on GRPCServer.
|
|
if h, ok := as.s.acmeHandlers[mountName]; ok {
|
|
return h, nil
|
|
}
|
|
|
|
baseURL := as.s.cfg.Server.ExternalURL
|
|
if baseURL == "" {
|
|
baseURL = "https://" + as.s.cfg.Server.ListenAddr
|
|
}
|
|
h := internacme.NewHandler(mountName, as.s.sealMgr.Barrier(), as.s.engines, baseURL, as.s.logger)
|
|
as.s.acmeHandlers[mountName] = h
|
|
return h, nil
|
|
}
|