diff --git a/cmd/server/assets/realmkeys.html b/cmd/server/assets/realmkeys.html index 998613b43..333a5e6e8 100644 --- a/cmd/server/assets/realmkeys.html +++ b/cmd/server/assets/realmkeys.html @@ -83,9 +83,8 @@ - - - + + @@ -93,26 +92,48 @@ {{$publicKeys := .publicKeys}} {{range $rk := .realmKeys}} - - diff --git a/cmd/server/main.go b/cmd/server/main.go index 53c995712..c379b852f 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -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") diff --git a/pkg/controller/realmkeys/destroy.go b/pkg/controller/realmkeys/destroy.go new file mode 100644 index 000000000..ca514a876 --- /dev/null +++ b/pkg/controller/realmkeys/destroy.go @@ -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) + }) +} diff --git a/pkg/database/realm.go b/pkg/database/realm.go index d01153987..b767f4244 100644 --- a/pkg/database/realm.go +++ b/pkg/database/realm.go @@ -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 +}
KeyIDPublic KeyControlsKey IDPublic key
{{$rk.GetKID}} - {{if $rk.Active}}

Active

{{end}} +
+ {{$rk.GetKID}} + {{if $rk.Active}}Active{{end}}
- + {{template "clippy" $rk.GetKID}}
- Backed by (your server operator may ask you for this information):
- {{$rk.KeyID}} -
+ +

Backed by:

+
+ + {{template "clippy" (printf "key-%d" $rk.ID)}} +
+ + Your server operator may ask for this. + + {{if not $rk.Active}} -
- {{ $csrfField }} - - Make Active -
+
+
+ + + +
- +
+
+ {{ $csrfField }} + + + Activate + +
+
+
{{end}}