Commit 7b17e38f by woodsaj

add Token authentication support

Added CRUD methods for Tokens.
Extend Auth Handler to check for the presence of a Bearer Authorization
header to authenticate against. If there is no header, or the token is not
valid, the Auth Handler falls back to looking for a Session.
parent e58cd914
......@@ -26,6 +26,12 @@ func Register(m *macaron.Macaron) {
m.Post("/api/account/using/:id", auth, SetUsingAccount)
m.Get("/api/account/others", auth, GetOtherAccounts)
// Token
m.Get("/api/tokens/list", auth, GetTokens)
m.Put("/api/tokens", auth, AddToken)
m.Post("/api/tokens", auth, UpdateToken)
m.Delete("/api/tokens/:id", auth, DeleteToken)
// data sources
m.Get("/acount/datasources/", auth, Index)
m.Get("/api/datasources/list", auth, GetDataSources)
......
package api
import (
"github.com/torkelo/grafana-pro/pkg/bus"
"github.com/torkelo/grafana-pro/pkg/middleware"
m "github.com/torkelo/grafana-pro/pkg/models"
"github.com/torkelo/grafana-pro/pkg/util"
)
func GetTokens(c *middleware.Context) {
query := m.GetTokensQuery{AccountId: c.Account.Id}
err := bus.Dispatch(&query)
if err != nil {
c.JsonApiErr(500, "Failed to list tokens", err)
return
}
result := make([]*m.TokenDTO, len(query.Result))
for i, t := range query.Result {
result[i] = &m.TokenDTO{
Id: t.Id,
Name: t.Name,
Role: t.Role,
Token: t.Token,
}
}
c.JSON(200, result)
}
func DeleteToken(c *middleware.Context) {
id := c.ParamsInt64(":id")
cmd := &m.DeleteTokenCommand{Id: id, AccountId: c.UserAccount.Id}
err := bus.Dispatch(cmd)
if err != nil {
c.JsonApiErr(500, "Failed to delete token", err)
return
}
c.JsonOK("Token deleted")
}
func AddToken(c *middleware.Context) {
cmd := m.AddTokenCommand{}
if !c.JsonBody(&cmd) {
c.JsonApiErr(400, "Validation failed", nil)
return
}
if cmd.Role != m.ROLE_READ_WRITE && cmd.Role != m.ROLE_READ {
c.JsonApiErr(400, "Invalid role specified", nil)
return
}
cmd.AccountId = c.Account.Id
cmd.Token = util.GetRandomString(64)
if err := bus.Dispatch(&cmd); err != nil {
c.JsonApiErr(500, "Failed to add token", err)
return
}
result := &m.TokenDTO{
Id: cmd.Result.Id,
Name: cmd.Result.Name,
Role: cmd.Result.Role,
Token: cmd.Result.Token,
}
c.JSON(200, result)
}
func UpdateToken(c *middleware.Context) {
cmd := m.UpdateTokenCommand{}
if !c.JsonBody(&cmd) {
c.JsonApiErr(400, "Validation failed", nil)
return
}
cmd.AccountId = c.Account.Id
err := bus.Dispatch(&cmd)
if err != nil {
c.JsonApiErr(500, "Failed to update token", err)
return
}
c.JsonOK("Token updated")
}
......@@ -2,10 +2,10 @@ package middleware
import (
"errors"
"strconv"
"github.com/Unknwon/macaron"
"github.com/macaron-contrib/session"
"strconv"
"strings"
"github.com/torkelo/grafana-pro/pkg/bus"
m "github.com/torkelo/grafana-pro/pkg/models"
......@@ -39,30 +39,60 @@ func authDenied(c *Context) {
c.Redirect(setting.AppSubUrl + "/login")
}
func Auth() macaron.Handler {
return func(c *Context, sess session.Store) {
accountId, err := authGetRequestAccountId(c, sess)
func authByToken(c *Context) {
header := c.Req.Header.Get("Authorization")
parts := strings.SplitN(header, " ", 2)
if len(parts) != 2 || parts[0] != "Bearer" {
return
}
token := parts[1]
userQuery := m.GetAccountByTokenQuery{Token: token}
err := bus.Dispatch(&userQuery)
if err != nil {
return
}
if err != nil && c.Req.URL.Path != "/login" {
authDenied(c)
return
}
usingQuery := m.GetAccountByIdQuery{Id: userQuery.Result.UsingAccountId}
err = bus.Dispatch(&usingQuery)
if err != nil {
return
}
userQuery := m.GetAccountByIdQuery{Id: accountId}
err = bus.Dispatch(&userQuery)
if err != nil {
authDenied(c)
return
}
c.UserAccount = userQuery.Result
c.Account = usingQuery.Result
}
usingQuery := m.GetAccountByIdQuery{Id: userQuery.Result.UsingAccountId}
err = bus.Dispatch(&usingQuery)
if err != nil {
authDenied(c)
return
}
func authBySession(c *Context, sess session.Store) {
accountId, err := authGetRequestAccountId(c, sess)
c.UserAccount = userQuery.Result
c.Account = usingQuery.Result
if err != nil && c.Req.URL.Path != "/login" {
authDenied(c)
return
}
userQuery := m.GetAccountByIdQuery{Id: accountId}
err = bus.Dispatch(&userQuery)
if err != nil {
authDenied(c)
return
}
usingQuery := m.GetAccountByIdQuery{Id: userQuery.Result.UsingAccountId}
err = bus.Dispatch(&usingQuery)
if err != nil {
authDenied(c)
return
}
c.UserAccount = userQuery.Result
c.Account = usingQuery.Result
}
func Auth() macaron.Handler {
return func(c *Context, sess session.Store) {
authByToken(c)
if c.UserAccount == nil {
authBySession(c, sess)
}
}
}
......@@ -23,23 +23,21 @@ type Account struct {
Company string
NextDashboardId int
UsingAccountId int64
Created time.Time
Updated time.Time
Created time.Time
Updated time.Time
}
// ---------------------
// COMMANDS
type CreateAccountCommand struct {
Email string `json:"email" binding:"required"`
Login string `json:"login"`
Password string `json:"password" binding:"required"`
Name string `json:"name"`
Company string `json:"company"`
Salt string `json:"-"`
Result Account `json:"-"`
Email string `json:"email" binding:"required"`
Login string `json:"login"`
Password string `json:"password" binding:"required"`
Name string `json:"name"`
Company string `json:"company"`
Salt string `json:"-"`
Result Account `json:"-"`
}
type SetUsingAccountCommand struct {
......
package models
import (
"time"
)
type Token struct {
Id int64
AccountId int64 `xorm:"not null unique(uix_account_id_name)"`
Name string `xorm:"not null unique(uix_account_id_name)"`
Token string `xorm:"UNIQUE NOT NULL"`
Role RoleType `xorm:"not null"`
Created time.Time
Updated time.Time
}
// ---------------------
// COMMANDS
type AddTokenCommand struct {
Name string `json:"name" binding:"required"`
Role RoleType `json:"role" binding:"required"`
AccountId int64 `json:"-"`
Token string `json:"-"`
Result *Token `json:"-"`
}
type UpdateTokenCommand struct {
Id int64 `json:"id"`
Name string `json:"name"`
AccountId int64 `json:"-"`
Role RoleType `json:"role"`
Result *Token `json:"-"`
}
type DeleteTokenCommand struct {
Id int64 `json:"id"`
AccountId int64 `json:"-"`
Result *Token `json:"-"`
}
// ----------------------
// QUERIES
type GetTokensQuery struct {
AccountId int64
Result []*Token
}
type GetAccountByTokenQuery struct {
Token string
Result *Account
}
// ------------------------
// DTO & Projections
type TokenDTO struct {
Id int64 `json:"id"`
Name string `json:"name"`
Token string `json:"token"`
Role RoleType `json:"role"`
}
......@@ -17,6 +17,7 @@ func init() {
bus.AddHandler("sql", SetUsingAccount)
bus.AddHandler("sql", GetAccountById)
bus.AddHandler("sql", GetAccountByLogin)
bus.AddHandler("sql", GetAccountByToken)
}
func CreateAccount(cmd *m.CreateAccountCommand) error {
......@@ -109,6 +110,27 @@ func GetAccountById(query *m.GetAccountByIdQuery) error {
return nil
}
func GetAccountByToken(query *m.GetAccountByTokenQuery) error {
var err error
var account m.Account
has, err := x.Where("token=?", query.Token).Get(&account)
if err != nil {
return err
} else if has == false {
return m.ErrAccountNotFound
}
if account.UsingAccountId == 0 {
account.UsingAccountId = account.Id
}
query.Result = &account
return nil
}
func GetAccountByLogin(query *m.GetAccountByLoginQuery) error {
var err error
......
......@@ -38,7 +38,8 @@ func init() {
tables = make([]interface{}, 0)
tables = append(tables, new(m.Account), new(m.Dashboard),
new(m.Collaborator), new(m.DataSource), new(DashboardTag))
new(m.Collaborator), new(m.DataSource), new(DashboardTag),
new(m.Token))
}
func Init() {
......
package sqlstore
import (
"github.com/go-xorm/xorm"
"github.com/torkelo/grafana-pro/pkg/bus"
m "github.com/torkelo/grafana-pro/pkg/models"
"time"
)
func init() {
bus.AddHandler("sql", GetTokens)
bus.AddHandler("sql", AddToken)
bus.AddHandler("sql", UpdateToken)
bus.AddHandler("sql", DeleteToken)
}
func GetTokens(query *m.GetTokensQuery) error {
sess := x.Limit(100, 0).Where("account_id=?", query.AccountId).Asc("name")
query.Result = make([]*m.Token, 0)
return sess.Find(&query.Result)
}
func DeleteToken(cmd *m.DeleteTokenCommand) error {
return inTransaction(func(sess *xorm.Session) error {
var rawSql = "DELETE FROM token WHERE id=? and account_id=?"
_, err := sess.Exec(rawSql, cmd.Id, cmd.AccountId)
return err
})
}
func AddToken(cmd *m.AddTokenCommand) error {
return inTransaction(func(sess *xorm.Session) error {
t := m.Token{
AccountId: cmd.AccountId,
Name: cmd.Name,
Role: cmd.Role,
Token: cmd.Token,
Created: time.Now(),
Updated: time.Now(),
}
if _, err := sess.Insert(&t); err != nil {
return err
}
cmd.Result = &t
return nil
})
}
func UpdateToken(cmd *m.UpdateTokenCommand) error {
return inTransaction(func(sess *xorm.Session) error {
t := m.Token{
Id: cmd.Id,
AccountId: cmd.AccountId,
Name: cmd.Name,
Role: cmd.Role,
Updated: time.Now(),
}
_, err := sess.Where("id=? and account_id=?", t.Id, t.AccountId).Update(&t)
return err
})
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment