You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
109 lines
2.3 KiB
109 lines
2.3 KiB
package auth
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/binary"
|
|
"errors"
|
|
"strconv"
|
|
|
|
"git.aiterp.net/rpdata/api/internal/store"
|
|
"github.com/globalsign/mgo"
|
|
)
|
|
|
|
var keyCollection *mgo.Collection
|
|
|
|
// A Key contains a JWT secret and the limitations of it. There are two types of
|
|
// keys, single-user keys and wildcard keys. The former is used to authenticate
|
|
// a single user (e.g. the logbot) through an API while the latter is only for
|
|
// services that can be trusted to perform its own authentication (a frontend).
|
|
type Key struct {
|
|
ID string `bson:"_id"`
|
|
Name string `bson:"name"`
|
|
User string `bson:"user"`
|
|
Secret string `bson:"secret"`
|
|
}
|
|
|
|
// ValidForUser returns true if the key's user is the same as
|
|
// the user, or it's a wildcard key.
|
|
func (key *Key) ValidForUser(user string) bool {
|
|
return key.User == user || key.User == "*"
|
|
}
|
|
|
|
// FindKey finds a key by kid (key ID)
|
|
func FindKey(kid string) (Key, error) {
|
|
key := Key{}
|
|
err := keyCollection.FindId(kid).One(&key)
|
|
|
|
return key, err
|
|
}
|
|
|
|
// NewKey generates a new key for the user and name. This
|
|
// does not allow generating wildcard keys, they have to be
|
|
// manually inserted into the DB.
|
|
func NewKey(name, user string) (*Key, error) {
|
|
if user == "*" {
|
|
return nil, errors.New("auth: wildcard keys not allowed")
|
|
}
|
|
|
|
secret, err := makeKeySecret()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
key := &Key{
|
|
ID: makeKeyID(),
|
|
Name: name,
|
|
User: user,
|
|
Secret: secret,
|
|
}
|
|
|
|
if err := keyCollection.Insert(key); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return key, nil
|
|
}
|
|
|
|
func init() {
|
|
store.HandleInit(func(db *mgo.Database) {
|
|
keyCollection = db.C("auth.keys")
|
|
|
|
keyCollection.EnsureIndexKey("user")
|
|
})
|
|
}
|
|
|
|
// makeKeyID makes a random story ID that's 16 characters long
|
|
func makeKeyID() string {
|
|
result := "K"
|
|
offset := 0
|
|
data := make([]byte, 32)
|
|
|
|
rand.Read(data)
|
|
for len(result) < 16 {
|
|
result += strconv.FormatUint(binary.LittleEndian.Uint64(data[offset:]), 36)
|
|
offset += 8
|
|
|
|
if offset >= 32 {
|
|
rand.Read(data)
|
|
offset = 0
|
|
}
|
|
}
|
|
|
|
return result[:16]
|
|
}
|
|
|
|
func makeKeySecret() (string, error) {
|
|
data := make([]byte, 64)
|
|
alphabet := "0123456789abcdefghjiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSUTVWXYZ-_"
|
|
|
|
_, err := rand.Read(data)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
for i := range data {
|
|
data[i] = alphabet[data[i]%64]
|
|
}
|
|
|
|
return string(data), nil
|
|
}
|