vault/sdk/rotation/rotation_job.go
kpcraig 8f522a2bca
add ce side code and stubs for rotation manager
* add ce side code and stubs

* add changelog

* style refactor

* try to use APIPath as mount point instead of request field

* fix linter

* return a response struct instead of a pure timestamp

* add issue time to response

* add ttl to GetRotationInformation response

* rename field for clarity

* update ttl to just seconds

* rename next and last rotation time field; describe what they are

* rename function

* catch up to ent PR

* fix patch merge mistake
2025-07-15 12:48:00 -04:00

134 lines
3.5 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package rotation
import (
"fmt"
"time"
"github.com/robfig/cron/v3"
)
const (
PerformedRegistration = "registration"
PerformedDeregistration = "deregistration"
)
// RotationOptions is an embeddable struct to capture common rotation
// settings between a Secret and Auth
type RotationOptions struct {
// Schedule holds the info for the framework.Schedule
Schedule *RotationSchedule
}
// RotationJob represents the secret part of a response.
type RotationJob struct {
RotationOptions
// RotationID is the ID returned to the user to manage this secret.
// This is generated by Vault core. Any set value will be ignored.
// For requests, this will always be blank.
RotationID string `sentinel:""`
Path string
MountPoint string
Name string
}
type RotationJobConfigureRequest struct {
Name string
MountPoint string
ReqPath string
RotationSchedule string
RotationWindow time.Duration
RotationPeriod time.Duration
}
type RotationJobDeregisterRequest struct {
MountPoint string
ReqPath string
}
// RotationInfoRequest is the request struct used by SystemView.GetRotationInformation.
type RotationInfoRequest struct {
// ReqPath is the plugin-local path to the credential, and needs to match the ReqPath value that
// was supplied in schedule creation with RegisterRotationJob
ReqPath string
}
// RotationInfoResponse is the response struct returned by SystemView.GetRotationInformation.
type RotationInfoResponse struct {
// NextVaultRotation is the scheduled time of the next rotation.
NextVaultRotation time.Time
// LastVaultRotation is the time of the prior rotation.
LastVaultRotation time.Time
// TTL is integer seconds until next rotation, conventionally clamped to 0 (i.e., will not be negative)
TTL int64
}
func (s *RotationJob) Validate() error {
if s.MountPoint == "" {
return fmt.Errorf("MountPoint is required")
}
if s.Path == "" {
return fmt.Errorf("ReqPath is required")
}
if s.Schedule.RotationSchedule == "" && s.Schedule.RotationPeriod.Seconds() == 0 {
return fmt.Errorf("RotationSchedule or RotationPeriod is required to set up rotation job")
}
return nil
}
func newRotationJob(configRequest *RotationJobConfigureRequest) (*RotationJob, error) {
var cronSc *cron.SpecSchedule
if configRequest.RotationSchedule != "" {
var err error
cronSc, err = DefaultScheduler.Parse(configRequest.RotationSchedule)
if err != nil {
return nil, err
}
}
rs := &RotationSchedule{
Schedule: cronSc,
RotationSchedule: configRequest.RotationSchedule,
RotationWindow: configRequest.RotationWindow,
RotationPeriod: configRequest.RotationPeriod,
NextVaultRotation: time.Time{},
LastVaultRotation: time.Time{},
}
return &RotationJob{
RotationOptions: RotationOptions{
Schedule: rs,
},
MountPoint: configRequest.MountPoint,
Path: configRequest.ReqPath,
Name: configRequest.Name,
}, nil
}
// ConfigureRotationJob builds and returns a configured RotationJob for the mount and request with the given schedule.
func ConfigureRotationJob(configRequest *RotationJobConfigureRequest) (*RotationJob, error) {
rotationJob, err := newRotationJob(configRequest)
if err != nil {
return nil, err
}
if err := rotationJob.Validate(); err != nil {
return nil, fmt.Errorf("error validating rotation job: %s", err)
}
// Expect rotation job to exist here
if rotationJob == nil {
return nil, fmt.Errorf("rotation credential was nil; expected non-nil value")
}
return rotationJob, nil
}