Skip to content

Commit

Permalink
[ChaosCenter]: Revoke the token when the user logout (#4085)
Browse files Browse the repository at this point in the history
* feat: add logout logic

Signed-off-by: namkyu1999 <lak9348@konkuk.ac.kr>

* feat: add logout api to frontend

Signed-off-by: namkyu1999 <lak9348@konkuk.ac.kr>

* fix: rename collection

Signed-off-by: namkyu1999 <lak9348@konkuk.ac.kr>

* feat: add IsRevokedToken logic to graphql-server

Signed-off-by: namkyu1999 <lak9348@konkuk.ac.kr>

* fix: fix testcases

Signed-off-by: namkyu1999 <lak9348@konkuk.ac.kr>

* fix: fix minor bugs

Signed-off-by: namkyu1999 <lak9348@konkuk.ac.kr>

* feat: add logout logic to chaoscenter/auth

Signed-off-by: namkyu1999 <lak9348@konkuk.ac.kr>

* feat: add logout logic to chaoscenter/web

Signed-off-by: namkyu1999 <lak9348@konkuk.ac.kr>

* feat: add logout logic to chaoscenter/graphql

Signed-off-by: namkyu1999 <lak9348@konkuk.ac.kr>

* fix: fix minor bugs

Signed-off-by: namkyu1999 <lak9348@konkuk.ac.kr>

* fix: fix auth logic

Signed-off-by: namkyu1999 <lak9348@konkuk.ac.kr>

* feat: update swagger

Signed-off-by: namkyu1999 <lak9348@konkuk.ac.kr>

* fix: add mutation api

Signed-off-by: namkyu1999 <lak9348@konkuk.ac.kr>

* fix: rename field

Signed-off-by: namkyu1999 <lak9348@konkuk.ac.kr>

* fix: minor

Signed-off-by: namkyu1999 <lak9348@konkuk.ac.kr>

---------

Signed-off-by: namkyu1999 <lak9348@konkuk.ac.kr>
  • Loading branch information
namkyu1999 committed Aug 14, 2023
1 parent 8e1b602 commit 2d64b25
Show file tree
Hide file tree
Showing 42 changed files with 710 additions and 223 deletions.
8 changes: 8 additions & 0 deletions chaoscenter/Readme.md → chaoscenter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ metrics:
enabled: false
prometheusRule:
enabled: false

# bitnami/mongodb is not yet supported on ARM.
# Using unofficial tools to build bitnami/mongodb (arm64 support)
# more info: https://github.com/ZCube/bitnami-compat
#image:
# registry: ghcr.io/zcube
# repository: bitnami-compat/mongodb
# tag: 6.0.5
```

```shell
Expand Down
3 changes: 1 addition & 2 deletions chaoscenter/authentication/api/handlers/grpc/grpc_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"strconv"

"github.com/litmuschaos/litmus/chaoscenter/authentication/api/middleware"
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/presenter/protos"
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/entities"
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/validations"
Expand All @@ -16,7 +15,7 @@ import (

func (s *ServerGrpc) ValidateRequest(ctx context.Context,
inputRequest *protos.ValidationRequest) (*protos.ValidationResponse, error) {
token, err := middleware.ValidateToken(inputRequest.Jwt)
token, err := s.ValidateToken(inputRequest.Jwt)
if err != nil {
return &protos.ValidationResponse{Error: err.Error(), IsValid: false}, err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func DexCallback(userService services.ApplicationService) gin.HandlerFunc {
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
return
}
jwtToken, err := signedInUser.GetSignedJWT()
jwtToken, err := userService.GetSignedJWT(signedInUser)
if err != nil {
log.Error(err)
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
Expand Down
26 changes: 25 additions & 1 deletion chaoscenter/authentication/api/handlers/rest/user_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
"golang.org/x/crypto/bcrypt"
)

const BearerSchema = "Bearer "

func CreateUser(service services.ApplicationService) gin.HandlerFunc {
return func(c *gin.Context) {
userRole := c.MustGet("role").(string)
Expand Down Expand Up @@ -203,7 +205,7 @@ func LoginUser(service services.ApplicationService) gin.HandlerFunc {
return
}

token, err := user.GetSignedJWT()
token, err := service.GetSignedJWT(user)
if err != nil {
log.Error(err)
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
Expand Down Expand Up @@ -265,6 +267,28 @@ func LoginUser(service services.ApplicationService) gin.HandlerFunc {
}
}

// LogoutUser revokes the token passed in the Authorization header
func LogoutUser(service services.ApplicationService) gin.HandlerFunc {
return func(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.AbortWithStatusJSON(utils.ErrorStatusCodes[utils.ErrUnauthorized], presenter.CreateErrorResponse(utils.ErrUnauthorized))
return
}
tokenString := authHeader[len(BearerSchema):]
// revoke token
err := service.RevokeToken(tokenString)
if err != nil {
log.Error(err)
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError))
return
}
c.JSON(200, gin.H{
"message": "successfully logged out",
})
}
}

func UpdatePassword(service services.ApplicationService) gin.HandlerFunc {
return func(c *gin.Context) {
var userPasswordRequest entities.UserPassword
Expand Down
26 changes: 20 additions & 6 deletions chaoscenter/authentication/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/misc"
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/project"
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/session"
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/user"
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"

Expand All @@ -37,6 +38,8 @@ type Config struct {
}

func init() {
log.SetFormatter(&log.JSONFormatter{})
log.SetReportCaller(true)
printVersion()

var c Config
Expand All @@ -48,7 +51,7 @@ func init() {
}

func main() {
// send logs to stderr so we can use 'kubectl logs'
// send logs to stderr, so we can use 'kubectl logs'
_ = flag.Set("logtostderr", "true")
_ = flag.Set("v", "3")

Expand All @@ -64,18 +67,27 @@ func main() {
// Creating User Collection
err = utils.CreateCollection(utils.UserCollection, db)
if err != nil {
log.Fatalf("failed to create collection %s", err)
log.Errorf("failed to create collection %s", err)
}

err = utils.CreateIndex(utils.UserCollection, utils.UsernameField, db)
if err != nil {
log.Fatalf("failed to create index %s", err)
log.Errorf("failed to create index %s", err)
}

// Creating Project Collection
err = utils.CreateCollection(utils.ProjectCollection, db)
if err != nil {
log.Fatalf("failed to create collection %s", err)
log.Errorf("failed to create collection %s", err)
}

// Creating Session Collection
if err = utils.CreateCollection(utils.RevokedTokenCollection, db); err != nil {
log.Errorf("failed to create collection %s", err)
}

if err = utils.CreateTTLIndex(utils.RevokedTokenCollection, db); err != nil {
log.Errorf("failed to create index %s", err)
}

userCollection := db.Collection(utils.UserCollection)
Expand All @@ -84,9 +96,12 @@ func main() {
projectCollection := db.Collection(utils.ProjectCollection)
projectRepo := project.NewRepo(projectCollection)

revokedTokenCollection := db.Collection(utils.RevokedTokenCollection)
sessionRepo := session.NewRepo(revokedTokenCollection)

miscRepo := misc.NewRepo(db, client)

applicationService := services.NewService(userRepo, projectRepo, miscRepo, db)
applicationService := services.NewService(userRepo, projectRepo, miscRepo, sessionRepo, db)

validatedAdminSetup(applicationService)

Expand All @@ -95,7 +110,6 @@ func main() {
}

func validatedAdminSetup(service services.ApplicationService) {

// Assigning UID to admin
uID := uuid.Must(uuid.NewRandom()).String()

Expand Down
31 changes: 11 additions & 20 deletions chaoscenter/authentication/api/middleware/jwt_middlware.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
package middleware

import (
"fmt"

"github.com/litmuschaos/litmus/chaoscenter/authentication/api/presenter"
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"

"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt"
"github.com/sirupsen/logrus"
"github.com/litmuschaos/litmus/chaoscenter/authentication/api/presenter"
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/services"
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"
log "github.com/sirupsen/logrus"
)

// JwtMiddleware is a Gin Middleware that authorises requests
func JwtMiddleware() gin.HandlerFunc {
func JwtMiddleware(service services.ApplicationService) gin.HandlerFunc {
return func(c *gin.Context) {
const BearerSchema = "Bearer "
authHeader := c.GetHeader("Authorization")
Expand All @@ -21,28 +19,21 @@ func JwtMiddleware() gin.HandlerFunc {
return
}
tokenString := authHeader[len(BearerSchema):]
token, err := ValidateToken(tokenString)
token, err := service.ValidateToken(tokenString)
if err != nil {
log.Error(err)
c.AbortWithStatusJSON(utils.ErrorStatusCodes[utils.ErrUnauthorized], presenter.CreateErrorResponse(utils.ErrUnauthorized))
return
}
if token.Valid {
claims := token.Claims.(jwt.MapClaims)
c.Set("username", claims["username"])
c.Set("uid", claims["uid"])
c.Set("role", claims["role"])
c.Next()
} else {
logrus.Info(err)
c.AbortWithStatusJSON(utils.ErrorStatusCodes[utils.ErrUnauthorized], presenter.CreateErrorResponse(utils.ErrUnauthorized))
return
}
}
}

// ValidateToken validates the given JWT Token
func ValidateToken(encodedToken string) (*jwt.Token, error) {
return jwt.Parse(encodedToken, func(token *jwt.Token) (interface{}, error) {
if _, isValid := token.Method.(*jwt.SigningMethodHMAC); !isValid {
return nil, fmt.Errorf("invalid token %s", token.Header["alg"])
}
return []byte(utils.JwtSecret), nil
})

}
2 changes: 1 addition & 1 deletion chaoscenter/authentication/api/routes/project_router.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (

// ProjectRouter creates all the required routes for project related purposes.
func ProjectRouter(router *gin.Engine, service services.ApplicationService) {
router.Use(middleware.JwtMiddleware())
router.Use(middleware.JwtMiddleware(service))
router.GET("/get_project/:project_id", rest.GetProject(service))
router.GET("/get_project_members/:project_id/:state", rest.GetActiveProjectMembers(service))
router.GET("/get_user_with_project/:username", rest.GetUserWithProject(service))
Expand Down
3 changes: 2 additions & 1 deletion chaoscenter/authentication/api/routes/user_router.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import (
// UserRouter creates all the required routes for user authentications purposes.
func UserRouter(router *gin.Engine, service services.ApplicationService) {
router.POST("/login", rest.LoginUser(service))
router.Use(middleware.JwtMiddleware())
router.POST("/logout", rest.LogoutUser(service))
router.Use(middleware.JwtMiddleware(service))
router.POST("/update/password", rest.UpdatePassword(service))
router.POST("/reset/password", rest.ResetPassword(service))
router.POST("/create_user", rest.CreateUser(service))
Expand Down
8 changes: 8 additions & 0 deletions chaoscenter/authentication/pkg/entities/session.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package entities

// RevokedToken struct for storing revoked tokens
type RevokedToken struct {
Token string `bson:"token"`
ExpiresAt int64 `bson:"expires_at"`
CreatedAt int64 `bson:"created_at"`
}
25 changes: 0 additions & 25 deletions chaoscenter/authentication/pkg/entities/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@ package entities

import (
"net/mail"
"time"

"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/utils"

"github.com/golang-jwt/jwt"
"github.com/sirupsen/logrus"
)

// Role states the role of the user in the portal
Expand Down Expand Up @@ -96,22 +90,3 @@ func (user *User) IsEmailValid(email string) bool {
_, err := mail.ParseAddress(email)
return err == nil
}

// GetSignedJWT generates the JWT Token for the user object
func (user *User) GetSignedJWT() (string, error) {

token := jwt.New(jwt.SigningMethodHS512)
claims := token.Claims.(jwt.MapClaims)
claims["uid"] = user.ID
claims["role"] = user.Role
claims["username"] = user.Username
claims["exp"] = time.Now().Add(time.Minute * time.Duration(utils.JWTExpiryDuration)).Unix()

tokenString, err := token.SignedString([]byte(utils.JwtSecret))
if err != nil {
logrus.Info(err)
return "", err
}

return tokenString, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package services
import (
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/misc"
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/project"
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/session"
"github.com/litmuschaos/litmus/chaoscenter/authentication/pkg/user"

"go.mongodb.org/mongo-driver/mongo"
Expand All @@ -13,20 +14,23 @@ type ApplicationService interface {
projectService
transactionService
miscService
sessionService
}

type applicationService struct {
userRepository user.Repository
projectRepository project.Repository
miscRepository misc.Repository
sessionRepository session.Repository
db *mongo.Database
}

// NewService creates a new instance of this service
func NewService(userRepo user.Repository, projectRepo project.Repository, miscRepo misc.Repository, db *mongo.Database) ApplicationService {
func NewService(userRepo user.Repository, projectRepo project.Repository, miscRepo misc.Repository, sessionRepo session.Repository, db *mongo.Database) ApplicationService {
return &applicationService{
userRepository: userRepo,
projectRepository: projectRepo,
sessionRepository: sessionRepo,
db: db,
miscRepository: miscRepo,
}
Expand Down
Loading

0 comments on commit 2d64b25

Please sign in to comment.