diff --git a/controller/i.go b/controller/ROOT/main.go similarity index 88% rename from controller/i.go rename to controller/ROOT/main.go index 2b5cc21..6088939 100644 --- a/controller/i.go +++ b/controller/ROOT/main.go @@ -1,7 +1,7 @@ package main import ( - "git.xdrm.io/example/aicra/controller/db" + "git.xdrm.io/example/aicra/db" e "git.xdrm.io/go/aicra/err" i "git.xdrm.io/go/aicra/implement" ) @@ -26,7 +26,7 @@ func Get(d i.Arguments, r *i.Response) i.Response { } /* (3) Check if match for this key */ - val := cli.Get(key) + val := cli.Get(db.DATA, key) if val == nil { r.Err = e.NoMatchFound return *r @@ -57,13 +57,13 @@ func Post(d i.Arguments, r *i.Response) i.Response { } /* (3) Check if key already used */ - if cli.Get(url) != nil { + if cli.Get(db.DATA, url) != nil { r.Err = e.AlreadyExists return *r } /* (4) Store */ - if !cli.Set(url, target) { + if !cli.Set(db.DATA, url, target) { r.Err = e.Failure return *r } @@ -92,13 +92,13 @@ func Put(d i.Arguments, r *i.Response) i.Response { } /* (3) Check if key already used */ - if cli.Get(url) == nil { + if cli.Get(db.DATA, url) == nil { r.Err = e.NoMatchFound return *r } /* (4) Update */ - if !cli.Set(url, target) { + if !cli.Set(db.DATA, url, target) { r.Err = e.Failure return *r } @@ -126,13 +126,13 @@ func Delete(d i.Arguments, r *i.Response) i.Response { } /* (3) Check if key already used */ - if cli.Get(url) == nil { + if cli.Get(db.DATA, url) == nil { r.Err = e.NoMatchFound return *r } /* (4) Delete */ - if !cli.Del(url) { + if !cli.Del(db.DATA, url) { r.Err = e.Failure return *r } diff --git a/controller/authi.go b/controller/authi.go deleted file mode 100644 index a91037b..0000000 --- a/controller/authi.go +++ /dev/null @@ -1,18 +0,0 @@ -package main - -import ( - "fmt" - e "git.xdrm.io/go/aicra/err" - i "git.xdrm.io/go/aicra/implement" -) - -// Builds an access token from credentials -func Post(d i.Arguments, r *i.Response) i.Response { - - if d.Has("_AUTHORIZATION_") { - fmt.Printf("authorization: '%s'\n", d["_AUTHORIZATION_"].(string)) - } - - r.Err = e.Success - return *r -} diff --git a/controller/token/main.go b/controller/token/main.go new file mode 100644 index 0000000..40e090e --- /dev/null +++ b/controller/token/main.go @@ -0,0 +1,46 @@ +package main + +import ( + "crypto/sha512" + "encoding/hex" + "git.xdrm.io/example/aicra/db" + e "git.xdrm.io/go/aicra/err" + i "git.xdrm.io/go/aicra/implement" + "strconv" + "time" +) + +// Builds an access token from credentials +func Post(d i.Arguments, r *i.Response) i.Response { + + /* (1) Init redis connection */ + cli := db.Connect() + if cli == nil { + r.Err = e.Failure + return *r + } + + /* (2) Extract api input */ + role, ok := d["role"].(string) + if !ok { + r.Err = e.InvalidParam + r.Err.BindArgument("url") + return *r + } + + /* (3) Generate token */ + hasher := sha512.New() + defer hasher.Reset() + hasher.Write([]byte(strconv.FormatInt(time.Now().Unix(), 5))) + token := hex.EncodeToString(hasher.Sum(nil)) + + /* (4) Store */ + if !cli.Set(db.TOKEN, token, role, time.Minute) { + r.Err = e.Failure + return *r + } + + r.Set("token", token) + r.Err = e.Success + return *r +} diff --git a/controller/db/db.go b/db/db.go similarity index 60% rename from controller/db/db.go rename to db/db.go index d8fa576..0f477ae 100644 --- a/controller/db/db.go +++ b/db/db.go @@ -3,16 +3,23 @@ package db import ( "fmt" "github.com/go-redis/redis" + "time" ) const NONCE = "go-tiny-url" -var domain = "data" +type Domain string + +const ( + DATA Domain = "data" + TOKEN Domain = "token" +) type db redis.Client // Returns a connected client to dataset func Connect() *db { + cli := redis.NewClient(&redis.Options{ Addr: "127.0.0.1:6379", Password: "", @@ -27,10 +34,10 @@ func Connect() *db { } // returns value from key (nil if nothing) -func (c *db) Get(key string) []byte { +func (c *db) Get(dom Domain, key string) []byte { // 1. Try to get - if val, err := (*redis.Client)(c).Get(fmt.Sprintf("%s:%s:%s", NONCE, domain, key)).Result(); err != nil { + if val, err := (*redis.Client)(c).Get(fmt.Sprintf("%s:%s:%s", NONCE, dom, key)).Result(); err != nil { // 2. nil if nothing found return nil } else { @@ -41,10 +48,15 @@ func (c *db) Get(key string) []byte { } // stores a value for a key (success state in return) -func (c *db) Set(key string, value string) bool { +func (c *db) Set(dom Domain, key string, value string, exp ...time.Duration) bool { + + var expiration time.Duration = 0 + if len(exp) > 0 { + expiration = exp[0] + } // 1. Try to set - if (*redis.Client)(c).Set(fmt.Sprintf("%s:%s:%s", NONCE, domain, key), value, 0).Err() != nil { + if (*redis.Client)(c).Set(fmt.Sprintf("%s:%s:%s", NONCE, dom, key), value, expiration).Err() != nil { // 2. failure return false } @@ -55,10 +67,10 @@ func (c *db) Set(key string, value string) bool { } // deletes the value for a key (success state in return) -func (c *db) Del(key string) bool { +func (c *db) Del(dom Domain, key string) bool { // 1. Try to set - if (*redis.Client)(c).Del(fmt.Sprintf("%s:%s:%s", NONCE, domain, key)).Err() != nil { + if (*redis.Client)(c).Del(fmt.Sprintf("%s:%s:%s", NONCE, dom, key)).Err() != nil { // 2. failure return false } @@ -67,9 +79,3 @@ func (c *db) Del(key string) bool { return true } - -// Switch following operations to DATA dataset -func (c *db) SwitchData() { domain = "data" } - -// Switch following operations to AUTH dataset -func (c *db) SwitchAuth() { domain = "auth" } diff --git a/main.go b/main.go index fc95f82..7677cb2 100644 --- a/main.go +++ b/main.go @@ -1,4 +1,4 @@ -package aicraserver +package main import ( "git.xdrm.io/go/aicra" diff --git a/manifest.json b/manifest.json index 16a033a..fb61b61 100644 --- a/manifest.json +++ b/manifest.json @@ -38,16 +38,15 @@ }, "/": { - "auth": { + "token": { "POST": { - "info": "returns a 1-minute access token", + "info": "creates a 1-minute access token", "scope": [[]], "in": { - "username": { "info": "user name", "type": "varchar(3,20)" }, - "password": { "info": "password", "type": "varchar(5,150)" } + "URL#0": { "info": "wanted role", "type": "varchar(3,10)", "name": "role" } }, "out": { - "token": { "info": "access token", "type": "varchar(256,256)" } + "token": { "info": "access token", "type": "varchar(128,128)" } } } } diff --git a/middleware/1-auth/main.go b/middleware/1-auth/main.go index 6bfcc9d..609562f 100644 --- a/middleware/1-auth/main.go +++ b/middleware/1-auth/main.go @@ -1,11 +1,41 @@ package main import ( + "git.xdrm.io/example/aicra/db" "git.xdrm.io/go/aicra/middleware" "net/http" + "strings" ) // Authentication middleware func Inspect(req http.Request, scope *middleware.Scope) { - *scope = append(*scope, "admin") + + // 1. get authorization header + token := req.Header.Get("Authorization") + + // fail if no header + if len(token) < 1 { + return + } + + // 2. fail on invalid token format + if len(token) != 128 || strings.ContainsAny(token, "$-_") { + return + } + + // 3. get role for this token + cli := db.Connect() + if cli == nil { + return + } + defer cli.Close() + + role := cli.Get(db.TOKEN, token) + if role == nil { + return + } + + // add role to scope + *scope = append(*scope, string(role)) + }