Merge pull request #313 from authorizerdev/feat/add-get-user

Add get user api for admin
This commit is contained in:
Lakhan Samani 2023-01-05 20:17:48 +05:30 committed by GitHub
commit b39f0b87fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 286 additions and 0 deletions

View File

@ -203,6 +203,7 @@ type ComplexityRoot struct {
Meta func(childComplexity int) int Meta func(childComplexity int) int
Profile func(childComplexity int) int Profile func(childComplexity int) int
Session func(childComplexity int, params *model.SessionQueryInput) int Session func(childComplexity int, params *model.SessionQueryInput) int
User func(childComplexity int, params model.GetUserRequest) int
Users func(childComplexity int, params *model.PaginatedInput) int Users func(childComplexity int, params *model.PaginatedInput) int
ValidateJwtToken func(childComplexity int, params model.ValidateJWTTokenInput) int ValidateJwtToken func(childComplexity int, params model.ValidateJWTTokenInput) int
VerificationRequests func(childComplexity int, params *model.PaginatedInput) int VerificationRequests func(childComplexity int, params *model.PaginatedInput) int
@ -339,6 +340,7 @@ type QueryResolver interface {
Profile(ctx context.Context) (*model.User, error) Profile(ctx context.Context) (*model.User, error)
ValidateJwtToken(ctx context.Context, params model.ValidateJWTTokenInput) (*model.ValidateJWTTokenResponse, error) ValidateJwtToken(ctx context.Context, params model.ValidateJWTTokenInput) (*model.ValidateJWTTokenResponse, error)
Users(ctx context.Context, params *model.PaginatedInput) (*model.Users, error) Users(ctx context.Context, params *model.PaginatedInput) (*model.Users, error)
User(ctx context.Context, params model.GetUserRequest) (*model.User, error)
VerificationRequests(ctx context.Context, params *model.PaginatedInput) (*model.VerificationRequests, error) VerificationRequests(ctx context.Context, params *model.PaginatedInput) (*model.VerificationRequests, error)
AdminSession(ctx context.Context) (*model.Response, error) AdminSession(ctx context.Context) (*model.Response, error)
Env(ctx context.Context) (*model.Env, error) Env(ctx context.Context) (*model.Env, error)
@ -1435,6 +1437,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Query.Session(childComplexity, args["params"].(*model.SessionQueryInput)), true return e.complexity.Query.Session(childComplexity, args["params"].(*model.SessionQueryInput)), true
case "Query._user":
if e.complexity.Query.User == nil {
break
}
args, err := ec.field_Query__user_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Query.User(childComplexity, args["params"].(model.GetUserRequest)), true
case "Query._users": case "Query._users":
if e.complexity.Query.Users == nil { if e.complexity.Query.Users == nil {
break break
@ -1908,6 +1922,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
ec.unmarshalInputDeleteUserInput, ec.unmarshalInputDeleteUserInput,
ec.unmarshalInputForgotPasswordInput, ec.unmarshalInputForgotPasswordInput,
ec.unmarshalInputGenerateJWTKeysInput, ec.unmarshalInputGenerateJWTKeysInput,
ec.unmarshalInputGetUserRequest,
ec.unmarshalInputInviteMemberInput, ec.unmarshalInputInviteMemberInput,
ec.unmarshalInputListWebhookLogRequest, ec.unmarshalInputListWebhookLogRequest,
ec.unmarshalInputLoginInput, ec.unmarshalInputLoginInput,
@ -2510,6 +2525,10 @@ input ResendOTPRequest {
state: String state: String
} }
input GetUserRequest {
id: String!
}
type Mutation { type Mutation {
signup(params: SignUpInput!): AuthResponse! signup(params: SignUpInput!): AuthResponse!
mobile_signup(params: MobileSignUpInput): AuthResponse! mobile_signup(params: MobileSignUpInput): AuthResponse!
@ -2552,6 +2571,7 @@ type Query {
validate_jwt_token(params: ValidateJWTTokenInput!): ValidateJWTTokenResponse! validate_jwt_token(params: ValidateJWTTokenInput!): ValidateJWTTokenResponse!
# admin only apis # admin only apis
_users(params: PaginatedInput): Users! _users(params: PaginatedInput): Users!
_user(params: GetUserRequest!): User!
_verification_requests(params: PaginatedInput): VerificationRequests! _verification_requests(params: PaginatedInput): VerificationRequests!
_admin_session: Response! _admin_session: Response!
_env: Env! _env: Env!
@ -3033,6 +3053,21 @@ func (ec *executionContext) field_Query__email_templates_args(ctx context.Contex
return args, nil return args, nil
} }
func (ec *executionContext) field_Query__user_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 model.GetUserRequest
if tmp, ok := rawArgs["params"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params"))
arg0, err = ec.unmarshalNGetUserRequest2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐGetUserRequest(ctx, tmp)
if err != nil {
return nil, err
}
}
args["params"] = arg0
return args, nil
}
func (ec *executionContext) field_Query__users_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { func (ec *executionContext) field_Query__users_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error var err error
args := map[string]interface{}{} args := map[string]interface{}{}
@ -9491,6 +9526,101 @@ func (ec *executionContext) fieldContext_Query__users(ctx context.Context, field
return fc, nil return fc, nil
} }
func (ec *executionContext) _Query__user(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Query__user(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().User(rctx, fc.Args["params"].(model.GetUserRequest))
})
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.(*model.User)
fc.Result = res
return ec.marshalNUser2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐUser(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Query__user(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Query",
Field: field,
IsMethod: true,
IsResolver: true,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
switch field.Name {
case "id":
return ec.fieldContext_User_id(ctx, field)
case "email":
return ec.fieldContext_User_email(ctx, field)
case "email_verified":
return ec.fieldContext_User_email_verified(ctx, field)
case "signup_methods":
return ec.fieldContext_User_signup_methods(ctx, field)
case "given_name":
return ec.fieldContext_User_given_name(ctx, field)
case "family_name":
return ec.fieldContext_User_family_name(ctx, field)
case "middle_name":
return ec.fieldContext_User_middle_name(ctx, field)
case "nickname":
return ec.fieldContext_User_nickname(ctx, field)
case "preferred_username":
return ec.fieldContext_User_preferred_username(ctx, field)
case "gender":
return ec.fieldContext_User_gender(ctx, field)
case "birthdate":
return ec.fieldContext_User_birthdate(ctx, field)
case "phone_number":
return ec.fieldContext_User_phone_number(ctx, field)
case "phone_number_verified":
return ec.fieldContext_User_phone_number_verified(ctx, field)
case "picture":
return ec.fieldContext_User_picture(ctx, field)
case "roles":
return ec.fieldContext_User_roles(ctx, field)
case "created_at":
return ec.fieldContext_User_created_at(ctx, field)
case "updated_at":
return ec.fieldContext_User_updated_at(ctx, field)
case "revoked_timestamp":
return ec.fieldContext_User_revoked_timestamp(ctx, field)
case "is_multi_factor_auth_enabled":
return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
}
defer func() {
if r := recover(); r != nil {
err = ec.Recover(ctx, r)
ec.Error(ctx, err)
}
}()
ctx = graphql.WithFieldContext(ctx, fc)
if fc.Args, err = ec.field_Query__user_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
ec.Error(ctx, err)
return
}
return fc, nil
}
func (ec *executionContext) _Query__verification_requests(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { func (ec *executionContext) _Query__verification_requests(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Query__verification_requests(ctx, field) fc, err := ec.fieldContext_Query__verification_requests(ctx, field)
if err != nil { if err != nil {
@ -14641,6 +14771,34 @@ func (ec *executionContext) unmarshalInputGenerateJWTKeysInput(ctx context.Conte
return it, nil return it, nil
} }
func (ec *executionContext) unmarshalInputGetUserRequest(ctx context.Context, obj interface{}) (model.GetUserRequest, error) {
var it model.GetUserRequest
asMap := map[string]interface{}{}
for k, v := range obj.(map[string]interface{}) {
asMap[k] = v
}
fieldsInOrder := [...]string{"id"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
continue
}
switch k {
case "id":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id"))
it.ID, err = ec.unmarshalNString2string(ctx, v)
if err != nil {
return it, err
}
}
}
return it, nil
}
func (ec *executionContext) unmarshalInputInviteMemberInput(ctx context.Context, obj interface{}) (model.InviteMemberInput, error) { func (ec *executionContext) unmarshalInputInviteMemberInput(ctx context.Context, obj interface{}) (model.InviteMemberInput, error) {
var it model.InviteMemberInput var it model.InviteMemberInput
asMap := map[string]interface{}{} asMap := map[string]interface{}{}
@ -17536,6 +17694,29 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc)
} }
out.Concurrently(i, func() graphql.Marshaler {
return rrm(innerCtx)
})
case "_user":
field := field
innerFunc := func(ctx context.Context) (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._Query__user(ctx, field)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
}
rrm := func(ctx context.Context) graphql.Marshaler {
return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc)
}
out.Concurrently(i, func() graphql.Marshaler { out.Concurrently(i, func() graphql.Marshaler {
return rrm(innerCtx) return rrm(innerCtx)
}) })
@ -18711,6 +18892,11 @@ func (ec *executionContext) marshalNGenerateJWTKeysResponse2ᚖgithubᚗcomᚋau
return ec._GenerateJWTKeysResponse(ctx, sel, v) return ec._GenerateJWTKeysResponse(ctx, sel, v)
} }
func (ec *executionContext) unmarshalNGetUserRequest2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐGetUserRequest(ctx context.Context, v interface{}) (model.GetUserRequest, error) {
res, err := ec.unmarshalInputGetUserRequest(ctx, v)
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) unmarshalNID2string(ctx context.Context, v interface{}) (string, error) { func (ec *executionContext) unmarshalNID2string(ctx context.Context, v interface{}) (string, error) {
res, err := graphql.UnmarshalID(v) res, err := graphql.UnmarshalID(v)
return res, graphql.ErrorOnPath(ctx, err) return res, graphql.ErrorOnPath(ctx, err)

View File

@ -136,6 +136,10 @@ type GenerateJWTKeysResponse struct {
PrivateKey *string `json:"private_key"` PrivateKey *string `json:"private_key"`
} }
type GetUserRequest struct {
ID string `json:"id"`
}
type InviteMemberInput struct { type InviteMemberInput struct {
Emails []string `json:"emails"` Emails []string `json:"emails"`
RedirectURI *string `json:"redirect_uri"` RedirectURI *string `json:"redirect_uri"`

View File

@ -515,6 +515,10 @@ input ResendOTPRequest {
state: String state: String
} }
input GetUserRequest {
id: String!
}
type Mutation { type Mutation {
signup(params: SignUpInput!): AuthResponse! signup(params: SignUpInput!): AuthResponse!
mobile_signup(params: MobileSignUpInput): AuthResponse! mobile_signup(params: MobileSignUpInput): AuthResponse!
@ -557,6 +561,7 @@ type Query {
validate_jwt_token(params: ValidateJWTTokenInput!): ValidateJWTTokenResponse! validate_jwt_token(params: ValidateJWTTokenInput!): ValidateJWTTokenResponse!
# admin only apis # admin only apis
_users(params: PaginatedInput): Users! _users(params: PaginatedInput): Users!
_user(params: GetUserRequest!): User!
_verification_requests(params: PaginatedInput): VerificationRequests! _verification_requests(params: PaginatedInput): VerificationRequests!
_admin_session: Response! _admin_session: Response!
_env: Env! _env: Env!

View File

@ -191,6 +191,11 @@ func (r *queryResolver) Users(ctx context.Context, params *model.PaginatedInput)
return resolvers.UsersResolver(ctx, params) return resolvers.UsersResolver(ctx, params)
} }
// User is the resolver for the _user field.
func (r *queryResolver) User(ctx context.Context, params model.GetUserRequest) (*model.User, error) {
return resolvers.UserResolver(ctx, params)
}
// VerificationRequests is the resolver for the _verification_requests field. // VerificationRequests is the resolver for the _verification_requests field.
func (r *queryResolver) VerificationRequests(ctx context.Context, params *model.PaginatedInput) (*model.VerificationRequests, error) { func (r *queryResolver) VerificationRequests(ctx context.Context, params *model.PaginatedInput) (*model.VerificationRequests, error) {
return resolvers.VerificationRequestsResolver(ctx, params) return resolvers.VerificationRequestsResolver(ctx, params)

36
server/resolvers/user.go Normal file
View File

@ -0,0 +1,36 @@
package resolvers
import (
"context"
"fmt"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils"
)
// UserResolver is a resolver for user query
// This is admin only query
func UserResolver(ctx context.Context, params model.GetUserRequest) (*model.User, error) {
gc, err := utils.GinContextFromContext(ctx)
if err != nil {
log.Debug("Failed to get GinContext: ", err)
return nil, err
}
if !token.IsSuperAdmin(gc) {
log.Debug("Not logged in as super admin.")
return nil, fmt.Errorf("unauthorized")
}
res, err := db.Provider.GetUserByID(ctx, params.ID)
if err != nil {
log.Debug("Failed to get users: ", err)
return nil, err
}
return res.AsAPIUser(), nil
}

View File

@ -93,6 +93,7 @@ func TestResolvers(t *testing.T) {
webhookTest(t, s) webhookTest(t, s)
webhooksTest(t, s) webhooksTest(t, s)
usersTest(t, s) usersTest(t, s)
userTest(t, s)
deleteUserTest(t, s) deleteUserTest(t, s)
updateUserTest(t, s) updateUserTest(t, s)
adminLoginTests(t, s) adminLoginTests(t, s)

49
server/test/user_test.go Normal file
View File

@ -0,0 +1,49 @@
package test
import (
"fmt"
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/crypto"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func userTest(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should get users list with admin secret only`, func(t *testing.T) {
req, ctx := createContext(s)
email := "user." + s.TestInfo.Email
res, err := resolvers.SignupResolver(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
assert.NoError(t, err)
assert.NotEmpty(t, res.User)
userRes, err := resolvers.UserResolver(ctx, model.GetUserRequest{
ID: res.User.ID,
})
assert.Nil(t, userRes)
assert.NotNil(t, err, "unauthorized")
adminSecret, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyAdminSecret)
assert.Nil(t, err)
h, err := crypto.EncryptPassword(adminSecret)
assert.Nil(t, err)
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", constants.AdminCookieName, h))
userRes, err = resolvers.UserResolver(ctx, model.GetUserRequest{
ID: res.User.ID,
})
assert.Nil(t, err)
assert.Equal(t, res.User.ID, userRes.ID)
assert.Equal(t, email, userRes.Email)
cleanData(email)
})
}