GraphQL API and utilities for the rpdata project
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

  1. package auth
  2. import (
  3. "crypto/rand"
  4. "encoding/binary"
  5. "errors"
  6. "strconv"
  7. "git.aiterp.net/rpdata/api/internal/store"
  8. "github.com/globalsign/mgo"
  9. )
  10. var keyCollection *mgo.Collection
  11. // A Key contains a JWT secret and the limitations of it. There are two types of
  12. // keys, single-user keys and wildcard keys. The former is used to authenticate
  13. // a single user (e.g. the logbot) through an API while the latter is only for
  14. // services that can be trusted to perform its own authentication (a frontend).
  15. type Key struct {
  16. ID string `bson:"_id"`
  17. Name string `bson:"name"`
  18. User string `bson:"user"`
  19. Secret string `bson:"secret"`
  20. }
  21. // ValidForUser returns true if the key's user is the same as
  22. // the user, or it's a wildcard key.
  23. func (key *Key) ValidForUser(user string) bool {
  24. return key.User == user || key.User == "*"
  25. }
  26. // FindKey finds a key by kid (key ID)
  27. func FindKey(kid string) (Key, error) {
  28. key := Key{}
  29. err := keyCollection.FindId(kid).One(&key)
  30. return key, err
  31. }
  32. // NewKey generates a new key for the user and name. This
  33. // does not allow generating wildcard keys, they have to be
  34. // manually inserted into the DB.
  35. func NewKey(name, user string) (*Key, error) {
  36. if user == "*" {
  37. return nil, errors.New("auth: wildcard keys not allowed")
  38. }
  39. secret, err := makeKeySecret()
  40. if err != nil {
  41. return nil, err
  42. }
  43. key := &Key{
  44. ID: makeKeyID(),
  45. Name: name,
  46. User: user,
  47. Secret: secret,
  48. }
  49. if err := keyCollection.Insert(key); err != nil {
  50. return nil, err
  51. }
  52. return key, nil
  53. }
  54. func init() {
  55. store.HandleInit(func(db *mgo.Database) {
  56. keyCollection = db.C("auth.keys")
  57. keyCollection.EnsureIndexKey("user")
  58. })
  59. }
  60. // makeKeyID makes a random story ID that's 16 characters long
  61. func makeKeyID() string {
  62. result := "K"
  63. offset := 0
  64. data := make([]byte, 32)
  65. rand.Read(data)
  66. for len(result) < 16 {
  67. result += strconv.FormatUint(binary.LittleEndian.Uint64(data[offset:]), 36)
  68. offset += 8
  69. if offset >= 32 {
  70. rand.Read(data)
  71. offset = 0
  72. }
  73. }
  74. return result[:16]
  75. }
  76. func makeKeySecret() (string, error) {
  77. data := make([]byte, 64)
  78. alphabet := "0123456789abcdefghjiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSUTVWXYZ-_"
  79. _, err := rand.Read(data)
  80. if err != nil {
  81. return "", err
  82. }
  83. for i := range data {
  84. data[i] = alphabet[data[i]%64]
  85. }
  86. return string(data), nil
  87. }