feat: add admin session api
This commit is contained in:
parent
e35d0cbcd6
commit
217410e9a4
|
@ -3,6 +3,7 @@ package test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/resolvers"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -18,7 +19,7 @@ func aminLoginTests(s TestSetup, t *testing.T) {
|
|||
assert.NotNil(t, err)
|
||||
|
||||
res, err := resolvers.AdminLoginResolver(ctx, model.AdminLoginInput{
|
||||
AdminSecret: "admin",
|
||||
AdminSecret: constants.EnvData.ADMIN_SECRET,
|
||||
})
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
|
28
server/__test__/admin_session_test.go
Normal file
28
server/__test__/admin_session_test.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/resolvers"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func aminSessionTests(s TestSetup, t *testing.T) {
|
||||
t.Run(`should get admin session`, func(t *testing.T) {
|
||||
req, ctx := createContext(s)
|
||||
_, err := resolvers.AdminSession(ctx)
|
||||
log.Println("error:", err)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
h, err := utils.HashPassword(constants.EnvData.ADMIN_SECRET)
|
||||
assert.Nil(t, err)
|
||||
req.Header.Add("Authorization", "Bearer "+h)
|
||||
res, err := resolvers.AdminSession(ctx)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Greater(t, len(res.AccessToken), 0)
|
||||
})
|
||||
}
|
|
@ -43,6 +43,7 @@ func TestResolvers(t *testing.T) {
|
|||
deleteUserTest(s, t)
|
||||
updateUserTest(s, t)
|
||||
aminLoginTests(s, t)
|
||||
aminSessionTests(s, t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,6 @@ type DirectiveRoot struct {
|
|||
type ComplexityRoot struct {
|
||||
AdminLoginResponse struct {
|
||||
AccessToken func(childComplexity int) int
|
||||
ExpiresAt func(childComplexity int) int
|
||||
Message func(childComplexity int) int
|
||||
}
|
||||
|
||||
|
@ -175,13 +174,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
|
||||
return e.complexity.AdminLoginResponse.AccessToken(childComplexity), true
|
||||
|
||||
case "AdminLoginResponse.expires_at":
|
||||
if e.complexity.AdminLoginResponse.ExpiresAt == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.AdminLoginResponse.ExpiresAt(childComplexity), true
|
||||
|
||||
case "AdminLoginResponse.message":
|
||||
if e.complexity.AdminLoginResponse.Message == nil {
|
||||
break
|
||||
|
@ -772,7 +764,6 @@ type Response {
|
|||
type AdminLoginResponse {
|
||||
message: String!
|
||||
access_token: String!
|
||||
expires_at: Int64!
|
||||
}
|
||||
|
||||
input AdminLoginInput {
|
||||
|
@ -1193,41 +1184,6 @@ func (ec *executionContext) _AdminLoginResponse_access_token(ctx context.Context
|
|||
return ec.marshalNString2string(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _AdminLoginResponse_expires_at(ctx context.Context, field graphql.CollectedField, obj *model.AdminLoginResponse) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "AdminLoginResponse",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.ExpiresAt, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(int64)
|
||||
fc.Result = res
|
||||
return ec.marshalNInt642int64(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _AuthResponse_message(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -4979,11 +4935,6 @@ func (ec *executionContext) _AdminLoginResponse(ctx context.Context, sel ast.Sel
|
|||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "expires_at":
|
||||
out.Values[i] = ec._AdminLoginResponse_expires_at(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
|
@ -5754,21 +5705,6 @@ func (ec *executionContext) marshalNID2string(ctx context.Context, sel ast.Selec
|
|||
return res
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNInt642int64(ctx context.Context, v interface{}) (int64, error) {
|
||||
res, err := graphql.UnmarshalInt64(v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNInt642int64(ctx context.Context, sel ast.SelectionSet, v int64) graphql.Marshaler {
|
||||
res := graphql.MarshalInt64(v)
|
||||
if res == graphql.Null {
|
||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNLoginInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐLoginInput(ctx context.Context, v interface{}) (model.LoginInput, error) {
|
||||
res, err := ec.unmarshalInputLoginInput(ctx, v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
|
|
|
@ -9,7 +9,6 @@ type AdminLoginInput struct {
|
|||
type AdminLoginResponse struct {
|
||||
Message string `json:"message"`
|
||||
AccessToken string `json:"access_token"`
|
||||
ExpiresAt int64 `json:"expires_at"`
|
||||
}
|
||||
|
||||
type AuthResponse struct {
|
||||
|
|
|
@ -65,7 +65,6 @@ type Response {
|
|||
type AdminLoginResponse {
|
||||
message: String!
|
||||
access_token: String!
|
||||
expires_at: Int64!
|
||||
}
|
||||
|
||||
input AdminLoginInput {
|
||||
|
|
|
@ -5,7 +5,6 @@ package graph
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/graph/generated"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
|
@ -81,7 +80,7 @@ func (r *queryResolver) VerificationRequests(ctx context.Context) ([]*model.Veri
|
|||
}
|
||||
|
||||
func (r *queryResolver) AdminSession(ctx context.Context) (*model.AdminLoginResponse, error) {
|
||||
panic(fmt.Errorf("not implemented"))
|
||||
return resolvers.AdminSession(ctx)
|
||||
}
|
||||
|
||||
// Mutation returns generated.MutationResolver implementation.
|
||||
|
|
|
@ -3,38 +3,32 @@ package resolvers
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/enum"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/session"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
)
|
||||
|
||||
func AdminLoginResolver(ctx context.Context, params model.AdminLoginInput) (*model.AdminLoginResponse, error) {
|
||||
gc, err := utils.GinContextFromContext(ctx)
|
||||
var res *model.AdminLoginResponse
|
||||
|
||||
if err != nil {
|
||||
log.Println("=> error:", err)
|
||||
return res, err
|
||||
}
|
||||
|
||||
if params.AdminSecret != constants.EnvData.ADMIN_SECRET {
|
||||
return nil, fmt.Errorf(`invalid admin secret`)
|
||||
return res, fmt.Errorf(`invalid admin secret`)
|
||||
}
|
||||
|
||||
refreshToken, _, _ := utils.CreateAdminAuthToken(enum.RefreshToken, gc)
|
||||
accessToken, expiresAt, _ := utils.CreateAdminAuthToken(enum.AccessToken, gc)
|
||||
|
||||
currentTime := time.Now().Unix()
|
||||
tokenId := fmt.Sprintf("authorizer_admin_%d", currentTime)
|
||||
session.SetToken(tokenId, accessToken, refreshToken)
|
||||
utils.SetAdminCookie(gc, accessToken)
|
||||
hashedKey, err := utils.HashPassword(constants.EnvData.ADMIN_SECRET)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
utils.SetAdminCookie(gc, hashedKey)
|
||||
|
||||
res = &model.AdminLoginResponse{
|
||||
AccessToken: accessToken,
|
||||
ExpiresAt: expiresAt,
|
||||
AccessToken: hashedKey,
|
||||
Message: "admin logged in successfully",
|
||||
}
|
||||
return res, nil
|
||||
|
|
35
server/resolvers/admin_session.go
Normal file
35
server/resolvers/admin_session.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package resolvers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
)
|
||||
|
||||
func AdminSession(ctx context.Context) (*model.AdminLoginResponse, error) {
|
||||
gc, err := utils.GinContextFromContext(ctx)
|
||||
var res *model.AdminLoginResponse
|
||||
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
if !utils.IsSuperAdmin(gc) {
|
||||
return res, fmt.Errorf("unauthorized")
|
||||
}
|
||||
|
||||
hashedKey, err := utils.HashPassword(constants.EnvData.ADMIN_SECRET)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
utils.SetAdminCookie(gc, hashedKey)
|
||||
|
||||
res = &model.AdminLoginResponse{
|
||||
AccessToken: hashedKey,
|
||||
Message: "admin logged in successfully",
|
||||
}
|
||||
return res, nil
|
||||
}
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/robertkrimen/otto"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (string, int64, error) {
|
||||
|
@ -124,32 +125,8 @@ func VerifyAuthToken(token string) (map[string]interface{}, error) {
|
|||
return res, nil
|
||||
}
|
||||
|
||||
func CreateAdminAuthToken(tokenType enum.TokenType, c *gin.Context) (string, int64, error) {
|
||||
t := jwt.New(jwt.GetSigningMethod(constants.EnvData.JWT_TYPE))
|
||||
expiryBound := time.Hour
|
||||
if tokenType == enum.RefreshToken {
|
||||
// expires in 1 year
|
||||
expiryBound = time.Hour * 8760
|
||||
}
|
||||
|
||||
expiresAt := time.Now().Add(expiryBound).Unix()
|
||||
|
||||
customClaims := jwt.MapClaims{
|
||||
"exp": expiresAt,
|
||||
"iat": time.Now().Unix(),
|
||||
"user_agent": GetUserAgent(c.Request),
|
||||
"ip": GetIP(c.Request),
|
||||
"role": "authorizer_admin",
|
||||
"created_at": time.Now().Unix(),
|
||||
}
|
||||
|
||||
t.Claims = customClaims
|
||||
|
||||
token, err := t.SignedString([]byte(constants.EnvData.JWT_SECRET))
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
return token, expiresAt, nil
|
||||
func CreateAdminAuthToken(tokenType enum.TokenType, c *gin.Context) (string, error) {
|
||||
return HashPassword(constants.EnvData.ADMIN_SECRET)
|
||||
}
|
||||
|
||||
func GetAdminAuthToken(gc *gin.Context) (string, error) {
|
||||
|
@ -162,6 +139,11 @@ func GetAdminAuthToken(gc *gin.Context) (string, error) {
|
|||
}
|
||||
|
||||
token = strings.TrimPrefix(auth, "Bearer ")
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(token), []byte(constants.EnvData.ADMIN_SECRET))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user