diff --git a/README.md b/README.md
index 8b863da..9e55ca6 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# golang API Skeleton
-[](https://drone.devices.local/mawas/golang-api-skeleton)
+[](https://drone.devices.local/mawas/golang-api-skeleton)
refined skeleton future apis should be based on
diff --git a/api/v1/users/route.go b/api/v1/users/route.go
index a902e41..af5932a 100644
--- a/api/v1/users/route.go
+++ b/api/v1/users/route.go
@@ -1,14 +1,17 @@
package users
-import "github.com/gin-gonic/gin"
+import (
+ "git.devices.local/mawas/golang-api-skeleton/lib/authentication"
+ "github.com/gin-gonic/gin"
+)
// ApplyRoutes applies router to the gin Engine
func ApplyRoutes(r *gin.RouterGroup) *gin.RouterGroup {
users := r.Group("/users")
{
// actions.POST("", middlewares.Authorized, CreateActions)
- // actions.GET("/:id", middlewares.Authorized, ReadAction)
- users.GET("/:username", Read)
+ users.GET("/:username", authentication.Authorized, ReadByID)
+ users.GET("", authentication.Authorized, ReadAll)
// actions.GET("/:id/logs", middlewares.Authorized, actionlogs.ReadActionLogs)
// actions.PATCH("/:id", middlewares.Authorized, UpdateAction)
// actions.PATCH("", middlewares.Authorized, UpdateActions)
diff --git a/api/v1/users/users.go b/api/v1/users/users.go
index e5337dd..57f05c5 100644
--- a/api/v1/users/users.go
+++ b/api/v1/users/users.go
@@ -9,19 +9,37 @@ import (
"gorm.io/gorm"
)
-func Read(c *gin.Context) {
- db := c.MustGet("db").(*gorm.DB)
- cc := c.MustGet("cache").(cache.Cache)
- response := response.Envelope{
+func prepare(c *gin.Context) (response.Envelope, *services.UserService) {
+ resp := response.Envelope{
RequestID: c.MustGet("requestID").(string),
}
- userRepo := repositories.NewUserRepository(db, "01F5FSJXDHWT4HK93B9NB8V5G4", "test", cc)
+ userRepo := repositories.NewUserRepository(
+ c.MustGet("db").(*gorm.DB),
+ c.MustGet("userID").(string),
+ c.MustGet("username").(string),
+ c.MustGet("cache").(cache.Cache),
+ )
userService := services.NewUserService(userRepo)
+ return resp, userService
+}
+
+func ReadByID(c *gin.Context) {
+ resp, userService := prepare(c)
username := c.Param("username")
user, err := userService.ReadByID(username)
if err != nil {
- c.AbortWithStatusJSON(500, response.AppendError(err))
+ c.AbortWithStatusJSON(500, resp.AppendError(err))
+ return
+ }
+ c.JSON(200, resp.SetSuccess(user))
+}
+
+func ReadAll(c *gin.Context) {
+ resp, userService := prepare(c)
+ users, err := userService.ReadAll()
+ if err != nil {
+ c.AbortWithStatusJSON(500, resp.AppendError(err))
return
}
- c.JSON(200, response.SetSuccess(user))
+ c.JSON(200, resp.SetSuccess(users))
}
diff --git a/cmd/server/server.go b/cmd/server/server.go
index 6bd0e11..18110f0 100644
--- a/cmd/server/server.go
+++ b/cmd/server/server.go
@@ -71,23 +71,38 @@ func startServer(version string, appName string, cfgFile string, flagCfg map[str
}
// Prefill cache
// TODO also add deleted users to cache for old references
+ // users
userRepo := repositories.NewUserRepository(db, common.CLIUserID, common.CLIUsername, nil)
userService := services.NewUserService(userRepo)
- if userData, err := userService.ReadAll(); err != nil {
- } else {
- for i := range userData {
- if err := c.Set("user:"+userData[i].ID.String(), userData[i].Username); err != nil { // userID -> username mapping
- return fmt.Errorf("cache:%v", err)
- }
- if err := c.Set("user:"+userData[i].Username, userData[i].ID.String()); err != nil { // username -> userID mapping
- return fmt.Errorf("cache:%v", err)
- }
+ userData, err := userService.ReadAll()
+ if err != nil {
+ return fmt.Errorf("cache:%v", err)
+ }
+ for i := range userData {
+ if err := c.Set("user:"+userData[i].ID.String(), userData[i].Username); err != nil { // userID -> username mapping
+ return fmt.Errorf("cache:%v", err)
+ }
+ if err := c.Set("user:"+userData[i].Username, userData[i].ID.String()); err != nil { // username -> userID mapping
+ return fmt.Errorf("cache:%v", err)
+ }
+ }
+ // tokens
+ tokenRepo := repositories.NewTokenRepository(db, common.CLIUserID, common.CLIUsername, nil)
+ tokenService := services.NewTokenService(tokenRepo)
+ tokenData, err := tokenService.ReadAll()
+ if err != nil {
+ return fmt.Errorf("cache:%v", err)
+ }
+ for i := range tokenData {
+ if err := c.Set("token:"+tokenData[i].Token.String(), tokenData[i].UserID.String()); err != nil { // token -> userID mapping
+ return fmt.Errorf("cache:%v", err)
}
}
- app.Use(middlewares.RequestID())
- app.Use(middlewares.VersionHeader(version))
app.Use(inject("cache", c))
app.Use(inject("db", db))
+ app.Use(middlewares.RequestID())
+ app.Use(middlewares.Authentication())
+ app.Use(middlewares.VersionHeader(version))
// FIXME find out where todo database migration
if err := db.AutoMigrate(&models.Token{}); err != nil {
return fmt.Errorf("database:%v", err)
diff --git a/lib/authentication/authentication.go b/lib/authentication/authentication.go
new file mode 100644
index 0000000..24eef4a
--- /dev/null
+++ b/lib/authentication/authentication.go
@@ -0,0 +1,21 @@
+package authentication
+
+import (
+ "errors"
+ "fmt"
+
+ "git.devices.local/mawas/golang-api-skeleton/lib/response"
+ "github.com/gin-gonic/gin"
+)
+
+// Authorized blocks unauthorized requestors
+func Authorized(c *gin.Context) {
+ var resp response.Envelope
+ userID, exists := c.Get("userID")
+ if !exists {
+ c.AbortWithStatusJSON(403, resp.AppendError(errors.New("unauthorized")))
+ return
+ }
+ fmt.Println("permission check", userID)
+ // TODO add cache perm check here
+}
diff --git a/lib/cache/cache.go b/lib/cache/cache.go
index 7889ac7..c5932bd 100644
--- a/lib/cache/cache.go
+++ b/lib/cache/cache.go
@@ -63,8 +63,11 @@ func (cache bCache) Get(key string) (*string, error) {
}); err != nil {
return nil, err
}
- r := string(result)
- return &r, nil
+ if len(result) > 0 {
+ r := string(result)
+ return &r, nil
+ }
+ return nil, nil
}
// SetWithTTL sets a cache entry
diff --git a/lib/middlewares/authentication.go b/lib/middlewares/authentication.go
new file mode 100644
index 0000000..408139c
--- /dev/null
+++ b/lib/middlewares/authentication.go
@@ -0,0 +1,49 @@
+package middlewares
+
+import (
+ "strings"
+
+ "git.devices.local/mawas/golang-api-skeleton/lib/cache"
+ "github.com/gin-gonic/gin"
+)
+
+func Authentication() gin.HandlerFunc {
+ return func(c *gin.Context) {
+ tokenString, err := c.Cookie("token")
+ appCache := c.MustGet("cache").(cache.Cache)
+ // failed to read cookie
+ if err != nil {
+ // try reading HTTP Header
+ authorization := c.Request.Header.Get("Authorization")
+ if authorization == "" {
+ c.Next()
+ return
+ }
+ sp := strings.Split(authorization, "Bearer ")
+ // invalid token
+ if len(sp) < 2 {
+ c.Next()
+ return
+ }
+ tokenString = sp[1]
+ }
+ // https://datatracker.ietf.org/doc/rfc8959/?include_text=1
+ userID, err := appCache.Get("token:" + tokenString)
+ if err != nil {
+ c.Next()
+ return
+ }
+ if userID != nil {
+ username, err := appCache.Get("user:" + *userID)
+ if err != nil {
+ c.Next()
+ return
+ }
+ if username != nil {
+ c.Set("username", *username)
+ }
+ c.Set("userID", *userID)
+ }
+ c.Next()
+ }
+}