mirror of
https://github.com/semaphoreui/semaphore.git
synced 2025-01-20 23:39:56 +01:00
API Tokens, Documentation
- API Tokens - More documentation - Update API
This commit is contained in:
parent
5d332266ac
commit
d6370c875e
8
database/sql_migrations/v1.2.0.sql
Normal file
8
database/sql_migrations/v1.2.0.sql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
create table `user__token` (
|
||||||
|
`id` varchar(32) not null primary key,
|
||||||
|
`created` datetime not null default NOW(),
|
||||||
|
`expired` tinyint(1) not null default 0,
|
||||||
|
`user_id` int(11) not null,
|
||||||
|
|
||||||
|
foreign key (`user_id`) references user(`id`) on delete cascade
|
||||||
|
) ENGINE=InnoDB CHARSET=utf8;
|
18
models/APIToken.go
Normal file
18
models/APIToken.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ansible-semaphore/semaphore/database"
|
||||||
|
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type APIToken struct {
|
||||||
|
ID string `db:"id" json:"id"`
|
||||||
|
Created time.Time `db:"created" json:"created"`
|
||||||
|
Expired bool `db:"expired" json:"expired"`
|
||||||
|
UserID int `db:"user_id" json:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
database.Mysql.AddTableWithName(APIToken{}, "user__token").SetKeys(false, "id")
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
br
|
||||||
|
|
||||||
|
h4.text-center.text-muted Scheduled tasks are WIP
|
@ -7,6 +7,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ansible-semaphore/semaphore/database"
|
"github.com/ansible-semaphore/semaphore/database"
|
||||||
@ -16,56 +17,67 @@ import (
|
|||||||
"gopkg.in/redis.v3"
|
"gopkg.in/redis.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resetSessionExpiry(sessionID string) {
|
func resetSessionExpiry(sessionID string, ttl time.Duration) {
|
||||||
if err := database.Redis.Expire(sessionID, 7*24*time.Hour).Err(); err != nil {
|
var cmd *redis.BoolCmd
|
||||||
|
|
||||||
|
if ttl == 0 {
|
||||||
|
cmd = database.Redis.Persist(sessionID)
|
||||||
|
} else {
|
||||||
|
cmd = database.Redis.Expire(sessionID, ttl)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cmd.Err(); err != nil {
|
||||||
fmt.Println("Cannot reset session expiry:", err)
|
fmt.Println("Cannot reset session expiry:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func authentication(c *gin.Context) {
|
func authentication(c *gin.Context) {
|
||||||
cookie, err := c.Request.Cookie("semaphore")
|
var redisKey string
|
||||||
if err != nil {
|
ttl := 7 * 24 * time.Hour
|
||||||
// create cookie
|
|
||||||
new_cookie := make([]byte, 32)
|
if authHeader := strings.ToLower(c.Request.Header.Get("authorization")); len(authHeader) > 0 {
|
||||||
if _, err := io.ReadFull(rand.Reader, new_cookie); err != nil {
|
redisKey = "token-session:" + strings.Replace(authHeader, "bearer ", "", 1)
|
||||||
panic(err)
|
ttl = 0
|
||||||
|
} else {
|
||||||
|
cookie, err := c.Request.Cookie("semaphore")
|
||||||
|
if err != nil {
|
||||||
|
// create cookie
|
||||||
|
new_cookie := make([]byte, 32)
|
||||||
|
if _, err := io.ReadFull(rand.Reader, new_cookie); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cookie_value := url.QueryEscape(base64.URLEncoding.EncodeToString(new_cookie))
|
||||||
|
cookie = &http.Cookie{Name: "semaphore", Value: cookie_value, Path: "/", HttpOnly: true}
|
||||||
|
http.SetCookie(c.Writer, cookie)
|
||||||
}
|
}
|
||||||
|
|
||||||
cookie_value := url.QueryEscape(base64.URLEncoding.EncodeToString(new_cookie))
|
redisKey = "session:" + cookie.Value
|
||||||
cookie = &http.Cookie{Name: "semaphore", Value: cookie_value, Path: "/", HttpOnly: true}
|
|
||||||
http.SetCookie(c.Writer, cookie)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
redis_key := "session:" + cookie.Value
|
s, err := database.Redis.Get(redisKey).Result()
|
||||||
s, err := database.Redis.Get(redis_key).Result()
|
|
||||||
if err == redis.Nil {
|
if err == redis.Nil {
|
||||||
// create a session
|
// create a session
|
||||||
temp_session := models.Session{}
|
temp_session := models.Session{}
|
||||||
s = string(temp_session.Encode())
|
s = string(temp_session.Encode())
|
||||||
|
|
||||||
if err := database.Redis.Set(redis_key, s, 0).Err(); err != nil {
|
if err := database.Redis.Set(redisKey, s, 0).Err(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
fmt.Println("Cannot get cookie from redis:", err)
|
fmt.Println("Cannot get session from redis:", err)
|
||||||
c.AbortWithStatus(500)
|
c.AbortWithStatus(500)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset session expiry
|
sess, err := models.DecodeSession(redisKey, s)
|
||||||
go resetSessionExpiry(redis_key)
|
|
||||||
|
|
||||||
sess, err := models.DecodeSession(cookie.Value, s)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Cannot decode session:", err)
|
fmt.Println("Cannot decode session:", err)
|
||||||
util.AuthFailed(c)
|
util.AuthFailed(c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sess.ID = cookie.Value
|
|
||||||
c.Set("session", sess)
|
|
||||||
|
|
||||||
if sess.UserID != nil {
|
if sess.UserID != nil {
|
||||||
user, err := models.FetchUser(*sess.UserID)
|
user, err := models.FetchUser(*sess.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -77,6 +89,10 @@ func authentication(c *gin.Context) {
|
|||||||
c.Set("user", user)
|
c.Set("user", user)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reset session expiry
|
||||||
|
go resetSessionExpiry(redisKey, ttl)
|
||||||
|
|
||||||
|
c.Set("session", sess)
|
||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package auth
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
@ -14,7 +14,7 @@ import (
|
|||||||
sq "github.com/masterminds/squirrel"
|
sq "github.com/masterminds/squirrel"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Login(c *gin.Context) {
|
func login(c *gin.Context) {
|
||||||
var login struct {
|
var login struct {
|
||||||
Auth string `json:"auth" binding:"required"`
|
Auth string `json:"auth" binding:"required"`
|
||||||
Password string `json:"password" binding:"required"`
|
Password string `json:"password" binding:"required"`
|
||||||
@ -56,7 +56,7 @@ func Login(c *gin.Context) {
|
|||||||
session := c.MustGet("session").(models.Session)
|
session := c.MustGet("session").(models.Session)
|
||||||
session.UserID = &user.ID
|
session.UserID = &user.ID
|
||||||
|
|
||||||
status := database.Redis.Set("session:"+session.ID, string(session.Encode()), 7*24*time.Hour)
|
status := database.Redis.Set(session.ID, string(session.Encode()), 7*24*time.Hour)
|
||||||
if err := status.Err(); err != nil {
|
if err := status.Err(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -64,9 +64,9 @@ func Login(c *gin.Context) {
|
|||||||
c.AbortWithStatus(204)
|
c.AbortWithStatus(204)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Logout(c *gin.Context) {
|
func logout(c *gin.Context) {
|
||||||
session := c.MustGet("session").(models.Session)
|
session := c.MustGet("session").(models.Session)
|
||||||
if err := database.Redis.Del("session:" + session.ID).Err(); err != nil {
|
if err := database.Redis.Del(session.ID).Err(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
|||||||
package projects
|
package projects
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
"github.com/ansible-semaphore/semaphore/database"
|
"github.com/ansible-semaphore/semaphore/database"
|
||||||
"github.com/ansible-semaphore/semaphore/models"
|
"github.com/ansible-semaphore/semaphore/models"
|
||||||
"github.com/ansible-semaphore/semaphore/util"
|
"github.com/ansible-semaphore/semaphore/util"
|
||||||
@ -9,6 +11,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func KeyMiddleware(c *gin.Context) {
|
func KeyMiddleware(c *gin.Context) {
|
||||||
|
project := c.MustGet("project").(models.Project)
|
||||||
|
keyID, err := util.GetIntParam("key_id", c)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var key models.AccessKey
|
||||||
|
if err := database.Mysql.SelectOne(&key, "select * from access_key where project_id=? and id=?", project.ID, keyID); err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
c.AbortWithStatus(404)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set("accessKey", key)
|
||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,7 +44,6 @@ func GetKeys(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
query, args, _ := q.ToSql()
|
query, args, _ := q.ToSql()
|
||||||
|
|
||||||
if _, err := database.Mysql.Select(&keys, query, args...); err != nil {
|
if _, err := database.Mysql.Select(&keys, query, args...); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -57,17 +75,33 @@ func AddKey(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func UpdateKey(c *gin.Context) {
|
func UpdateKey(c *gin.Context) {
|
||||||
c.AbortWithStatus(501)
|
var key models.AccessKey
|
||||||
}
|
oldKey := c.MustGet("accessKey").(models.AccessKey)
|
||||||
|
|
||||||
func RemoveKey(c *gin.Context) {
|
if err := c.Bind(&key); err != nil {
|
||||||
project := c.MustGet("project").(models.Project)
|
|
||||||
keyID, err := util.GetIntParam("key_id", c)
|
|
||||||
if err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := database.Mysql.Exec("delete from access_key where project_id=? and id=?", project.ID, keyID); err != nil {
|
switch key.Type {
|
||||||
|
case "aws", "gcloud", "do", "ssh":
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
c.AbortWithStatus(400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := database.Mysql.Exec("update access_key set name=?, type=?, `key`=?, secret=?", key.Name, key.Type, key.Key, key.Secret, oldKey.ID); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.AbortWithStatus(204)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveKey(c *gin.Context) {
|
||||||
|
project := c.MustGet("project").(models.Project)
|
||||||
|
key := c.MustGet("accessKey").(models.AccessKey)
|
||||||
|
|
||||||
|
if _, err := database.Mysql.Exec("delete from access_key where project_id=? and id=?", project.ID, key.ID); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package projects
|
package projects
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
"github.com/ansible-semaphore/semaphore/database"
|
"github.com/ansible-semaphore/semaphore/database"
|
||||||
"github.com/ansible-semaphore/semaphore/models"
|
"github.com/ansible-semaphore/semaphore/models"
|
||||||
"github.com/ansible-semaphore/semaphore/util"
|
"github.com/ansible-semaphore/semaphore/util"
|
||||||
@ -9,6 +11,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func RepositoryMiddleware(c *gin.Context) {
|
func RepositoryMiddleware(c *gin.Context) {
|
||||||
|
project := c.MustGet("project").(models.Project)
|
||||||
|
repositoryID, err := util.GetIntParam("repository_id", c)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var repository models.Repository
|
||||||
|
if err := database.Mysql.SelectOne(&repository, "select * from project__repository where project_id=? and id=?", project.ID, repositoryID); err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
c.AbortWithStatus(404)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set("repository", repository)
|
||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,15 +63,23 @@ func AddRepository(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func UpdateRepository(c *gin.Context) {
|
func UpdateRepository(c *gin.Context) {
|
||||||
c.AbortWithStatus(501)
|
project := c.MustGet("project").(models.Project)
|
||||||
|
var repository models.Repository
|
||||||
|
|
||||||
|
if err := c.Bind(&repository); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := database.Mysql.Exec("update project__repository set git_url=?, ssh_key_id=? where id=?", repository.GitUrl, repository.SshKeyID, c.MustGet("repository").(models.Repository).ID); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.AbortWithStatus(204)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RemoveRepository(c *gin.Context) {
|
func RemoveRepository(c *gin.Context) {
|
||||||
project := c.MustGet("project").(models.Project)
|
project := c.MustGet("project").(models.Project)
|
||||||
repositoryID, err := util.GetIntParam("repository_id", c)
|
repository := c.MustGet("repository").(models.Repository)
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := database.Mysql.Exec("delete from project__repository where project_id=? and id=?", project.ID, repositoryID); err != nil {
|
if _, err := database.Mysql.Exec("delete from project__repository where project_id=? and id=?", project.ID, repositoryID); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package projects
|
package projects
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
"github.com/ansible-semaphore/semaphore/database"
|
"github.com/ansible-semaphore/semaphore/database"
|
||||||
"github.com/ansible-semaphore/semaphore/models"
|
"github.com/ansible-semaphore/semaphore/models"
|
||||||
"github.com/ansible-semaphore/semaphore/util"
|
"github.com/ansible-semaphore/semaphore/util"
|
||||||
@ -9,6 +11,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func UserMiddleware(c *gin.Context) {
|
func UserMiddleware(c *gin.Context) {
|
||||||
|
project := c.MustGet("project").(models.Project)
|
||||||
|
userID, err := util.GetIntParam("user_id", c)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var user models.User
|
||||||
|
if err := database.Mysql.SelectOne(&user, "select u.* from project__user as pu join user as u on pu.user_id=u.id where pu.user_id=? and pu.project_id=?", userID, project.ID); err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
c.AbortWithStatus(404)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set("projectUser", user)
|
||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,12 +68,9 @@ func AddUser(c *gin.Context) {
|
|||||||
|
|
||||||
func RemoveUser(c *gin.Context) {
|
func RemoveUser(c *gin.Context) {
|
||||||
project := c.MustGet("project").(models.Project)
|
project := c.MustGet("project").(models.Project)
|
||||||
userID, err := util.GetIntParam("user_id", c)
|
user := c.MustGet("projectUser").(models.User)
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := database.Mysql.Exec("delete from project__user where user_id=? and project_id=?", userID, project.ID); err != nil {
|
if _, err := database.Mysql.Exec("delete from project__user where user_id=? and project_id=?", user.ID, project.ID); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,9 +78,18 @@ func RemoveUser(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func MakeUserAdmin(c *gin.Context) {
|
func MakeUserAdmin(c *gin.Context) {
|
||||||
|
project := c.MustGet("project").(models.Project)
|
||||||
|
user := c.MustGet("projectUser").(models.User)
|
||||||
|
admin := 1
|
||||||
|
|
||||||
if c.Request.Method == "DELETE" {
|
if c.Request.Method == "DELETE" {
|
||||||
// strip admin
|
// strip admin
|
||||||
|
admin = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
c.AbortWithStatus(501)
|
if _, err := database.Mysql.Exec("update project__user set admin=? where user_id=? and project_id=?", admin, user.ID, project.ID); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.AbortWithStatus(204)
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package routes
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ansible-semaphore/semaphore/routes/auth"
|
|
||||||
"github.com/ansible-semaphore/semaphore/routes/projects"
|
"github.com/ansible-semaphore/semaphore/routes/projects"
|
||||||
"github.com/ansible-semaphore/semaphore/routes/sockets"
|
"github.com/ansible-semaphore/semaphore/routes/sockets"
|
||||||
"github.com/ansible-semaphore/semaphore/routes/tasks"
|
"github.com/ansible-semaphore/semaphore/routes/tasks"
|
||||||
@ -25,16 +24,22 @@ func Route(r *gin.Engine) {
|
|||||||
api.Use(authentication)
|
api.Use(authentication)
|
||||||
|
|
||||||
func(api *gin.RouterGroup) {
|
func(api *gin.RouterGroup) {
|
||||||
api.POST("/login", auth.Login)
|
api.POST("/login", login)
|
||||||
api.POST("/logout", auth.Logout)
|
api.POST("/logout", logout)
|
||||||
}(api.Group("/auth"))
|
}(api.Group("/auth"))
|
||||||
|
|
||||||
api.Use(MustAuthenticate)
|
api.Use(MustAuthenticate)
|
||||||
|
|
||||||
api.GET("/ws", sockets.Handler)
|
api.GET("/ws", sockets.Handler)
|
||||||
|
|
||||||
api.GET("/user", getUser)
|
func(api *gin.RouterGroup) {
|
||||||
// api.PUT("/user", misc.UpdateUser)
|
api.GET("", getUser)
|
||||||
|
// api.PUT("/user", misc.UpdateUser)
|
||||||
|
|
||||||
|
api.GET("/tokens", getAPITokens)
|
||||||
|
api.POST("/tokens", createAPIToken)
|
||||||
|
api.DELETE("/tokens/:token_id", expireAPIToken)
|
||||||
|
}(api.Group("/user"))
|
||||||
|
|
||||||
api.GET("/projects", projects.GetProjects)
|
api.GET("/projects", projects.GetProjects)
|
||||||
api.POST("/projects", projects.AddProject)
|
api.POST("/projects", projects.AddProject)
|
||||||
@ -129,7 +134,3 @@ func servePublic(c *gin.Context) {
|
|||||||
c.Writer.Header().Set("content-type", contentType)
|
c.Writer.Header().Set("content-type", contentType)
|
||||||
c.String(200, string(res))
|
c.String(200, string(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUser(c *gin.Context) {
|
|
||||||
c.JSON(200, c.MustGet("user"))
|
|
||||||
}
|
|
||||||
|
80
routes/user.go
Normal file
80
routes/user.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ansible-semaphore/semaphore/database"
|
||||||
|
"github.com/ansible-semaphore/semaphore/models"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getUser(c *gin.Context) {
|
||||||
|
c.JSON(200, c.MustGet("user"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAPITokens(c *gin.Context) {
|
||||||
|
user := c.MustGet("user").(*models.User)
|
||||||
|
|
||||||
|
var tokens []models.APIToken
|
||||||
|
if _, err := database.Mysql.Select(&tokens, "select * from user__token where user_id=?", user.ID); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createAPIToken(c *gin.Context) {
|
||||||
|
user := c.MustGet("user").(*models.User)
|
||||||
|
tokenID := make([]byte, 32)
|
||||||
|
if _, err := io.ReadFull(rand.Reader, tokenID); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
token := models.APIToken{
|
||||||
|
ID: strings.ToLower(base64.URLEncoding.EncodeToString(tokenID)),
|
||||||
|
Created: time.Now(),
|
||||||
|
UserID: user.ID,
|
||||||
|
Expired: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := database.Mysql.Insert(&token); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_session := models.Session{
|
||||||
|
UserID: &user.ID,
|
||||||
|
}
|
||||||
|
if err := database.Redis.Set("token-session:"+token.ID, temp_session.Encode(), 0).Err(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(201, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
func expireAPIToken(c *gin.Context) {
|
||||||
|
user := c.MustGet("user").(*models.User)
|
||||||
|
|
||||||
|
tokenID := c.Param("token_id")
|
||||||
|
res, err := database.Mysql.Exec("update user__token set expired=1 where id=? and user_id=?", tokenID, user.ID)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := res.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if affected > 0 {
|
||||||
|
// remove from redis
|
||||||
|
if err := database.Redis.Del("token-session:" + tokenID).Err(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.AbortWithStatus(204)
|
||||||
|
}
|
227
swagger.yml
227
swagger.yml
@ -45,12 +45,52 @@ definitions:
|
|||||||
created:
|
created:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
|
APIToken:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
created:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
expired:
|
||||||
|
type: boolean
|
||||||
|
user_id:
|
||||||
|
type: integer
|
||||||
|
Project:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
created:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
|
||||||
securityDefinitions:
|
# securityDefinitions:
|
||||||
cookie:
|
# cookie:
|
||||||
type: apiKey
|
# type: apiKey
|
||||||
name: Cookie
|
# name: Cookie
|
||||||
in: header
|
# in: header
|
||||||
|
# bearer:
|
||||||
|
# type: apiKey
|
||||||
|
# name: Authorization
|
||||||
|
# in: header
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
project_id:
|
||||||
|
name: project_id
|
||||||
|
description: Project ID
|
||||||
|
in: path
|
||||||
|
type: integer
|
||||||
|
required: true
|
||||||
|
user_id:
|
||||||
|
name: user_id
|
||||||
|
description: User ID
|
||||||
|
in: path
|
||||||
|
type: integer
|
||||||
|
required: true
|
||||||
|
|
||||||
paths:
|
paths:
|
||||||
/ping:
|
/ping:
|
||||||
@ -61,8 +101,25 @@ paths:
|
|||||||
description: Successful "PONG" reply
|
description: Successful "PONG" reply
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/PONG"
|
$ref: "#/definitions/PONG"
|
||||||
|
|
||||||
|
/ws:
|
||||||
|
get:
|
||||||
|
summary: Websocket handler
|
||||||
|
schemes:
|
||||||
|
- ws
|
||||||
|
- wss
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: OK
|
||||||
|
# security:
|
||||||
|
# - cookie: []
|
||||||
|
# - bearer: []
|
||||||
|
|
||||||
|
# Authentication
|
||||||
/auth/login:
|
/auth/login:
|
||||||
post:
|
post:
|
||||||
|
tags:
|
||||||
|
- authentication
|
||||||
summary: Performs Login
|
summary: Performs Login
|
||||||
description: |
|
description: |
|
||||||
Upon success you will be logged in
|
Upon success you will be logged in
|
||||||
@ -79,26 +136,162 @@ paths:
|
|||||||
description: something in body is missing / is invalid
|
description: something in body is missing / is invalid
|
||||||
/auth/logout:
|
/auth/logout:
|
||||||
post:
|
post:
|
||||||
|
tags:
|
||||||
|
- authentication
|
||||||
summary: Destroys current session
|
summary: Destroys current session
|
||||||
responses:
|
responses:
|
||||||
204:
|
204:
|
||||||
description: Your session was successfully nuked
|
description: Your session was successfully nuked
|
||||||
/ws:
|
|
||||||
get:
|
# User stuff
|
||||||
summary: Websocket handler
|
|
||||||
schemes:
|
|
||||||
- ws
|
|
||||||
- wss
|
|
||||||
responses:
|
|
||||||
200:
|
|
||||||
description: OK
|
|
||||||
security:
|
|
||||||
- cookie: []
|
|
||||||
/user:
|
/user:
|
||||||
get:
|
get:
|
||||||
|
tags:
|
||||||
|
- user
|
||||||
summary: Fetch logged in user
|
summary: Fetch logged in user
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: User
|
description: User
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/definitions/User"
|
$ref: "#/definitions/User"
|
||||||
|
|
||||||
|
/user/tokens:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- authentication
|
||||||
|
- user
|
||||||
|
summary: Fetch API tokens for user
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: API Tokens
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/definitions/APIToken"
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- authentication
|
||||||
|
- user
|
||||||
|
summary: Create an API token
|
||||||
|
responses:
|
||||||
|
201:
|
||||||
|
description: API Token
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/APIToken"
|
||||||
|
/user/tokens/{api_token_id}:
|
||||||
|
parameters:
|
||||||
|
- name: api_token_id
|
||||||
|
in: path
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
delete:
|
||||||
|
tags:
|
||||||
|
- authentication
|
||||||
|
- user
|
||||||
|
summary: Expires API token
|
||||||
|
responses:
|
||||||
|
204:
|
||||||
|
description: Expired API Token
|
||||||
|
|
||||||
|
# Projects
|
||||||
|
/projects:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- projects
|
||||||
|
summary: Get projects
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: List of projects
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/definitions/Project"
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- projects
|
||||||
|
summary: Create a new project
|
||||||
|
parameters:
|
||||||
|
- name: Project
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Project'
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Created project
|
||||||
|
/project/{project_id}:
|
||||||
|
parameters:
|
||||||
|
- $ref: "#/parameters/project_id"
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- project
|
||||||
|
summary: Fetch project
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Project
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/Project"
|
||||||
|
|
||||||
|
# User management
|
||||||
|
/project/{project_id}/users:
|
||||||
|
parameters:
|
||||||
|
- $ref: "#/parameters/project_id"
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- project
|
||||||
|
summary: Get users linked to project
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Users
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/definitions/User"
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- project
|
||||||
|
summary: Link user to project
|
||||||
|
parameters:
|
||||||
|
- name: User
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
user_id:
|
||||||
|
type: integer
|
||||||
|
format: userID
|
||||||
|
admin:
|
||||||
|
type: boolean
|
||||||
|
responses:
|
||||||
|
204:
|
||||||
|
description: User added
|
||||||
|
/project/{project_id}/users/{user_id}:
|
||||||
|
parameters:
|
||||||
|
- $ref: "#/parameters/project_id"
|
||||||
|
- $ref: "#/parameters/user_id"
|
||||||
|
delete:
|
||||||
|
tags:
|
||||||
|
- project
|
||||||
|
summary: Removes user from project
|
||||||
|
responses:
|
||||||
|
204:
|
||||||
|
description: User removed
|
||||||
|
/project/{project_id}/users/{user_id}/admin:
|
||||||
|
parameters:
|
||||||
|
- $ref: "#/parameters/project_id"
|
||||||
|
- $ref: "#/parameters/user_id"
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- project
|
||||||
|
summary: Makes user admin
|
||||||
|
responses:
|
||||||
|
204:
|
||||||
|
description: User made administrator
|
||||||
|
delete:
|
||||||
|
tags:
|
||||||
|
- project
|
||||||
|
summary: Revoke admin privileges
|
||||||
|
responses:
|
||||||
|
204:
|
||||||
|
description: User admin privileges revoked
|
||||||
|
Loading…
Reference in New Issue
Block a user