package auth import ( "crypto/sha512" "database/sql" "encoding/hex" "log" "strconv" "strings" "time" "git.xdrm.io/example/aicra/storage" "git.xdrm.io/go/aicra" "git.xdrm.io/go/aicra/api" ) // Service manages the url shortener type Service struct { storage *sql.DB } // New returns a bare service func New(storage *sql.DB) *Service { log.Printf("[service.auth] created") return &Service{ storage: storage, } } // Wire to the aicra server func (svc *Service) Wire(server *aicra.Server) { log.Printf("[service.auth] wired") server.HandleFunc("POST", "/token", svc.generateToken) } // generateToken generates a token valid for 5 mintes func (svc *Service) generateToken(req api.Request, res *api.Response) { // 1. extract input role, err := req.Param.GetString("role") if err != nil { res.SetError(api.ErrorInvalidParam(), "role", err.Error()) return } // 2. generate token hasher := sha512.New() defer hasher.Reset() hasher.Write([]byte(strconv.FormatInt(time.Now().Unix(), 5))) token := hex.EncodeToString(hasher.Sum(nil)) // 3. store token if !svc.storage.Set(storage.TOKEN, token, role, 5*time.Minute) { res.SetError(api.ErrorFailure()) return } // 4. return data res.Data["token"] = token res.SetError(api.ErrorSuccess()) } // CheckToken returns whether a token is valid func (svc *Service) CheckToken(handler api.HandlerFunc) api.HandlerFunc { return func(req api.Request, res *api.Response) { // success if no scope [['admin']] if req.Scope == nil || len(req.Scope) < 1 { handler(req, res) return } headerToken := req.Request.Header.Get("Authorization") // fail if invalid header if len(headerToken) != 128 || strings.ContainsAny(headerToken, "$-_") { res.SetError(api.ErrorPermission()) return } tokenRole := svc.storage.Get(storage.TOKEN, headerToken) // fail if the role of the token does not match any scope for _, scope := range req.Scope { if scope == nil || len(scope) != 1 { continue } // success if scope[0] == string(tokenRole) { handler(req, res) return } } // failure res.SetError(api.ErrorPermission()) return } }