mirror of https://github.com/grafana/grafana
New implementation for API Keys that only stores hashed api keys, and the client key is base64 decoded json web token with the unhashed key, Closes #1440
parent
6a2a6afc1d
commit
c75aa23092
@ -0,0 +1,6 @@ |
||||
package dtos |
||||
|
||||
type NewApiKeyResult struct { |
||||
Name string `json:"name"` |
||||
Key string `json:"key"` |
||||
} |
||||
@ -1,30 +1,58 @@ |
||||
package apikeygen |
||||
|
||||
import ( |
||||
"strconv" |
||||
"encoding/base64" |
||||
"encoding/json" |
||||
"errors" |
||||
|
||||
"github.com/grafana/grafana/pkg/util" |
||||
) |
||||
|
||||
var ErrInvalidApiKey = errors.New("Invalid Api Key") |
||||
|
||||
type KeyGenResult struct { |
||||
HashedKey string |
||||
JsonKeyEncoded string |
||||
HashedKey string |
||||
ClientSecret string |
||||
} |
||||
|
||||
type ApiKeyJson struct { |
||||
Key string |
||||
AccountId int64 |
||||
Name string |
||||
Key string `json:"k"` |
||||
Name string `json:"n"` |
||||
OrgId int64 `json:"id"` |
||||
} |
||||
|
||||
func GenerateNewKey(accountId int64, name string) KeyGenResult { |
||||
func New(orgId int64, name string) KeyGenResult { |
||||
jsonKey := ApiKeyJson{} |
||||
|
||||
jsonKey.AccountId = accountId |
||||
jsonKey.OrgId = orgId |
||||
jsonKey.Name = name |
||||
jsonKey.Key = util.GetRandomString(32) |
||||
|
||||
result := KeyGenResult{} |
||||
result.HashedKey = util.EncodePassword([]byte(jsonKey.Key), []byte(strconv.FormatInt(accountId, 10))) |
||||
result.HashedKey = util.EncodePassword(jsonKey.Key, name) |
||||
|
||||
jsonString, _ := json.Marshal(jsonKey) |
||||
|
||||
result.ClientSecret = base64.StdEncoding.EncodeToString([]byte(jsonString)) |
||||
return result |
||||
} |
||||
|
||||
func Decode(keyString string) (*ApiKeyJson, error) { |
||||
jsonString, err := base64.StdEncoding.DecodeString(keyString) |
||||
if err != nil { |
||||
return nil, ErrInvalidApiKey |
||||
} |
||||
|
||||
var keyObj ApiKeyJson |
||||
err = json.Unmarshal([]byte(jsonString), &keyObj) |
||||
if err != nil { |
||||
return nil, ErrInvalidApiKey |
||||
} |
||||
|
||||
return &keyObj, nil |
||||
} |
||||
|
||||
func IsValid(key *ApiKeyJson, hashedKey string) bool { |
||||
check := util.EncodePassword(key.Key, key.Name) |
||||
return check == hashedKey |
||||
} |
||||
|
||||
@ -0,0 +1,26 @@ |
||||
package apikeygen |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/grafana/grafana/pkg/util" |
||||
. "github.com/smartystreets/goconvey/convey" |
||||
) |
||||
|
||||
func TestApiKeyGen(t *testing.T) { |
||||
|
||||
Convey("When generating new api key", t, func() { |
||||
result := New(12, "Cool key") |
||||
|
||||
So(result.ClientSecret, ShouldNotBeEmpty) |
||||
So(result.HashedKey, ShouldNotBeEmpty) |
||||
|
||||
Convey("can decode key", func() { |
||||
keyInfo, err := Decode(result.ClientSecret) |
||||
So(err, ShouldBeNil) |
||||
|
||||
keyHashed := util.EncodePassword(keyInfo.Key, keyInfo.Name) |
||||
So(keyHashed, ShouldEqual, result.HashedKey) |
||||
}) |
||||
}) |
||||
} |
||||
@ -0,0 +1,44 @@ |
||||
<div class="modal-body gf-box gf-box-no-margin"> |
||||
<div class="gf-box-header"> |
||||
<div class="gf-box-title"> |
||||
<i class="fa fa-key"></i> |
||||
API Key Created |
||||
</div> |
||||
|
||||
<button class="gf-box-header-close-btn" ng-click="dismiss();"> |
||||
<i class="fa fa-remove"></i> |
||||
</button> |
||||
</div> |
||||
|
||||
<div class="gf-box-body" style="min-height: 0px;"> |
||||
|
||||
<div class="tight-form last"> |
||||
<ul class="tight-form-list"> |
||||
<li class="tight-form-item"> |
||||
<strong>Key</strong> |
||||
</li> |
||||
<li class="tight-form-item last"> |
||||
{{key}} |
||||
</li> |
||||
</ul> |
||||
<div class="clearfix"></div> |
||||
</div> |
||||
<br> |
||||
<br> |
||||
|
||||
<div class="grafana-info-box" style="text-align: left"> |
||||
You will only be able to view this key here once! It is not stored in this form. So be sure to copy it now. |
||||
<br> |
||||
<br> |
||||
You can authenticate request using the Authorization HTTP header, example: |
||||
<br> |
||||
<br> |
||||
<pre class="small" style="overflow: hidden"> |
||||
curl -H "Authorization: Bearer your_key_above" http://your.grafana.com/api/dashboards/db/mydash |
||||
</pre> |
||||
</div> |
||||
|
||||
</div> |
||||
|
||||
</div> |
||||
|
||||
Loading…
Reference in new issue