Skip to content
This repository has been archived by the owner on Jul 12, 2023. It is now read-only.

Commit

Permalink
Destroy signing key versions
Browse files Browse the repository at this point in the history
  • Loading branch information
sethvargo committed Aug 27, 2020
1 parent c56b384 commit d78dbc6
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 16 deletions.
53 changes: 37 additions & 16 deletions cmd/server/assets/realmkeys.html
Original file line number Diff line number Diff line change
Expand Up @@ -83,36 +83,57 @@
<table class="table table-bordered table-striped">
<thead>
<tr>
<th scope="col" width="30">KeyID</th>
<th scope="col">Public Key</th>
<th scope="col" width="30">Controls</th>
<th scope="col" width="75">Key ID</th>
<th scope="col">Public key</th>
</tr>
</thead>
<tbody>
{{$csrfField := .csrfField}}
{{$publicKeys := .publicKeys}}
{{range $rk := .realmKeys}}
<tr>
<td>{{$rk.GetKID}}
{{if $rk.Active}}<h4><span class="badge badge-success">Active</span></h4>{{end}}
<td>
<span class="text-monospace">{{$rk.GetKID}}</span>
{{if $rk.Active}}<span class="badge badge-success">Active</span>{{end}}
</td>
<td>
<div class="input-group">
<textarea class="form-control" rows="4" id="{{$rk.GetKID}}" readonly>{{index $publicKeys $rk.GetKID}}</textarea>
<textarea class="form-control text-monospace" rows="4" id="{{$rk.GetKID}}" readonly>{{index $publicKeys $rk.GetKID}}</textarea>
{{template "clippy" $rk.GetKID}}
</div>
Backed by (your server operator may ask you for this information):<br/>
<code>{{$rk.KeyID}}</code>
</td>
<td>

<p class="mt-3">Backed by:</p>
<div class="input-group">
<input type="text" id="key-{{$rk.ID}}" class="form-control text-monospace" value="{{$rk.KeyID}}" readonly/>
{{template "clippy" (printf "key-%d" $rk.ID)}}
</div>
<small class="form-text text-muted">
Your server operator may ask for this.
</small>

{{if not $rk.Active}}
<form method="POST" action="/realm/keys/activate">
{{ $csrfField }}
<input type="hidden" name="id" value="{{$rk.ID}}" />
<a href="#" class="btn btn-primary btn-block" data-confirm="Have you already shared the new certificate key version and public key with your 'key server' operator?" data-submit-form>Make Active</a>
</form>
<div class="row mt-3 align-items-end h-100">
<div class="col">
<a href="/realm/keys/{{$rk.ID}}"
class="text-danger"
data-method="DELETE"
data-confirm="Are you sure you want to destroy this key? This action is irreversible!"
data-toggle="tooltip"
title="Destroy this key version">
<span class="oi oi-trash" aria-hidden="true"></span>
</a>
</div>

<!-- TODO - implement destroy -->
<div class="col">
<form method="POST" action="/realm/keys/activate">
{{ $csrfField }}
<input type="hidden" name="id" value="{{$rk.ID}}" />
<a href="#" class="btn btn-primary float-right" data-confirm="Have you already shared the new certificate key version and public key with your 'key server' operator?" data-submit-form>
Activate
</a>
</form>
</div>
</div>
{{end}}
</td>
</tr>
Expand Down
1 change: 1 addition & 0 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ func realMain(ctx context.Context) error {
return fmt.Errorf("failed to create realmkeys controller: %w", err)
}
realmSub.Handle("/keys", realmKeysController.HandleIndex()).Methods("GET")
realmSub.Handle("/keys/{id}", realmKeysController.HandleDestroy()).Methods("DELETE")
realmSub.Handle("/keys/create", realmKeysController.HandleCreateKey()).Methods("POST")
realmSub.Handle("/keys/upgrade", realmKeysController.HandleUpgrade()).Methods("POST")
realmSub.Handle("/keys/save", realmKeysController.HandleSave()).Methods("POST")
Expand Down
51 changes: 51 additions & 0 deletions pkg/controller/realmkeys/destroy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package realmkeys

import (
"net/http"

"github.com/google/exposure-notifications-verification-server/pkg/controller"
"github.com/gorilla/mux"
)

func (c *Controller) HandleDestroy() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
vars := mux.Vars(r)

session := controller.SessionFromContext(ctx)
if session == nil {
controller.MissingSession(w, r, c.h)
return
}
flash := controller.Flash(session)

realm := controller.RealmFromContext(ctx)
if realm == nil {
controller.MissingRealm(w, r, c.h)
return
}

if err := realm.DestroySigningKeyVersion(ctx, c.db, vars["id"]); err != nil {
flash.Error("Failed to destroy signing key version: %v", err)
c.renderShow(ctx, w, r, realm)
return
}

flash.Alert("Successfully destroyed signing key!")
c.redirectShow(ctx, w, r)
})
}
48 changes: 48 additions & 0 deletions pkg/database/realm.go
Original file line number Diff line number Diff line change
Expand Up @@ -520,3 +520,51 @@ func (r *Realm) CreateSigningKeyVersion(ctx context.Context, db *Database) (stri

return signingKey.GetKID(), nil
}

// DestroySigningKeyVersion destroys the given key version in both the database
// and the key manager. ID is the primary key ID from the database. If the id
// does not exist, it does nothing.
func (r *Realm) DestroySigningKeyVersion(ctx context.Context, db *Database, id interface{}) error {
manager := db.signingKeyManager
if manager == nil {
return ErrNoSigningKeyManager
}

if err := db.db.Transaction(func(tx *gorm.DB) error {
// Load the signing key to ensure it actually exists.
var signingKey SigningKey
if err := tx.
Set("gorm:query_option", "FOR UPDATE").
Table("signing_keys").
Where("id = ?", id).
Where("realm_id = ?", r.ID).
First(&signingKey).
Error; err != nil {
if IsNotFound(err) {
return nil
}
return fmt.Errorf("failed to load signing key: %w", err)
}

if signingKey.Active {
return fmt.Errorf("cannot destroy active signing key")
}

// Delete the signing key from the key manager - we want to do this in the
// transaction so, if it fails, we can rollback and try again.
if err := manager.DestroyKeyVersion(ctx, signingKey.KeyID); err != nil {
return fmt.Errorf("failed to destroy signing key in key manager: %w", err)
}

// Successfully deleted from the key manager, now remove the record.
if err := tx.Delete(&signingKey).Error; err != nil {
return fmt.Errorf("failed to delete signing key from database: %w", err)
}

return nil
}); err != nil {
return fmt.Errorf("failed to destroy signing key version: %w", err)
}

return nil
}

0 comments on commit d78dbc6

Please sign in to comment.