github.com/greenpau/go-authcrunch@v1.1.4/pkg/identity/api_key.go (about) 1 // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package identity 16 17 import ( 18 "time" 19 20 "github.com/greenpau/go-authcrunch/pkg/errors" 21 "github.com/greenpau/go-authcrunch/pkg/requests" 22 "github.com/greenpau/go-authcrunch/pkg/tagging" 23 "github.com/greenpau/go-authcrunch/pkg/util" 24 "golang.org/x/crypto/bcrypt" 25 ) 26 27 // APIKeyBundle is a collection of API keys. 28 type APIKeyBundle struct { 29 keys []*APIKey 30 size int 31 } 32 33 // APIKey is an API key. 34 type APIKey struct { 35 ID string `json:"id,omitempty" xml:"id,omitempty" yaml:"id,omitempty"` 36 Prefix string `json:"prefix,omitempty" xml:"prefix,omitempty" yaml:"prefix,omitempty"` 37 Usage string `json:"usage,omitempty" xml:"usage,omitempty" yaml:"usage,omitempty"` 38 Comment string `json:"comment,omitempty" xml:"comment,omitempty" yaml:"comment,omitempty"` 39 Payload string `json:"payload,omitempty" xml:"payload,omitempty" yaml:"payload,omitempty"` 40 Expired bool `json:"expired,omitempty" xml:"expired,omitempty" yaml:"expired,omitempty"` 41 ExpiredAt time.Time `json:"expired_at,omitempty" xml:"expired_at,omitempty" yaml:"expired_at,omitempty"` 42 CreatedAt time.Time `json:"created_at,omitempty" xml:"created_at,omitempty" yaml:"created_at,omitempty"` 43 Disabled bool `json:"disabled,omitempty" xml:"disabled,omitempty" yaml:"disabled,omitempty"` 44 DisabledAt time.Time `json:"disabled_at,omitempty" xml:"disabled_at,omitempty" yaml:"disabled_at,omitempty"` 45 Description string `json:"description,omitempty" xml:"description,omitempty" yaml:"description,omitempty"` 46 Tags []tagging.Tag `json:"tags,omitempty" xml:"tags,omitempty" yaml:"tags,omitempty"` 47 Labels []string `json:"labels,omitempty" xml:"labels,omitempty" yaml:"labels,omitempty"` 48 } 49 50 // NewAPIKeyBundle returns an instance of APIKeyBundle. 51 func NewAPIKeyBundle() *APIKeyBundle { 52 return &APIKeyBundle{ 53 keys: []*APIKey{}, 54 } 55 } 56 57 // Add adds APIKey to APIKeyBundle. 58 func (b *APIKeyBundle) Add(k *APIKey) { 59 b.keys = append(b.keys, k) 60 b.size++ 61 } 62 63 // Get returns APIKey instances of the APIKeyBundle. 64 func (b *APIKeyBundle) Get() []*APIKey { 65 return b.keys 66 } 67 68 // Size returns the number of APIKey instances in APIKeyBundle. 69 func (b *APIKeyBundle) Size() int { 70 return b.size 71 } 72 73 // NewAPIKey returns an instance of APIKey. 74 func NewAPIKey(r *requests.Request) (*APIKey, error) { 75 if r.Key.Payload == "" { 76 return nil, errors.ErrAPIKeyPayloadEmpty 77 } 78 if r.Key.Usage == "" { 79 return nil, errors.ErrAPIKeyUsageEmpty 80 } 81 if r.Key.Usage != "api" { 82 return nil, errors.ErrAPIKeyUsageUnsupported.WithArgs(r.Key.Usage) 83 } 84 if r.Key.Comment == "" { 85 return nil, errors.ErrAPIKeyCommentEmpty 86 } 87 p := &APIKey{ 88 Comment: r.Key.Comment, 89 ID: util.GetRandomString(40), 90 Prefix: r.Key.Prefix, 91 Payload: r.Key.Payload, 92 Usage: r.Key.Usage, 93 CreatedAt: time.Now().UTC(), 94 Description: r.Key.Description, 95 Tags: r.Key.Tags, 96 Labels: r.Key.Labels, 97 } 98 if r.Key.Disabled { 99 p.Disabled = true 100 p.DisabledAt = time.Now().UTC() 101 } 102 return p, nil 103 } 104 105 // Disable disables APIKey instance. 106 func (p *APIKey) Disable() { 107 p.Expired = true 108 p.ExpiredAt = time.Now().UTC() 109 p.Disabled = true 110 p.DisabledAt = time.Now().UTC() 111 } 112 113 // Match returns true when the provided API matches. 114 func (p *APIKey) Match(s string) bool { 115 if err := bcrypt.CompareHashAndPassword([]byte(p.Payload), []byte(s)); err == nil { 116 return true 117 } 118 return false 119 }