grpcctl: add auth login and policy commands

- Add auth/login and auth/logout to mciasgrpcctl, calling
  the existing AuthService.Login/Logout RPCs; password is
  always prompted interactively (term.ReadPassword), never
  accepted as a flag, raw bytes zeroed after use
- Add proto/mcias/v1/policy.proto with PolicyService
  (List, Create, Get, Update, Delete policy rules)
- Regenerate gen/mcias/v1/ stubs to include policy
- Implement internal/grpcserver/policyservice.go delegating
  to the same db layer as the REST policy handlers
- Register PolicyService in grpcserver.go
- Add policy list/create/get/update/delete to mciasgrpcctl
- Update mciasgrpcctl man page with new commands

Security: auth login uses the same interactive password
prompt pattern as mciasctl; password never appears in
process args, shell history, or logs; raw bytes zeroed
after string conversion (same as REST CLI and REST server).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 20:51:10 -07:00
parent cbcb1a0533
commit 7ede54afb2
21 changed files with 1835 additions and 25 deletions

View File

@@ -6,5 +6,5 @@
//
// Prerequisites: protoc, protoc-gen-go, protoc-gen-go-grpc must be in PATH.
//
//go:generate protoc --proto_path=../proto --go_out=../gen --go_opt=paths=source_relative --go-grpc_out=../gen --go-grpc_opt=paths=source_relative mcias/v1/common.proto mcias/v1/admin.proto mcias/v1/auth.proto mcias/v1/token.proto mcias/v1/account.proto
//go:generate protoc --proto_path=../proto --go_out=../gen --go_opt=paths=source_relative --go-grpc_out=../gen --go-grpc_opt=paths=source_relative mcias/v1/common.proto mcias/v1/admin.proto mcias/v1/auth.proto mcias/v1/token.proto mcias/v1/account.proto mcias/v1/policy.proto
package proto

104
proto/mcias/v1/policy.proto Normal file
View File

@@ -0,0 +1,104 @@
// PolicyService: CRUD management of policy rules.
syntax = "proto3";
package mcias.v1;
option go_package = "git.wntrmute.dev/kyle/mcias/gen/mcias/v1;mciasv1";
// PolicyRule is the wire representation of a policy rule record.
message PolicyRule {
int64 id = 1;
string description = 2;
int32 priority = 3;
bool enabled = 4;
string rule_json = 5; // JSON-encoded RuleBody
string created_at = 6; // RFC3339
string updated_at = 7; // RFC3339
string not_before = 8; // RFC3339; empty if unset
string expires_at = 9; // RFC3339; empty if unset
}
// --- List ---
message ListPolicyRulesRequest {}
message ListPolicyRulesResponse {
repeated PolicyRule rules = 1;
}
// --- Create ---
message CreatePolicyRuleRequest {
string description = 1; // required
string rule_json = 2; // required; JSON-encoded RuleBody
int32 priority = 3; // default 100 when zero
string not_before = 4; // RFC3339; optional
string expires_at = 5; // RFC3339; optional
}
message CreatePolicyRuleResponse {
PolicyRule rule = 1;
}
// --- Get ---
message GetPolicyRuleRequest {
int64 id = 1;
}
message GetPolicyRuleResponse {
PolicyRule rule = 1;
}
// --- Update ---
// UpdatePolicyRuleRequest carries partial updates.
// Fields left at their zero value are not changed on the server, except:
// - clear_not_before=true removes the not_before constraint
// - clear_expires_at=true removes the expires_at constraint
// has_priority / has_enabled use proto3 optional (field presence) so the
// server can distinguish "not supplied" from "set to zero/false".
message UpdatePolicyRuleRequest {
int64 id = 1;
optional int32 priority = 2; // omit to leave unchanged
optional bool enabled = 3; // omit to leave unchanged
string not_before = 4; // RFC3339; ignored when clear_not_before=true
string expires_at = 5; // RFC3339; ignored when clear_expires_at=true
bool clear_not_before = 6;
bool clear_expires_at = 7;
}
message UpdatePolicyRuleResponse {
PolicyRule rule = 1;
}
// --- Delete ---
message DeletePolicyRuleRequest {
int64 id = 1;
}
message DeletePolicyRuleResponse {}
// PolicyService manages policy rules (admin only).
service PolicyService {
// ListPolicyRules returns all policy rules.
// Requires: admin JWT.
rpc ListPolicyRules(ListPolicyRulesRequest) returns (ListPolicyRulesResponse);
// CreatePolicyRule creates a new policy rule.
// Requires: admin JWT.
rpc CreatePolicyRule(CreatePolicyRuleRequest) returns (CreatePolicyRuleResponse);
// GetPolicyRule returns a single policy rule by ID.
// Requires: admin JWT.
rpc GetPolicyRule(GetPolicyRuleRequest) returns (GetPolicyRuleResponse);
// UpdatePolicyRule applies a partial update to a policy rule.
// Requires: admin JWT.
rpc UpdatePolicyRule(UpdatePolicyRuleRequest) returns (UpdatePolicyRuleResponse);
// DeletePolicyRule permanently removes a policy rule.
// Requires: admin JWT.
rpc DeletePolicyRule(DeletePolicyRuleRequest) returns (DeletePolicyRuleResponse);
}