Commit ec1bcfe0 authored by ale's avatar ale

Do not return an error when a user has no encryption keys on Open

And make HTTP result codes slightly more meaningful.
parent 3dff474e
Pipeline #836 passed with stages
in 49 seconds
......@@ -27,14 +27,17 @@ similarly JSON-encoded.
Retrieve the encrypted key for a user, decrypt it with the provided
password, and store it in memory.
OpenRequest is an object with the
following attributes:
OpenRequest is an object with the following attributes:
* `username`
* `password` to decrypt the user's key with
* `ttl` (seconds) time after which the credentials are automatically
forgotten
If the user has no encrypted keys in the database, the request will
still return successfully: no action will be performed, and no errors
will be returned.
`/api/get` (*GetRequest*) -> *GetResponse*
Retrieve the key for a user. GetRequest must contain the following
......
......@@ -4,6 +4,7 @@ import (
"context"
"errors"
"io/ioutil"
"log"
"strings"
"sync"
"time"
......@@ -15,9 +16,10 @@ import (
)
var (
ErrNoKeys = errors.New("no keys available")
ErrBadUser = errors.New("username does not match authentication token")
ErrInvalidTTL = errors.New("invalid ttl")
errNoKeys = errors.New("no keys available")
errBadUser = errors.New("username does not match authentication token")
errUnauthorized = errors.New("unauthorized")
errInvalidTTL = errors.New("invalid ttl")
)
// Database represents the interface to the underlying backend for
......@@ -130,7 +132,7 @@ func (s *KeyStore) expire() {
// A Context is needed because this method might issue an RPC.
func (s *KeyStore) Open(ctx context.Context, username, password string, ttlSeconds int) error {
if ttlSeconds == 0 {
return ErrInvalidTTL
return errInvalidTTL
}
encKeys, err := s.db.GetPrivateKeys(ctx, username)
......@@ -138,7 +140,8 @@ func (s *KeyStore) Open(ctx context.Context, username, password string, ttlSecon
return err
}
if len(encKeys) == 0 {
return ErrNoKeys
// No keys found. Not an error.
return nil
}
// Naive and inefficient way of decrypting multiple keys: it
......@@ -163,17 +166,20 @@ func (s *KeyStore) Get(username, ssoTicket string) ([]byte, error) {
// Validate the SSO ticket.
tkt, err := s.validator.Validate(ssoTicket, "", s.service, nil)
if err != nil {
return nil, err
// Log authentication failures for debugging purposes.
log.Printf("Validate(%s) error: %v", username, err)
return nil, errUnauthorized
}
if tkt.User != username {
return nil, ErrBadUser
log.Printf("Validate(%s) user mismatch: sso=%s", username, tkt.User)
return nil, errBadUser
}
s.mx.Lock()
defer s.mx.Unlock()
u, ok := s.userKeys[username]
if !ok {
return nil, ErrNoKeys
return nil, errNoKeys
}
return u.pkey, nil
}
......
......@@ -24,7 +24,7 @@ func (s *keyStoreServer) handleOpen(w http.ResponseWriter, r *http.Request) {
err := s.KeyStore.Open(r.Context(), req.Username, req.Password, req.TTL)
if err != nil {
log.Printf("Open(%s) error: %v", req.Username, err)
http.Error(w, err.Error(), http.StatusUnauthorized)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
......@@ -40,7 +40,15 @@ func (s *keyStoreServer) handleGet(w http.ResponseWriter, r *http.Request) {
key, err := s.KeyStore.Get(req.Username, req.SSOTicket)
if err != nil {
log.Printf("Get(%s) error: %v", req.Username, err)
http.Error(w, err.Error(), http.StatusUnauthorized)
// Return an appropriate error code.
switch err {
case errUnauthorized, errBadUser:
http.Error(w, err.Error(), http.StatusUnauthorized)
case errNoKeys:
http.NotFound(w, r)
default:
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment