mirror of
https://github.com/juanfont/headscale.git
synced 2026-11-04 04:34:07 +09:00
api/v2: add POST /device/{id}/expire
SaaS has it, and the replays went round its absence. Same effect as the v1 route.
This commit is contained in:
1 parent
4f6a727e93
commit
73a2ca9377
3 files changed
+31
-11
No files matched your search
@@ -353,6 +353,33 @@ func registerDevices(api huma.API, b Backend) {
|
||||
return &emptyOutput{}, nil
|
||||
})
|
||||
|
||||
huma.Register(api, requireScope(huma.Operation{
|
||||
OperationID: "expireDevice",
|
||||
Method: http.MethodPost,
|
||||
Path: "/api/v2/device/{id}/expire",
|
||||
Summary: "Expire a device's key",
|
||||
Tags: deviceTags,
|
||||
Security: security,
|
||||
DefaultStatus: http.StatusOK,
|
||||
Errors: []int{http.StatusUnauthorized, http.StatusForbidden, http.StatusNotFound},
|
||||
}, scope.DevicesCore), func(ctx context.Context, in *deviceByIDInput) (*emptyOutput, error) {
|
||||
node, err := lookupNode(b, in.DeviceID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
_, nodeChange, err := b.State.SetNodeExpiry(node.ID(), &now)
|
||||
b.Change(nodeChange)
|
||||
|
||||
if err != nil {
|
||||
return nil, mapError("expiring device", err)
|
||||
}
|
||||
|
||||
return &emptyOutput{}, nil
|
||||
})
|
||||
|
||||
huma.Register(api, requireScope(huma.Operation{
|
||||
OperationID: "setDeviceKey",
|
||||
Method: http.MethodPost,
|
||||
|
||||
@@ -117,6 +117,7 @@ func TestAPIv2OAuthMatrix_Enforcement(t *testing.T) {
|
||||
{"listDevices", http.MethodGet, "/api/v2/tailnet/-/devices", scope.DevicesCoreRead, nil, false},
|
||||
{"deleteDevice", http.MethodDelete, "/api/v2/device/1", scope.DevicesCore, nil, false},
|
||||
{"authorizeDevice", http.MethodPost, "/api/v2/device/1/authorized", scope.DevicesCore, map[string]any{}, false},
|
||||
{"expireDevice", http.MethodPost, "/api/v2/device/1/expire", scope.DevicesCore, map[string]any{}, false},
|
||||
{"setDeviceName", http.MethodPost, "/api/v2/device/1/name", scope.DevicesCore, map[string]any{}, false},
|
||||
{"setDeviceTags", http.MethodPost, "/api/v2/device/1/tags", scope.DevicesCore, map[string]any{}, false},
|
||||
{"setDeviceKey", http.MethodPost, "/api/v2/device/1/key", scope.DevicesCore, map[string]any{}, false},
|
||||
|
||||
@@ -1038,10 +1038,9 @@ func (r *stepReplay) setPolicy(t *testing.T, stepName, policy string) {
|
||||
}
|
||||
}
|
||||
|
||||
// api replays one admin exchange, the i-th action of its step. Device and
|
||||
// key deletes go to the v2 API; key mints and expiry, which the v2 API lacks
|
||||
// or would answer with secrets, go to State. Reads are replayed only with
|
||||
// readAPI.
|
||||
// api replays one admin exchange, the i-th action of its step. Device calls
|
||||
// and key deletes go to the v2 API; key mints, which it would answer with
|
||||
// secrets, go to State. Reads are replayed only with readAPI.
|
||||
func (r *stepReplay) api(t *testing.T, stepName string, i int, a testcapture.Action) {
|
||||
t.Helper()
|
||||
|
||||
@@ -1121,13 +1120,6 @@ func (r *stepReplay) api(t *testing.T, stepName string, i int, a testcapture.Act
|
||||
// Headscale has no user roles; a role decides only what the user's
|
||||
// own API key may call, and the replay mints for him directly.
|
||||
|
||||
case a.Method == http.MethodPost && strings.HasSuffix(a.Path, "/expire"):
|
||||
now := time.Now()
|
||||
|
||||
_, c, err := r.srv.State().SetNodeExpiry(r.node(t, deviceAlias(t, a.Path)).ID(), &now)
|
||||
require.NoError(t, err, "%s: expire", stepName)
|
||||
r.srv.App.Change(c)
|
||||
|
||||
case strings.HasPrefix(a.Path, "/device/"):
|
||||
path, ok := r.v2Path(a.Path)
|
||||
require.True(t, ok, "%s: %s names no single device", stepName, a.Path)
|
||||
|
||||
Reference in new issue
Block a user