mirror of
https://github.com/wahyd4/badger.git
synced 2026-08-08 21:05:55 +10:00
Add endpoint to authenticate a user
This commit is contained in:
@@ -9,6 +9,7 @@ require (
|
||||
github.com/gin-gonic/gin v1.6.3
|
||||
github.com/go-playground/validator/v10 v10.3.0 // indirect
|
||||
github.com/jinzhu/gorm v1.9.16
|
||||
github.com/oklog/ulid v1.3.1
|
||||
github.com/oklog/ulid/v2 v2.0.2
|
||||
github.com/prometheus/client_golang v1.7.1
|
||||
github.com/prometheus/common v0.10.0
|
||||
|
||||
@@ -11,8 +11,10 @@ github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY
|
||||
github.com/ajstarks/svgo v0.0.0-20200320125537-f189e35d30ca h1:kWzLcty5V2rzOqJM7Tp/MfSX0RMSI1x4IOLApEefYxA=
|
||||
github.com/ajstarks/svgo v0.0.0-20200320125537-f189e35d30ca/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
@@ -183,6 +185,7 @@ github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ
|
||||
github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/oklog/ulid/v2 v2.0.2 h1:r4fFzBm+bv0wNKNh5eXTwU7i85y5x+uwkxCUTNVQqLc=
|
||||
github.com/oklog/ulid/v2 v2.0.2/go.mod h1:mtBL0Qe/0HAx6/a4Z30qxVIAL1eQDweXq5lxOEiwQ68=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
@@ -337,6 +340,7 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
|
||||
+1
-9
@@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
@@ -15,7 +14,6 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/jinzhu/gorm/dialects/postgres"
|
||||
"github.com/oklog/ulid/v2"
|
||||
"github.com/prometheus/client_golang/api"
|
||||
v1 "github.com/prometheus/client_golang/api/prometheus/v1"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -193,7 +191,7 @@ func (handler *BadgesHandler) queryBadgeFromDB(queryName string) (*models.Badge,
|
||||
}
|
||||
|
||||
func (handler *BadgesHandler) CreateBadge(c *gin.Context) {
|
||||
newID := newULID()
|
||||
newID := NewULID()
|
||||
jsonB := new(postgres.Jsonb)
|
||||
options := models.PrometheusOptions{
|
||||
ValueField: "",
|
||||
@@ -229,12 +227,6 @@ func (handler *BadgesHandler) CreateBadge(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"id": newID})
|
||||
}
|
||||
|
||||
func newULID() string {
|
||||
now := time.Now()
|
||||
entropy := ulid.Monotonic(rand.New(rand.NewSource(now.UnixNano())), 0)
|
||||
return ulid.MustNew(ulid.Timestamp(now), entropy).String()
|
||||
}
|
||||
|
||||
func queryMetric(client api.Client, options *models.PrometheusOptions) (*Metric, error) {
|
||||
v1api := v1.NewAPI(client)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), metricRequestTimeout)
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/oklog/ulid"
|
||||
)
|
||||
|
||||
func NewULID() string {
|
||||
now := time.Now()
|
||||
entropy := ulid.Monotonic(rand.New(rand.NewSource(now.UnixNano())), 0)
|
||||
return ulid.MustNew(ulid.Timestamp(now), entropy).String()
|
||||
}
|
||||
|
||||
type GeneralError struct {
|
||||
Message string `json:"msg"`
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/prometheus/common/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/wahyd4/badger/models"
|
||||
)
|
||||
|
||||
const (
|
||||
githubHTTPTimeout = 10
|
||||
)
|
||||
|
||||
type UsersHandler struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
type UserAuthenticationRequest struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
GithubID string `json:"githubId" binding:"required"`
|
||||
GithubUsername string `json:"githubUsername" binding:"required"`
|
||||
GithubAvatarURL string `json:"githubAvatar" binding:"required"`
|
||||
Email string `json:"email"`
|
||||
Token string `json:"token" binding:"required"`
|
||||
ExpiresAt string `json:"expiresAt"`
|
||||
}
|
||||
|
||||
type UserInfo struct {
|
||||
ID uint `json:"id"`
|
||||
GithubID string `json:"githubId"`
|
||||
}
|
||||
|
||||
func (usersHandler *UsersHandler) Authenticate(c *gin.Context) {
|
||||
var requestBody UserAuthenticationRequest
|
||||
|
||||
if err := c.ShouldBindJSON(&requestBody); err != nil {
|
||||
logrus.Error(err)
|
||||
sentry.CaptureException(err)
|
||||
c.JSON(http.StatusBadRequest, GeneralError{"invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
if !validateUser(requestBody.Token) {
|
||||
c.JSON(http.StatusBadRequest, GeneralError{fmt.Sprintf("invalid token for user %s", requestBody.Name)})
|
||||
return
|
||||
}
|
||||
var user models.User
|
||||
|
||||
if err := usersHandler.DB.Preload("Token").First(&user, "name = ?", requestBody.Name).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
user = models.User{
|
||||
Name: requestBody.Name,
|
||||
ULID: NewULID(),
|
||||
GithubID: requestBody.GithubID,
|
||||
GithubUsername: requestBody.GithubUsername,
|
||||
GithubAvatarURL: requestBody.GithubAvatarURL,
|
||||
Email: requestBody.Email,
|
||||
Token: models.Token{
|
||||
Token: requestBody.Token,
|
||||
ExpiresAt: requestBody.ExpiresAt,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
logrus.Error(err)
|
||||
sentry.CaptureException(err)
|
||||
c.JSON(http.StatusInternalServerError, GeneralError{"internal error, please try it later"})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
user.Token.Token = requestBody.Token
|
||||
user.Token.ExpiresAt = requestBody.ExpiresAt
|
||||
}
|
||||
|
||||
if err := usersHandler.DB.Save(&user).Error; err != nil {
|
||||
logrus.Error(err)
|
||||
sentry.CaptureException(err)
|
||||
c.JSON(http.StatusInternalServerError, GeneralError{"internal error, please try it later"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, UserInfo{ID: user.ID, GithubID: user.GithubID})
|
||||
|
||||
}
|
||||
|
||||
func validateUser(token string) bool {
|
||||
client := &http.Client{
|
||||
Timeout: time.Second * githubHTTPTimeout,
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", "https://api.github.com/user", nil)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
sentry.CaptureException(err)
|
||||
return false
|
||||
}
|
||||
req.Header.Add("Accept", "application/vnd.github.v3+json")
|
||||
req.Header.Add("Authorization", fmt.Sprintf("token %s", token))
|
||||
resp, err := client.Do(req)
|
||||
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
sentry.CaptureException(err)
|
||||
return false
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
sentry.CaptureException(err)
|
||||
return false
|
||||
}
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
log.Warnf("request with error %s", body)
|
||||
return false
|
||||
}
|
||||
|
||||
logrus.Info()
|
||||
return true
|
||||
}
|
||||
@@ -55,7 +55,7 @@ func main() {
|
||||
defer db.Close()
|
||||
|
||||
// Migrate the schema
|
||||
db.AutoMigrate(&models.Badge{})
|
||||
db.AutoMigrate(&models.Badge{}, &models.Token{}, &models.User{})
|
||||
|
||||
client, err := api.NewClient(api.Config{
|
||||
Address: "https://prometheus-api.home.toozhao.com",
|
||||
@@ -68,6 +68,8 @@ func main() {
|
||||
|
||||
handler := &handlers.BadgesHandler{DB: db, PrometheusAPIClient: client}
|
||||
|
||||
userHandler := &handlers.UsersHandler{DB: db}
|
||||
|
||||
r := gin.Default()
|
||||
|
||||
r.Use(sentrygin.New(sentrygin.Options{
|
||||
@@ -84,6 +86,7 @@ func main() {
|
||||
|
||||
api := r.Group("/api")
|
||||
|
||||
api.POST("/auth", userHandler.Authenticate)
|
||||
api.POST("/badges", handler.CreateBadge)
|
||||
|
||||
go func() {
|
||||
|
||||
@@ -10,6 +10,8 @@ type Badge struct {
|
||||
Label string
|
||||
ULID string `gorm:"column:ulid;type:varchar;not null;unique_index"`
|
||||
Type string
|
||||
User User
|
||||
UserID int
|
||||
Options *postgres.Jsonb
|
||||
OptionStruct interface{} `gorm:"-"`
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package models
|
||||
|
||||
import "github.com/jinzhu/gorm"
|
||||
|
||||
type User struct {
|
||||
gorm.Model
|
||||
Name string
|
||||
ULID string `gorm:"column:ulid"`
|
||||
GithubID string
|
||||
GithubUsername string
|
||||
GithubAvatarURL string
|
||||
Email string
|
||||
Token Token
|
||||
TokenID string
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
gorm.Model
|
||||
Token string
|
||||
ExpiresAt string
|
||||
UserID int
|
||||
}
|
||||
Reference in New Issue
Block a user