-
Notifications
You must be signed in to change notification settings - Fork 90
/
error.go
110 lines (94 loc) · 3.35 KB
/
error.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// Copyright 2019 - MinIO, Inc. All rights reserved.
// Use of this source code is governed by the AGPLv3
// license that can be found in the LICENSE file.
package kes
import (
"encoding/json"
"io"
"net/http"
"strings"
)
// KES server API errors
var (
// ErrNotAllowed is returned by a KES server when a client has
// not sufficient permission to perform the API operation.
ErrNotAllowed = NewError(http.StatusForbidden, "not authorized: insufficient permissions")
// ErrKeyNotFound is returned by a KES server when a client tries to
// access or use a cryptographic key which does not exist.
ErrKeyNotFound = NewError(http.StatusNotFound, "key does not exist")
// ErrKeyExists is returned by a KES server when a client tries
// to create a cryptographic key which already exists.
ErrKeyExists = NewError(http.StatusBadRequest, "key already exists")
// ErrPolicyNotFound is returned by a KES server when a client
// tries to access a policy which does not exist.
ErrPolicyNotFound = NewError(http.StatusNotFound, "policy does not exist")
// ErrDecrypt is returned by a KES server when it fails to decrypt
// a ciphertext. It may occur when a client uses the wrong key or
// the ciphertext has been (maliciously) modified.
ErrDecrypt = NewError(http.StatusBadRequest, "decryption failed: ciphertext is not authentic")
)
// Error is a KES server API error.
type Error struct {
code int
message string
}
// NewError returns a new Error with the given
// HTTP status code and error message.
func NewError(code int, msg string) Error {
return Error{
code: code,
message: msg,
}
}
// Status returns the HTTP status code of the error.
func (e Error) Status() int { return e.code }
func (e Error) Error() string { return e.message }
// parseErrorResponse returns an error containing
// the response status code and response body
// as error message if the response is an error
// response - i.e. status code >= 400.
//
// If the response status code is < 400, e.g. 200 OK,
// parseErrorResponse returns nil and does not attempt
// to read or close the response body.
//
// If resp is an error response, parseErrorResponse reads
// and closes the response body.
func parseErrorResponse(resp *http.Response) error {
if resp == nil || resp.StatusCode < 400 {
return nil
}
if resp.Body == nil {
return NewError(resp.StatusCode, "")
}
defer resp.Body.Close()
const MaxBodySize = 1 << 20
size := resp.ContentLength
if size < 0 || size > MaxBodySize {
size = MaxBodySize
}
contentType := strings.TrimSpace(resp.Header.Get("Content-Type"))
if strings.HasPrefix(contentType, "application/json") {
type Response struct {
Message string `json:"message"`
}
var response Response
if err := json.NewDecoder(io.LimitReader(resp.Body, size)).Decode(&response); err != nil {
return err
}
// TODO(aead): Remove the backwards-compatibility error checks once enough of the
// KES server ecosystem has updated.
if resp.StatusCode == http.StatusBadRequest && response.Message == "key does already exist" {
return ErrKeyExists
}
if resp.StatusCode == http.StatusForbidden && response.Message == "prohibited by policy" {
return ErrNotAllowed
}
return NewError(resp.StatusCode, response.Message)
}
var sb strings.Builder
if _, err := io.Copy(&sb, io.LimitReader(resp.Body, size)); err != nil {
return err
}
return NewError(resp.StatusCode, sb.String())
}