feat: add api for admin login

This commit is contained in:
Lakhan Samani 2021-12-30 10:01:51 +05:30
parent 86bcb8ca87
commit d9c40057e6
16 changed files with 517 additions and 75 deletions

View File

@ -1,19 +1,12 @@
import * as React from 'react'; import * as React from 'react';
import { Text, ChakraProvider } from '@chakra-ui/react'; import { Text, ChakraProvider } from '@chakra-ui/react';
import { MdStar } from 'react-icons/md'; import { MdStar } from 'react-icons/md';
import { BrowserRouter } from 'react-router-dom';
export default function Example() { export default function Example() {
return ( return (
<ChakraProvider> <ChakraProvider>
<Text <BrowserRouter></BrowserRouter>
ml={2}
textTransform="uppercase"
fontSize="xl"
fontWeight="bold"
color="pink.800"
>
Authorizer Dashboard
</Text>
</ChakraProvider> </ChakraProvider>
); );
} }

0
dashboard/src/Router.tsx Normal file
View File

View File

@ -0,0 +1,5 @@
import React from 'react';
export default function AuthLayout() {
return <h1>Auth Layout</h1>;
}

View File

@ -0,0 +1,5 @@
import React from 'react';
export default function DefaultLayout() {
return <h1>Default Layout</h1>;
}

View File

View File

@ -0,0 +1,27 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func aminLoginTests(s TestSetup, t *testing.T) {
t.Run(`should complete admin login`, func(t *testing.T) {
_, ctx := createContext(s)
_, err := resolvers.AdminLoginResolver(ctx, model.AdminLoginInput{
AdminSecret: "admin_test",
})
assert.NotNil(t, err)
res, err := resolvers.AdminLoginResolver(ctx, model.AdminLoginInput{
AdminSecret: "admin",
})
assert.Nil(t, err)
assert.Greater(t, len(res.AccessToken), 0)
})
}

View File

@ -10,9 +10,9 @@ import (
func TestResolvers(t *testing.T) { func TestResolvers(t *testing.T) {
databases := map[string]string{ databases := map[string]string{
enum.Sqlite.String(): "../../data.db", enum.Sqlite.String(): "../../data.db",
enum.Arangodb.String(): "http://root:root@localhost:8529", // enum.Arangodb.String(): "http://root:root@localhost:8529",
enum.Mongodb.String(): "mongodb://localhost:27017", // enum.Mongodb.String(): "mongodb://localhost:27017",
} }
for dbType, dbURL := range databases { for dbType, dbURL := range databases {
@ -42,6 +42,7 @@ func TestResolvers(t *testing.T) {
usersTest(s, t) usersTest(s, t)
deleteUserTest(s, t) deleteUserTest(s, t)
updateUserTest(s, t) updateUserTest(s, t)
aminLoginTests(s, t)
}) })
} }
} }

View File

@ -20,7 +20,6 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f
github.com/stretchr/testify v1.7.0
github.com/ugorji/go v1.2.6 // indirect github.com/ugorji/go v1.2.6 // indirect
github.com/vektah/gqlparser/v2 v2.2.0 github.com/vektah/gqlparser/v2 v2.2.0
go.mongodb.org/mongo-driver v1.8.1 go.mongodb.org/mongo-driver v1.8.1

View File

@ -43,6 +43,12 @@ type DirectiveRoot struct {
} }
type ComplexityRoot struct { type ComplexityRoot struct {
AdminLoginResponse struct {
AccessToken func(childComplexity int) int
ExpiresAt func(childComplexity int) int
Message func(childComplexity int) int
}
AuthResponse struct { AuthResponse struct {
AccessToken func(childComplexity int) int AccessToken func(childComplexity int) int
ExpiresAt func(childComplexity int) int ExpiresAt func(childComplexity int) int
@ -66,6 +72,7 @@ type ComplexityRoot struct {
} }
Mutation struct { Mutation struct {
AdminLogin func(childComplexity int, params model.AdminLoginInput) int
DeleteUser func(childComplexity int, params model.DeleteUserInput) int DeleteUser func(childComplexity int, params model.DeleteUserInput) int
ForgotPassword func(childComplexity int, params model.ForgotPasswordInput) int ForgotPassword func(childComplexity int, params model.ForgotPasswordInput) int
Login func(childComplexity int, params model.LoginInput) int Login func(childComplexity int, params model.LoginInput) int
@ -80,6 +87,7 @@ type ComplexityRoot struct {
} }
Query struct { Query struct {
AdminSession func(childComplexity int) int
Meta func(childComplexity int) int Meta func(childComplexity int) int
Profile func(childComplexity int) int Profile func(childComplexity int) int
Session func(childComplexity int, roles []string) int Session func(childComplexity int, roles []string) int
@ -134,6 +142,7 @@ type MutationResolver interface {
ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error) ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error)
DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error) DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error)
UpdateUser(ctx context.Context, params model.UpdateUserInput) (*model.User, error) UpdateUser(ctx context.Context, params model.UpdateUserInput) (*model.User, error)
AdminLogin(ctx context.Context, params model.AdminLoginInput) (*model.AdminLoginResponse, error)
} }
type QueryResolver interface { type QueryResolver interface {
Meta(ctx context.Context) (*model.Meta, error) Meta(ctx context.Context) (*model.Meta, error)
@ -141,6 +150,7 @@ type QueryResolver interface {
Profile(ctx context.Context) (*model.User, error) Profile(ctx context.Context) (*model.User, error)
Users(ctx context.Context) ([]*model.User, error) Users(ctx context.Context) ([]*model.User, error)
VerificationRequests(ctx context.Context) ([]*model.VerificationRequest, error) VerificationRequests(ctx context.Context) ([]*model.VerificationRequest, error)
AdminSession(ctx context.Context) (*model.AdminLoginResponse, error)
} }
type executableSchema struct { type executableSchema struct {
@ -158,6 +168,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
_ = ec _ = ec
switch typeName + "." + field { switch typeName + "." + field {
case "AdminLoginResponse.access_token":
if e.complexity.AdminLoginResponse.AccessToken == nil {
break
}
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
}
return e.complexity.AdminLoginResponse.Message(childComplexity), true
case "AuthResponse.access_token": case "AuthResponse.access_token":
if e.complexity.AuthResponse.AccessToken == nil { if e.complexity.AuthResponse.AccessToken == nil {
break break
@ -249,6 +280,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Meta.Version(childComplexity), true return e.complexity.Meta.Version(childComplexity), true
case "Mutation._admin_login":
if e.complexity.Mutation.AdminLogin == nil {
break
}
args, err := ec.field_Mutation__admin_login_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Mutation.AdminLogin(childComplexity, args["params"].(model.AdminLoginInput)), true
case "Mutation._delete_user": case "Mutation._delete_user":
if e.complexity.Mutation.DeleteUser == nil { if e.complexity.Mutation.DeleteUser == nil {
break break
@ -376,6 +419,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Mutation.VerifyEmail(childComplexity, args["params"].(model.VerifyEmailInput)), true return e.complexity.Mutation.VerifyEmail(childComplexity, args["params"].(model.VerifyEmailInput)), true
case "Query._admin_session":
if e.complexity.Query.AdminSession == nil {
break
}
return e.complexity.Query.AdminSession(childComplexity), true
case "Query.meta": case "Query.meta":
if e.complexity.Query.Meta == nil { if e.complexity.Query.Meta == nil {
break break
@ -719,6 +769,16 @@ type Response {
message: String! message: String!
} }
type AdminLoginResponse {
message: String!
access_token: String!
expires_at: Int64!
}
input AdminLoginInput {
admin_secret: String!
}
input SignUpInput { input SignUpInput {
email: String! email: String!
given_name: String given_name: String
@ -810,6 +870,7 @@ type Mutation {
# admin only apis # admin only apis
_delete_user(params: DeleteUserInput!): Response! _delete_user(params: DeleteUserInput!): Response!
_update_user(params: UpdateUserInput!): User! _update_user(params: UpdateUserInput!): User!
_admin_login(params: AdminLoginInput!): AdminLoginResponse
} }
type Query { type Query {
@ -819,6 +880,7 @@ type Query {
# admin only apis # admin only apis
_users: [User!]! _users: [User!]!
_verification_requests: [VerificationRequest!]! _verification_requests: [VerificationRequest!]!
_admin_session: AdminLoginResponse
} }
`, BuiltIn: false}, `, BuiltIn: false},
} }
@ -828,6 +890,21 @@ var parsedSchema = gqlparser.MustLoadSchema(sources...)
// region ***************************** args.gotpl ***************************** // region ***************************** args.gotpl *****************************
func (ec *executionContext) field_Mutation__admin_login_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 model.AdminLoginInput
if tmp, ok := rawArgs["params"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params"))
arg0, err = ec.unmarshalNAdminLoginInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAdminLoginInput(ctx, tmp)
if err != nil {
return nil, err
}
}
args["params"] = arg0
return args, nil
}
func (ec *executionContext) field_Mutation__delete_user_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { func (ec *executionContext) field_Mutation__delete_user_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{}{}
@ -1046,6 +1123,111 @@ func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArg
// region **************************** field.gotpl ***************************** // region **************************** field.gotpl *****************************
func (ec *executionContext) _AdminLoginResponse_message(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.Message, 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.(string)
fc.Result = res
return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _AdminLoginResponse_access_token(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.AccessToken, 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.(string)
fc.Result = res
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) { func (ec *executionContext) _AuthResponse_message(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -1947,6 +2129,45 @@ func (ec *executionContext) _Mutation__update_user(ctx context.Context, field gr
return ec.marshalNUser2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐUser(ctx, field.Selections, res) return ec.marshalNUser2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐUser(ctx, field.Selections, res)
} }
func (ec *executionContext) _Mutation__admin_login(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Mutation",
Field: field,
Args: nil,
IsMethod: true,
IsResolver: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
rawArgs := field.ArgumentMap(ec.Variables)
args, err := ec.field_Mutation__admin_login_args(ctx, rawArgs)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
fc.Args = args
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Mutation().AdminLogin(rctx, args["params"].(model.AdminLoginInput))
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*model.AdminLoginResponse)
fc.Result = res
return ec.marshalOAdminLoginResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAdminLoginResponse(ctx, field.Selections, res)
}
func (ec *executionContext) _Query_meta(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { func (ec *executionContext) _Query_meta(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -2126,6 +2347,38 @@ func (ec *executionContext) _Query__verification_requests(ctx context.Context, f
return ec.marshalNVerificationRequest2ᚕᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐVerificationRequestᚄ(ctx, field.Selections, res) return ec.marshalNVerificationRequest2ᚕᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐVerificationRequestᚄ(ctx, field.Selections, res)
} }
func (ec *executionContext) _Query__admin_session(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Query",
Field: field,
Args: nil,
IsMethod: true,
IsResolver: true,
}
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 ec.resolvers.Query().AdminSession(rctx)
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*model.AdminLoginResponse)
fc.Result = res
return ec.marshalOAdminLoginResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAdminLoginResponse(ctx, field.Selections, res)
}
func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -4140,6 +4393,29 @@ func (ec *executionContext) ___Type_ofType(ctx context.Context, field graphql.Co
// region **************************** input.gotpl ***************************** // region **************************** input.gotpl *****************************
func (ec *executionContext) unmarshalInputAdminLoginInput(ctx context.Context, obj interface{}) (model.AdminLoginInput, error) {
var it model.AdminLoginInput
asMap := map[string]interface{}{}
for k, v := range obj.(map[string]interface{}) {
asMap[k] = v
}
for k, v := range asMap {
switch k {
case "admin_secret":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("admin_secret"))
it.AdminSecret, err = ec.unmarshalNString2string(ctx, v)
if err != nil {
return it, err
}
}
}
return it, nil
}
func (ec *executionContext) unmarshalInputDeleteUserInput(ctx context.Context, obj interface{}) (model.DeleteUserInput, error) { func (ec *executionContext) unmarshalInputDeleteUserInput(ctx context.Context, obj interface{}) (model.DeleteUserInput, error) {
var it model.DeleteUserInput var it model.DeleteUserInput
asMap := map[string]interface{}{} asMap := map[string]interface{}{}
@ -4682,6 +4958,43 @@ func (ec *executionContext) unmarshalInputVerifyEmailInput(ctx context.Context,
// region **************************** object.gotpl **************************** // region **************************** object.gotpl ****************************
var adminLoginResponseImplementors = []string{"AdminLoginResponse"}
func (ec *executionContext) _AdminLoginResponse(ctx context.Context, sel ast.SelectionSet, obj *model.AdminLoginResponse) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, adminLoginResponseImplementors)
out := graphql.NewFieldSet(fields)
var invalids uint32
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("AdminLoginResponse")
case "message":
out.Values[i] = ec._AdminLoginResponse_message(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "access_token":
out.Values[i] = ec._AdminLoginResponse_access_token(ctx, field, obj)
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))
}
}
out.Dispatch()
if invalids > 0 {
return graphql.Null
}
return out
}
var authResponseImplementors = []string{"AuthResponse"} var authResponseImplementors = []string{"AuthResponse"}
func (ec *executionContext) _AuthResponse(ctx context.Context, sel ast.SelectionSet, obj *model.AuthResponse) graphql.Marshaler { func (ec *executionContext) _AuthResponse(ctx context.Context, sel ast.SelectionSet, obj *model.AuthResponse) graphql.Marshaler {
@ -4874,6 +5187,8 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
case "_admin_login":
out.Values[i] = ec._Mutation__admin_login(ctx, field)
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
} }
@ -4967,6 +5282,17 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
} }
return res return res
}) })
case "_admin_session":
field := field
out.Concurrently(i, func() (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._Query__admin_session(ctx, field)
return res
})
case "__type": case "__type":
out.Values[i] = ec._Query___type(ctx, field) out.Values[i] = ec._Query___type(ctx, field)
case "__schema": case "__schema":
@ -5369,6 +5695,11 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o
// region ***************************** type.gotpl ***************************** // region ***************************** type.gotpl *****************************
func (ec *executionContext) unmarshalNAdminLoginInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAdminLoginInput(ctx context.Context, v interface{}) (model.AdminLoginInput, error) {
res, err := ec.unmarshalInputAdminLoginInput(ctx, v)
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalNAuthResponse2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAuthResponse(ctx context.Context, sel ast.SelectionSet, v model.AuthResponse) graphql.Marshaler { func (ec *executionContext) marshalNAuthResponse2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAuthResponse(ctx context.Context, sel ast.SelectionSet, v model.AuthResponse) graphql.Marshaler {
return ec._AuthResponse(ctx, sel, &v) return ec._AuthResponse(ctx, sel, &v)
} }
@ -5423,6 +5754,21 @@ func (ec *executionContext) marshalNID2string(ctx context.Context, sel ast.Selec
return res 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) { 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) res, err := ec.unmarshalInputLoginInput(ctx, v)
return res, graphql.ErrorOnPath(ctx, err) return res, graphql.ErrorOnPath(ctx, err)
@ -5911,6 +6257,13 @@ func (ec *executionContext) marshalN__TypeKind2string(ctx context.Context, sel a
return res return res
} }
func (ec *executionContext) marshalOAdminLoginResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAdminLoginResponse(ctx context.Context, sel ast.SelectionSet, v *model.AdminLoginResponse) graphql.Marshaler {
if v == nil {
return graphql.Null
}
return ec._AdminLoginResponse(ctx, sel, v)
}
func (ec *executionContext) marshalOAuthResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAuthResponse(ctx context.Context, sel ast.SelectionSet, v *model.AuthResponse) graphql.Marshaler { func (ec *executionContext) marshalOAuthResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAuthResponse(ctx context.Context, sel ast.SelectionSet, v *model.AuthResponse) graphql.Marshaler {
if v == nil { if v == nil {
return graphql.Null return graphql.Null

View File

@ -2,6 +2,16 @@
package model package model
type AdminLoginInput struct {
AdminSecret string `json:"admin_secret"`
}
type AdminLoginResponse struct {
Message string `json:"message"`
AccessToken string `json:"access_token"`
ExpiresAt int64 `json:"expires_at"`
}
type AuthResponse struct { type AuthResponse struct {
Message string `json:"message"` Message string `json:"message"`
AccessToken *string `json:"access_token"` AccessToken *string `json:"access_token"`

View File

@ -62,6 +62,16 @@ type Response {
message: String! message: String!
} }
type AdminLoginResponse {
message: String!
access_token: String!
expires_at: Int64!
}
input AdminLoginInput {
admin_secret: String!
}
input SignUpInput { input SignUpInput {
email: String! email: String!
given_name: String given_name: String
@ -153,6 +163,7 @@ type Mutation {
# admin only apis # admin only apis
_delete_user(params: DeleteUserInput!): Response! _delete_user(params: DeleteUserInput!): Response!
_update_user(params: UpdateUserInput!): User! _update_user(params: UpdateUserInput!): User!
_admin_login(params: AdminLoginInput!): AdminLoginResponse
} }
type Query { type Query {
@ -162,4 +173,5 @@ type Query {
# admin only apis # admin only apis
_users: [User!]! _users: [User!]!
_verification_requests: [VerificationRequest!]! _verification_requests: [VerificationRequest!]!
_admin_session: AdminLoginResponse
} }

View File

@ -5,6 +5,7 @@ package graph
import ( import (
"context" "context"
"fmt"
"github.com/authorizerdev/authorizer/server/graph/generated" "github.com/authorizerdev/authorizer/server/graph/generated"
"github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/graph/model"
@ -55,6 +56,10 @@ func (r *mutationResolver) UpdateUser(ctx context.Context, params model.UpdateUs
return resolvers.UpdateUser(ctx, params) return resolvers.UpdateUser(ctx, params)
} }
func (r *mutationResolver) AdminLogin(ctx context.Context, params model.AdminLoginInput) (*model.AdminLoginResponse, error) {
return resolvers.AdminLoginResolver(ctx, params)
}
func (r *queryResolver) Meta(ctx context.Context) (*model.Meta, error) { func (r *queryResolver) Meta(ctx context.Context) (*model.Meta, error) {
return resolvers.Meta(ctx) return resolvers.Meta(ctx)
} }
@ -75,6 +80,10 @@ func (r *queryResolver) VerificationRequests(ctx context.Context) ([]*model.Veri
return resolvers.VerificationRequests(ctx) return resolvers.VerificationRequests(ctx)
} }
func (r *queryResolver) AdminSession(ctx context.Context) (*model.AdminLoginResponse, error) {
panic(fmt.Errorf("not implemented"))
}
// Mutation returns generated.MutationResolver implementation. // Mutation returns generated.MutationResolver implementation.
func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} } func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} }

View File

@ -1,79 +1,22 @@
package handlers package handlers
import ( import (
"encoding/base64"
"encoding/json"
"log"
"net/http" "net/http"
"strings"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
func DashboardHandler() gin.HandlerFunc { func DashboardHandler() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
state := c.Query("state") isOnboardingCompleted := false
if constants.ADMIN_SECRET != "" && constants.DATABASE_TYPE != "" && constants.DATABASE_URL != "" {
var stateObj State isOnboardingCompleted = true
if state == "" {
// cookie, err := utils.GetAuthToken(c)
// if err != nil {
// c.JSON(400, gin.H{"error": "invalid state"})
// return
// }
stateObj.AuthorizerURL = constants.AUTHORIZER_URL
stateObj.RedirectURL = constants.AUTHORIZER_URL + "/app"
} else {
decodedState, err := base64.StdEncoding.DecodeString(state)
if err != nil {
c.JSON(400, gin.H{"error": "[unable to decode state] invalid state"})
return
}
err = json.Unmarshal(decodedState, &stateObj)
if err != nil {
c.JSON(400, gin.H{"error": "[unable to parse state] invalid state"})
return
}
stateObj.AuthorizerURL = strings.TrimSuffix(stateObj.AuthorizerURL, "/")
stateObj.RedirectURL = strings.TrimSuffix(stateObj.RedirectURL, "/")
// validate redirect url with allowed origins
if !utils.IsValidOrigin(stateObj.RedirectURL) {
c.JSON(400, gin.H{"error": "invalid redirect url"})
return
}
if stateObj.AuthorizerURL == "" {
c.JSON(400, gin.H{"error": "invalid authorizer url"})
return
}
// validate host and domain of authorizer url
if strings.TrimSuffix(stateObj.AuthorizerURL, "/") != constants.AUTHORIZER_URL {
c.JSON(400, gin.H{"error": "invalid host url"})
return
}
} }
// debug the request state
if pusher := c.Writer.Pusher(); pusher != nil {
// use pusher.Push() to do server push
if err := pusher.Push("/app/build/bundle.js", nil); err != nil {
log.Printf("Failed to push: %v", err)
}
}
c.HTML(http.StatusOK, "dashboard.tmpl", gin.H{ c.HTML(http.StatusOK, "dashboard.tmpl", gin.H{
"data": map[string]string{ "data": map[string]interface{}{
"authorizerURL": stateObj.AuthorizerURL, "isOnboardingCompleted": isOnboardingCompleted,
"redirectURL": stateObj.RedirectURL,
"organizationName": constants.ORGANIZATION_NAME,
"organizationLogo": constants.ORGANIZATION_LOGO,
}, },
}) })
} }

View File

@ -0,0 +1,41 @@
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.ADMIN_SECRET {
return nil, 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)
res = &model.AdminLoginResponse{
AccessToken: accessToken,
ExpiresAt: expiresAt,
Message: "admin logged in successfully",
}
return res, nil
}

View File

@ -124,3 +124,31 @@ func VerifyAuthToken(token string) (map[string]interface{}, error) {
return res, nil return res, nil
} }
func CreateAdminAuthToken(tokenType enum.TokenType, c *gin.Context) (string, int64, error) {
t := jwt.New(jwt.GetSigningMethod(constants.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.JWT_SECRET))
if err != nil {
return "", 0, err
}
return token, expiresAt, nil
}

View File

@ -47,3 +47,19 @@ func DeleteCookie(gc *gin.Context) {
gc.SetCookie(constants.COOKIE_NAME, "", -1, "/", host, secure, httpOnly) gc.SetCookie(constants.COOKIE_NAME, "", -1, "/", host, secure, httpOnly)
gc.SetCookie(constants.COOKIE_NAME+"-client", "", -1, "/", domain, secure, httpOnly) gc.SetCookie(constants.COOKIE_NAME+"-client", "", -1, "/", domain, secure, httpOnly)
} }
func SetAdminCookie(gc *gin.Context, token string) {
secure := true
httpOnly := true
host, _ := GetHostParts(constants.AUTHORIZER_URL)
gc.SetCookie("authorizer-admin", token, 3600, "/", host, secure, httpOnly)
}
func DeleteAdminCookie(gc *gin.Context, token string) {
secure := true
httpOnly := true
host, _ := GetHostParts(constants.AUTHORIZER_URL)
gc.SetCookie("authorizer-admin", "", -1, "/", host, secure, httpOnly)
}