OSDN Git Service

add accesstoken
[bytom/bytom.git] / blockchain / accesstoken / accesstoken.go
1 // Package accesstoken provides storage and validation of Chain Core
2 // credentials.
3 package accesstoken
4
5 // TODO(tessr): merge this package into chain/net/http/authn
6
7 import (
8         "context"
9         "crypto/rand"
10         //      "database/sql"
11         "fmt"
12         "regexp"
13         "time"
14
15         "github.com/bytom/crypto/sha3pool"
16         //      "chain/database/pg"
17         "github.com/bytom/errors"
18 )
19
20 const (
21         tokenSize    = 32
22         defaultLimit = 100
23 )
24
25 var (
26         // ErrBadID is returned when Create is called on an invalid id string.
27         ErrBadID = errors.New("invalid id")
28         // ErrDuplicateID is returned when Create is called on an existing ID.
29         ErrDuplicateID = errors.New("duplicate access token ID")
30         // ErrBadType is returned when Create is called with a bad type.
31         ErrBadType = errors.New("type must be client or network")
32
33         // validIDRegexp checks that all characters are alphumeric, _ or -.
34         // It also must have a length of at least 1.
35         validIDRegexp = regexp.MustCompile(`^[\w-]+$`)
36 )
37
38 type Token struct {
39         ID      string    `json:"id"`
40         Token   string    `json:"token,omitempty"`
41         Type    string    `json:"type,omitempty"` // deprecated in 1.2
42         Created time.Time `json:"created_at"`
43         sortID  string
44 }
45
46 type CredentialStore struct {
47         //      DB pg.DB
48 }
49
50 // Create generates a new access token with the given ID.
51 func (cs *Token) Create(ctx context.Context, id, typ string) (*Token, error) {
52         if !validIDRegexp.MatchString(id) {
53                 return nil, errors.WithDetailf(ErrBadID, "invalid id %q", id)
54         }
55
56         var secret [tokenSize]byte
57         _, err := rand.Read(secret[:])
58         if err != nil {
59                 return nil, err
60         }
61         var hashedSecret [32]byte
62         sha3pool.Sum256(hashedSecret[:], secret[:])
63
64         /*      const q = `
65                         INSERT INTO access_tokens (id, type, hashed_secret)
66                         VALUES($1, $2, $3)
67                         RETURNING created, sort_id
68                 `
69         */var (
70                 created time.Time
71                 sortID  string
72         //      maybeType = sql.NullString{String: typ, Valid: typ != ""}
73         )
74         /*      err = cs.DB.QueryRowContext(ctx, q, id, maybeType, hashedSecret[:]).Scan(&created, &sortID)
75                 if pg.IsUniqueViolation(err) {
76                         return nil, errors.WithDetailf(ErrDuplicateID, "id %q already in use", id)
77                 }
78                 if err != nil {
79                         return nil, errors.Wrap(err)
80                 }
81         */
82         return &Token{
83                 ID:      id,
84                 Token:   fmt.Sprintf("%s:%x", id, secret),
85                 Type:    typ,
86                 Created: created,
87                 sortID:  sortID,
88         }, nil
89 }
90
91 // Check returns whether or not an id-secret pair is a valid access token.
92 func (cs *Token) Check(ctx context.Context, id string, secret []byte) (bool, error) {
93         var (
94                 toHash [tokenSize]byte
95                 hashed [32]byte
96         )
97         copy(toHash[:], secret)
98         sha3pool.Sum256(hashed[:], toHash[:])
99
100         /*const q = `SELECT EXISTS(SELECT 1 FROM access_tokens WHERE id=$1 AND hashed_secret=$2)`
101         var valid bool
102         err := cs.DB.QueryRowContext(ctx, q, id, hashed[:]).Scan(&valid)
103         if err != nil {
104                 return false, err
105         }
106         */
107         //      return valid, nil
108         return true, nil
109 }
110
111 // Exists returns whether an id is part of a valid access token. It does not validate a secret.
112 func (cs *Token) Exists(ctx context.Context, id string) bool {
113         /*      const q = `SELECT EXISTS(SELECT 1 FROM access_tokens WHERE id=$1)`
114                 var valid bool
115                 err := cs.DB.QueryRowContext(ctx, q, id).Scan(&valid)
116                 if err != nil {
117                         return false
118                 }
119                 return valid
120         */
121         return true
122 }
123
124 // List lists all access tokens.
125 func (cs *Token) List(ctx context.Context, typ, after string, limit int) ([]*Token, string, error) {
126         if limit == 0 {
127                 limit = defaultLimit
128         }
129         /*      const q = `
130                                 SELECT id, type, sort_id, created FROM access_tokens
131                                 WHERE ($1='' OR type=$1::access_token_type) AND ($2='' OR sort_id<$2)
132                                 ORDER BY sort_id DESC
133                                 LIMIT $3
134                         `
135                 var tokens []*Token
136                 //      err := pg.ForQueryRows(ctx, cs.DB, q, typ, after, limit, func(id string, maybeType sql.NullString, sortID string, created time.Time) {
137                 t := Token{
138                         ID:      id,
139                         Created: created,
140                         Type:    maybeType.String,
141                         sortID:  sortID,
142                 }
143                 tokens = append(tokens, &t)
144                         })
145                 if err != nil {
146                         return nil, "", errors.Wrap(err)
147                 }
148         */
149         var tokens []*Token
150         var next string
151         if len(tokens) > 0 {
152                 next = tokens[len(tokens)-1].sortID
153         }
154
155         return tokens, next, nil
156 }
157
158 // Delete deletes an access token by id.
159 func (cs *Token) Delete(ctx context.Context, id string) error {
160         /*      const q = `DELETE FROM access_tokens WHERE id=$1`
161                 res, err := cs.DB.ExecContext(ctx, q, id)
162                 if err != nil {
163                         return errors.Wrap(err)
164                 }
165         */
166         /*      deleted, err := res.RowsAffected()
167                 if err != nil {
168                         return errors.Wrap(err)
169                 }
170
171                 if deleted == 0 {
172                         return errors.WithDetailf(pg.ErrUserInputNotFound, "acccess token id %s", id)
173                 }
174         */return nil
175 }