forked from litmuschaos/litmus
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Added enhancement in authentication server (litmuschaos#4024)
* base commit to add dockerfile Signed-off-by: Sarthak Jain <sarthak.jain@harness.io> * Added utility functions Signed-off-by: Sarthak Jain <sarthak.jain@harness.io> * Added schemas Signed-off-by: Sarthak Jain <sarthak.jain@harness.io> * Added presenters Signed-off-by: Sarthak Jain <sarthak.jain@harness.io> * Added pkg, handlers and roles Signed-off-by: Sarthak Jain <sarthak.jain@harness.io> * Code optimisation Signed-off-by: Sarthak Jain <sarthak.jain@harness.io> --------- Signed-off-by: Sarthak Jain <sarthak.jain@harness.io> Co-authored-by: Saranya Jena <saranya.jena@harness.io> Signed-off-by: SohamRatnaparkhi <soham.ratnaparkhi@gmail.com>
- Loading branch information
1 parent
3d858e9
commit e151e20
Showing
42 changed files
with
4,638 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# BUILD STAGE | ||
FROM golang:1.18 AS builder | ||
|
||
ARG TARGETOS=linux | ||
ARG TARGETARCH | ||
|
||
ADD . /auth-server | ||
WORKDIR /auth-server | ||
|
||
ENV GOOS=${TARGETOS} \ | ||
GOARCH=${TARGETARCH} | ||
|
||
RUN go env | ||
|
||
RUN CGO_ENABLED=0 go build -o /output/server -v ./api/ | ||
|
||
# Packaging stage | ||
# Image source: https://github.com/litmuschaos/test-tools/blob/master/custom/hardened-alpine/infra/Dockerfile | ||
# The base image is non-root (have litmus user) with default litmus directory. | ||
FROM litmuschaos/infra-alpine | ||
|
||
LABEL maintainer="LitmusChaos" | ||
|
||
COPY --from=builder /output/server /litmus | ||
|
||
CMD ["./server"] | ||
|
||
EXPOSE 3000 |
103 changes: 103 additions & 0 deletions
103
chaoscenter/authentication/api/handlers/grpc/grpc_handler.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package grpc | ||
|
||
import ( | ||
"context" | ||
"litmus/litmus-portal/authentication/api/middleware" | ||
"litmus/litmus-portal/authentication/api/presenter/protos" | ||
"litmus/litmus-portal/authentication/pkg/entities" | ||
"litmus/litmus-portal/authentication/pkg/validations" | ||
"strconv" | ||
|
||
log "github.com/sirupsen/logrus" | ||
|
||
"github.com/golang-jwt/jwt" | ||
) | ||
|
||
func (s *ServerGrpc) ValidateRequest(ctx context.Context, | ||
inputRequest *protos.ValidationRequest) (*protos.ValidationResponse, error) { | ||
token, err := middleware.ValidateToken(inputRequest.Jwt) | ||
if err != nil { | ||
return &protos.ValidationResponse{Error: err.Error(), IsValid: false}, err | ||
} | ||
claims := token.Claims.(jwt.MapClaims) | ||
uid := claims["uid"].(string) | ||
err = validations.RbacValidator(uid, inputRequest.ProjectId, | ||
inputRequest.RequiredRoles, inputRequest.Invitation, s.ApplicationService) | ||
if err != nil { | ||
return &protos.ValidationResponse{Error: err.Error(), IsValid: false}, err | ||
} | ||
return &protos.ValidationResponse{Error: "", IsValid: true}, nil | ||
} | ||
|
||
func (s *ServerGrpc) GetProjectById(ctx context.Context, | ||
inputRequest *protos.GetProjectByIdRequest) (*protos.GetProjectByIdResponse, error) { | ||
|
||
project, err := s.ApplicationService.GetProjectByProjectID(inputRequest.ProjectID) | ||
if err != nil { | ||
log.Error(err) | ||
return nil, err | ||
} | ||
|
||
// Fetching user ids of all the members in the project | ||
var uids []string | ||
|
||
for _, member := range project.Members { | ||
uids = append(uids, member.UserID) | ||
} | ||
|
||
memberMap := make(map[string]entities.User) | ||
|
||
authUsers, err := s.ApplicationService.FindUsersByUID(uids) | ||
for _, authUser := range *authUsers { | ||
memberMap[authUser.ID] = authUser | ||
} | ||
|
||
var projectMembers []*protos.ProjectMembers | ||
|
||
// Adding additional details of project members | ||
for _, member := range project.Members { | ||
var projectMember protos.ProjectMembers | ||
projectMember.Email = memberMap[member.UserID].Email | ||
projectMember.UserName = memberMap[member.UserID].UserName | ||
projectMember.Invitation = string(member.Invitation) | ||
projectMember.Uid = member.UserID | ||
projectMember.JoinedAt = member.JoinedAt | ||
projectMembers = append(projectMembers, &projectMember) | ||
} | ||
|
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &protos.GetProjectByIdResponse{ | ||
Id: project.ID, | ||
Name: project.Name, | ||
Members: projectMembers, | ||
State: "", | ||
CreatedAt: strconv.FormatInt(project.CreatedAt, 10), | ||
UpdatedAt: strconv.FormatInt(project.UpdatedAt, 10), | ||
}, nil | ||
} | ||
|
||
func (s *ServerGrpc) GetUserById(ctx context.Context, | ||
inputRequest *protos.GetUserByIdRequest) (*protos.GetUserByIdResponse, error) { | ||
user, err := s.ApplicationService.GetUser(inputRequest.UserID) | ||
if err != nil { | ||
log.Error(err) | ||
return nil, err | ||
} | ||
var deactivatedAt string | ||
if user.DeactivatedAt != nil { | ||
deactivatedAt = strconv.FormatInt(*user.DeactivatedAt, 10) | ||
} | ||
return &protos.GetUserByIdResponse{ | ||
Id: user.ID, | ||
Name: user.Name, | ||
Username: user.UserName, | ||
CreatedAt: strconv.FormatInt(user.CreatedAt, 10), | ||
UpdatedAt: strconv.FormatInt(user.UpdatedAt, 10), | ||
DeactivatedAt: deactivatedAt, | ||
Role: string(user.Role), | ||
Email: user.Email, | ||
}, nil | ||
} |
11 changes: 11 additions & 0 deletions
11
chaoscenter/authentication/api/handlers/grpc/grpc_server.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package grpc | ||
|
||
import ( | ||
"litmus/litmus-portal/authentication/api/presenter/protos" | ||
"litmus/litmus-portal/authentication/pkg/services" | ||
) | ||
|
||
type ServerGrpc struct { | ||
services.ApplicationService | ||
protos.UnimplementedAuthRpcServiceServer | ||
} |
126 changes: 126 additions & 0 deletions
126
chaoscenter/authentication/api/handlers/rest/dex_auth_handler.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package rest | ||
|
||
import ( | ||
"context" | ||
"litmus/litmus-portal/authentication/api/presenter" | ||
"litmus/litmus-portal/authentication/pkg/entities" | ||
"litmus/litmus-portal/authentication/pkg/services" | ||
"litmus/litmus-portal/authentication/pkg/utils" | ||
"net/http" | ||
"time" | ||
|
||
"github.com/coreos/go-oidc/v3/oidc" | ||
"github.com/gin-gonic/gin" | ||
log "github.com/sirupsen/logrus" | ||
"golang.org/x/oauth2" | ||
) | ||
|
||
func oAuthDexConfig() (*oauth2.Config, *oidc.IDTokenVerifier, error) { | ||
ctx := oidc.ClientContext(context.Background(), &http.Client{}) | ||
provider, err := oidc.NewProvider(ctx, utils.DexOIDCIssuer) | ||
if err != nil { | ||
log.Errorf("OAuth Error: Something went wrong with OIDC provider %s", err) | ||
return nil, nil, err | ||
} | ||
return &oauth2.Config{ | ||
RedirectURL: utils.DexCallBackURL, | ||
ClientID: utils.DexClientID, | ||
ClientSecret: utils.DexClientSecret, | ||
Scopes: []string{"openid", "profile", "email"}, | ||
Endpoint: provider.Endpoint(), | ||
}, provider.Verifier(&oidc.Config{ClientID: utils.DexClientID}), nil | ||
} | ||
|
||
// DexLogin handles and redirects to DexServer to proceed with OAuth | ||
func DexLogin() gin.HandlerFunc { | ||
return func(c *gin.Context) { | ||
|
||
dexToken, err := utils.GenerateOAuthJWT() | ||
if err != nil { | ||
log.Error(err) | ||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError)) | ||
return | ||
} | ||
config, _, err := oAuthDexConfig() | ||
if err != nil { | ||
log.Error(err) | ||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError)) | ||
return | ||
} | ||
url := config.AuthCodeURL(dexToken) | ||
c.Redirect(http.StatusTemporaryRedirect, url) | ||
} | ||
} | ||
|
||
// DexCallback is the handler that creates/logs in the user from Dex and provides JWT to frontend via a redirect | ||
func DexCallback(userService services.ApplicationService) gin.HandlerFunc { | ||
return func(c *gin.Context) { | ||
incomingState := c.Query("state") | ||
validated, err := utils.ValidateOAuthJWT(incomingState) | ||
if !validated { | ||
c.Redirect(http.StatusTemporaryRedirect, "/") | ||
} | ||
config, verifier, err := oAuthDexConfig() | ||
if err != nil { | ||
log.Error(err) | ||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError)) | ||
return | ||
} | ||
token, err := config.Exchange(context.Background(), c.Query("code")) | ||
if err != nil { | ||
log.Error(err) | ||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError)) | ||
return | ||
} | ||
|
||
rawIDToken, ok := token.Extra("id_token").(string) | ||
if !ok { | ||
log.Error("OAuth Error: no raw id_token found") | ||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError)) | ||
return | ||
} | ||
idToken, err := verifier.Verify(c, rawIDToken) | ||
if err != nil { | ||
log.Error("OAuth Error: no id_token found") | ||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError)) | ||
return | ||
} | ||
|
||
var claims struct { | ||
Name string | ||
Email string `json:"email"` | ||
Verified bool `json:"email_verified"` | ||
} | ||
if err := idToken.Claims(&claims); err != nil { | ||
log.Error("OAuth Error: claims not found") | ||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError)) | ||
return | ||
} | ||
createdAt := time.Now().Unix() | ||
|
||
var userData = entities.User{ | ||
Name: claims.Name, | ||
Email: claims.Email, | ||
UserName: claims.Email, | ||
Role: entities.RoleUser, | ||
Audit: entities.Audit{ | ||
CreatedAt: createdAt, | ||
UpdatedAt: createdAt, | ||
}, | ||
} | ||
|
||
signedInUser, err := userService.LoginUser(&userData) | ||
if err != nil { | ||
log.Error(err) | ||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError)) | ||
return | ||
} | ||
jwtToken, err := signedInUser.GetSignedJWT() | ||
if err != nil { | ||
log.Error(err) | ||
c.JSON(utils.ErrorStatusCodes[utils.ErrServerError], presenter.CreateErrorResponse(utils.ErrServerError)) | ||
return | ||
} | ||
c.Redirect(http.StatusPermanentRedirect, "/login?jwtToken="+jwtToken) | ||
} | ||
} |
71 changes: 71 additions & 0 deletions
71
chaoscenter/authentication/api/handlers/rest/misc_handlers.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package rest | ||
|
||
import ( | ||
"litmus/litmus-portal/authentication/pkg/entities" | ||
"litmus/litmus-portal/authentication/pkg/services" | ||
|
||
"github.com/gin-gonic/gin" | ||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
func contains(s []string, str string) bool { | ||
for _, v := range s { | ||
if v == str { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} | ||
|
||
type ReadinessAPIStatus struct { | ||
DataBase string `json:"database"` | ||
Collections string `json:"collections"` | ||
} | ||
|
||
// Status will request users list and return, if successful, | ||
// an http code 200 | ||
func Status(service services.ApplicationService) gin.HandlerFunc { | ||
return func(c *gin.Context) { | ||
_, err := service.GetUsers() | ||
if err != nil { | ||
log.Error(err) | ||
c.JSON(500, entities.APIStatus{"down"}) | ||
return | ||
} | ||
c.JSON(200, entities.APIStatus{"up"}) | ||
} | ||
} | ||
|
||
func Readiness(service services.ApplicationService) gin.HandlerFunc { | ||
return func(c *gin.Context) { | ||
var ( | ||
db_flag = "up" | ||
col_flag = "up" | ||
) | ||
|
||
dbs, err := service.ListDataBase() | ||
if !contains(dbs, "auth") { | ||
db_flag = "down" | ||
} | ||
|
||
if err != nil { | ||
log.Error(err) | ||
c.JSON(500, ReadinessAPIStatus{"down", "unknown"}) | ||
return | ||
} | ||
|
||
cols, err := service.ListCollection() | ||
if !contains(cols, "project") || !contains(cols, "users") { | ||
col_flag = "down" | ||
} | ||
|
||
if err != nil { | ||
log.Error(err) | ||
c.JSON(500, ReadinessAPIStatus{db_flag, "down"}) | ||
return | ||
} | ||
|
||
c.JSON(200, ReadinessAPIStatus{db_flag, col_flag}) | ||
} | ||
} |
Oops, something went wrong.