feat: update user access (#151)

* feat: update user access

* revoked timestamp field updated

* updates

* updates

* updates
This commit is contained in:
Anik Ghosh 2022-03-24 14:13:55 +05:30 committed by GitHub
parent 1f3dec6ea6
commit b2541c8e9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 477 additions and 7 deletions

View File

@ -53,3 +53,19 @@ export const InviteMembers = `
} }
} }
`; `;
export const RevokeAccess = `
mutation revokeAccess($param: UpdateAccessInput!) {
_revoke_access(param: $param) {
message
}
}
`;
export const EnableAccess = `
mutation revokeAccess($param: UpdateAccessInput!) {
_enable_access(param: $param) {
message
}
}
`;

View File

@ -81,6 +81,7 @@ export const UserDetailsQuery = `
signup_methods signup_methods
roles roles
created_at created_at
revoked_timestamp
} }
} }
} }

View File

@ -39,7 +39,7 @@ import {
FaAngleDown, FaAngleDown,
} from 'react-icons/fa'; } from 'react-icons/fa';
import { EmailVerificationQuery, UserDetailsQuery } from '../graphql/queries'; import { EmailVerificationQuery, UserDetailsQuery } from '../graphql/queries';
import { UpdateUser } from '../graphql/mutation'; import { EnableAccess, RevokeAccess, UpdateUser } from '../graphql/mutation';
import EditUserModal from '../components/EditUserModal'; import EditUserModal from '../components/EditUserModal';
import DeleteUserModal from '../components/DeleteUserModal'; import DeleteUserModal from '../components/DeleteUserModal';
import InviteMembersModal from '../components/InviteMembersModal'; import InviteMembersModal from '../components/InviteMembersModal';
@ -67,6 +67,12 @@ interface userDataTypes {
signup_methods: string; signup_methods: string;
roles: [string]; roles: [string];
created_at: number; created_at: number;
revoked_timestamp: number;
}
const enum updateAccessActions {
REVOKE = 'REVOKE',
ENABLE = 'ENABLE',
} }
const getMaxPages = (pagination: paginationPropTypes) => { const getMaxPages = (pagination: paginationPropTypes) => {
@ -185,6 +191,66 @@ export default function Users() {
updateUserList(); updateUserList();
}; };
const updateAccessHandler = async (
id: string,
action: updateAccessActions
) => {
switch (action) {
case updateAccessActions.ENABLE:
const enableAccessRes = await client
.mutation(EnableAccess, {
param: {
user_id: id,
},
})
.toPromise();
if (enableAccessRes.error) {
toast({
title: 'User access enable failed',
isClosable: true,
status: 'error',
position: 'bottom-right',
});
} else {
toast({
title: 'User access enabled successfully',
isClosable: true,
status: 'success',
position: 'bottom-right',
});
}
updateUserList();
break;
case updateAccessActions.REVOKE:
const revokeAccessRes = await client
.mutation(RevokeAccess, {
param: {
user_id: id,
},
})
.toPromise();
if (revokeAccessRes.error) {
toast({
title: 'User access revoke failed',
isClosable: true,
status: 'error',
position: 'bottom-right',
});
} else {
toast({
title: 'User access revoked successfully',
isClosable: true,
status: 'success',
position: 'bottom-right',
});
}
updateUserList();
break;
default:
break;
}
};
return ( return (
<Box m="5" py="5" px="10" bg="white" rounded="md"> <Box m="5" py="5" px="10" bg="white" rounded="md">
<Flex margin="2% 0" justifyContent="space-between" alignItems="center"> <Flex margin="2% 0" justifyContent="space-between" alignItems="center">
@ -206,6 +272,7 @@ export default function Users() {
<Th>Signup Methods</Th> <Th>Signup Methods</Th>
<Th>Roles</Th> <Th>Roles</Th>
<Th>Verified</Th> <Th>Verified</Th>
<Th>Access</Th>
<Th>Actions</Th> <Th>Actions</Th>
</Tr> </Tr>
</Thead> </Thead>
@ -214,7 +281,7 @@ export default function Users() {
const { email_verified, created_at, ...rest }: any = user; const { email_verified, created_at, ...rest }: any = user;
return ( return (
<Tr key={user.id} style={{ fontSize: 14 }}> <Tr key={user.id} style={{ fontSize: 14 }}>
<Td>{user.email}</Td> <Td maxW="300">{user.email}</Td>
<Td> <Td>
{dayjs(user.created_at * 1000).format('MMM DD, YYYY')} {dayjs(user.created_at * 1000).format('MMM DD, YYYY')}
</Td> </Td>
@ -229,6 +296,15 @@ export default function Users() {
{user.email_verified.toString()} {user.email_verified.toString()}
</Tag> </Tag>
</Td> </Td>
<Td>
<Tag
size="sm"
variant="outline"
colorScheme={user.revoked_timestamp ? 'red' : 'green'}
>
{user.revoked_timestamp ? 'Revoked' : 'Enabled'}
</Tag>
</Td>
<Td> <Td>
<Menu> <Menu>
<MenuButton as={Button} variant="unstyled" size="sm"> <MenuButton as={Button} variant="unstyled" size="sm">
@ -258,6 +334,29 @@ export default function Users() {
user={rest} user={rest}
updateUserList={updateUserList} updateUserList={updateUserList}
/> />
{user.revoked_timestamp ? (
<MenuItem
onClick={() =>
updateAccessHandler(
user.id,
updateAccessActions.ENABLE
)
}
>
Enable Access
</MenuItem>
) : (
<MenuItem
onClick={() =>
updateAccessHandler(
user.id,
updateAccessActions.REVOKE
)
}
>
Revoke Access
</MenuItem>
)}
</MenuList> </MenuList>
</Menu> </Menu>
</Td> </Td>

View File

@ -27,6 +27,7 @@ type User struct {
Roles string `json:"roles" bson:"roles"` Roles string `json:"roles" bson:"roles"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at"` UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
CreatedAt int64 `json:"created_at" bson:"created_at"` CreatedAt int64 `json:"created_at" bson:"created_at"`
RevokedTimestamp *int64 `json:"revoked_timestamp" bson:"revoked_timestamp"`
} }
func (user *User) AsAPIUser() *model.User { func (user *User) AsAPIUser() *model.User {
@ -35,6 +36,7 @@ func (user *User) AsAPIUser() *model.User {
email := user.Email email := user.Email
createdAt := user.CreatedAt createdAt := user.CreatedAt
updatedAt := user.UpdatedAt updatedAt := user.UpdatedAt
revokedTimestamp := user.RevokedTimestamp
return &model.User{ return &model.User{
ID: user.ID, ID: user.ID,
Email: user.Email, Email: user.Email,
@ -53,5 +55,6 @@ func (user *User) AsAPIUser() *model.User {
Roles: strings.Split(user.Roles, ","), Roles: strings.Split(user.Roles, ","),
CreatedAt: &createdAt, CreatedAt: &createdAt,
UpdatedAt: &updatedAt, UpdatedAt: &updatedAt,
RevokedTimestamp: revokedTimestamp,
} }
} }

View File

@ -115,6 +115,7 @@ type ComplexityRoot struct {
AdminLogout func(childComplexity int) int AdminLogout func(childComplexity int) int
AdminSignup func(childComplexity int, params model.AdminSignupInput) int AdminSignup func(childComplexity int, params model.AdminSignupInput) int
DeleteUser func(childComplexity int, params model.DeleteUserInput) int DeleteUser func(childComplexity int, params model.DeleteUserInput) int
EnableAccess func(childComplexity int, param model.UpdateAccessInput) int
ForgotPassword func(childComplexity int, params model.ForgotPasswordInput) int ForgotPassword func(childComplexity int, params model.ForgotPasswordInput) int
InviteMembers func(childComplexity int, params model.InviteMemberInput) int InviteMembers func(childComplexity int, params model.InviteMemberInput) int
Login func(childComplexity int, params model.LoginInput) int Login func(childComplexity int, params model.LoginInput) int
@ -123,6 +124,7 @@ type ComplexityRoot struct {
ResendVerifyEmail func(childComplexity int, params model.ResendVerifyEmailInput) int ResendVerifyEmail func(childComplexity int, params model.ResendVerifyEmailInput) int
ResetPassword func(childComplexity int, params model.ResetPasswordInput) int ResetPassword func(childComplexity int, params model.ResetPasswordInput) int
Revoke func(childComplexity int, params model.OAuthRevokeInput) int Revoke func(childComplexity int, params model.OAuthRevokeInput) int
RevokeAccess func(childComplexity int, param model.UpdateAccessInput) int
Signup func(childComplexity int, params model.SignUpInput) int Signup func(childComplexity int, params model.SignUpInput) int
UpdateEnv func(childComplexity int, params model.UpdateEnvInput) int UpdateEnv func(childComplexity int, params model.UpdateEnvInput) int
UpdateProfile func(childComplexity int, params model.UpdateProfileInput) int UpdateProfile func(childComplexity int, params model.UpdateProfileInput) int
@ -167,6 +169,7 @@ type ComplexityRoot struct {
PhoneNumberVerified func(childComplexity int) int PhoneNumberVerified func(childComplexity int) int
Picture func(childComplexity int) int Picture func(childComplexity int) int
PreferredUsername func(childComplexity int) int PreferredUsername func(childComplexity int) int
RevokedTimestamp func(childComplexity int) int
Roles func(childComplexity int) int Roles func(childComplexity int) int
SignupMethods func(childComplexity int) int SignupMethods func(childComplexity int) int
UpdatedAt func(childComplexity int) int UpdatedAt func(childComplexity int) int
@ -217,6 +220,8 @@ type MutationResolver interface {
AdminLogout(ctx context.Context) (*model.Response, error) AdminLogout(ctx context.Context) (*model.Response, error)
UpdateEnv(ctx context.Context, params model.UpdateEnvInput) (*model.Response, error) UpdateEnv(ctx context.Context, params model.UpdateEnvInput) (*model.Response, error)
InviteMembers(ctx context.Context, params model.InviteMemberInput) (*model.Response, error) InviteMembers(ctx context.Context, params model.InviteMemberInput) (*model.Response, error)
RevokeAccess(ctx context.Context, param model.UpdateAccessInput) (*model.Response, error)
EnableAccess(ctx context.Context, param model.UpdateAccessInput) (*model.Response, error)
} }
type QueryResolver interface { type QueryResolver interface {
Meta(ctx context.Context) (*model.Meta, error) Meta(ctx context.Context) (*model.Meta, error)
@ -672,6 +677,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Mutation.DeleteUser(childComplexity, args["params"].(model.DeleteUserInput)), true return e.complexity.Mutation.DeleteUser(childComplexity, args["params"].(model.DeleteUserInput)), true
case "Mutation._enable_access":
if e.complexity.Mutation.EnableAccess == nil {
break
}
args, err := ec.field_Mutation__enable_access_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Mutation.EnableAccess(childComplexity, args["param"].(model.UpdateAccessInput)), true
case "Mutation.forgot_password": case "Mutation.forgot_password":
if e.complexity.Mutation.ForgotPassword == nil { if e.complexity.Mutation.ForgotPassword == nil {
break break
@ -763,6 +780,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Mutation.Revoke(childComplexity, args["params"].(model.OAuthRevokeInput)), true return e.complexity.Mutation.Revoke(childComplexity, args["params"].(model.OAuthRevokeInput)), true
case "Mutation._revoke_access":
if e.complexity.Mutation.RevokeAccess == nil {
break
}
args, err := ec.field_Mutation__revoke_access_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Mutation.RevokeAccess(childComplexity, args["param"].(model.UpdateAccessInput)), true
case "Mutation.signup": case "Mutation.signup":
if e.complexity.Mutation.Signup == nil { if e.complexity.Mutation.Signup == nil {
break break
@ -1032,6 +1061,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.User.PreferredUsername(childComplexity), true return e.complexity.User.PreferredUsername(childComplexity), true
case "User.revoked_timestamp":
if e.complexity.User.RevokedTimestamp == nil {
break
}
return e.complexity.User.RevokedTimestamp(childComplexity), true
case "User.roles": case "User.roles":
if e.complexity.User.Roles == nil { if e.complexity.User.Roles == nil {
break break
@ -1260,6 +1296,7 @@ type User {
roles: [String!]! roles: [String!]!
created_at: Int64 created_at: Int64
updated_at: Int64 updated_at: Int64
revoked_timestamp: Int64
} }
type Users { type Users {
@ -1502,6 +1539,10 @@ input InviteMemberInput {
redirect_uri: String redirect_uri: String
} }
input UpdateAccessInput {
user_id: String!
}
input ValidateJWTTokenInput { input ValidateJWTTokenInput {
token_type: String! token_type: String!
token: String! token: String!
@ -1527,6 +1568,8 @@ type Mutation {
_admin_logout: Response! _admin_logout: Response!
_update_env(params: UpdateEnvInput!): Response! _update_env(params: UpdateEnvInput!): Response!
_invite_members(params: InviteMemberInput!): Response! _invite_members(params: InviteMemberInput!): Response!
_revoke_access(param: UpdateAccessInput!): Response!
_enable_access(param: UpdateAccessInput!): Response!
} }
type Query { type Query {
@ -1593,6 +1636,21 @@ func (ec *executionContext) field_Mutation__delete_user_args(ctx context.Context
return args, nil return args, nil
} }
func (ec *executionContext) field_Mutation__enable_access_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 model.UpdateAccessInput
if tmp, ok := rawArgs["param"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("param"))
arg0, err = ec.unmarshalNUpdateAccessInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐUpdateAccessInput(ctx, tmp)
if err != nil {
return nil, err
}
}
args["param"] = arg0
return args, nil
}
func (ec *executionContext) field_Mutation__invite_members_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { func (ec *executionContext) field_Mutation__invite_members_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{}{}
@ -1608,6 +1666,21 @@ func (ec *executionContext) field_Mutation__invite_members_args(ctx context.Cont
return args, nil return args, nil
} }
func (ec *executionContext) field_Mutation__revoke_access_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 model.UpdateAccessInput
if tmp, ok := rawArgs["param"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("param"))
arg0, err = ec.unmarshalNUpdateAccessInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐUpdateAccessInput(ctx, tmp)
if err != nil {
return nil, err
}
}
args["param"] = arg0
return args, nil
}
func (ec *executionContext) field_Mutation__update_env_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { func (ec *executionContext) field_Mutation__update_env_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{}{}
@ -4397,6 +4470,90 @@ func (ec *executionContext) _Mutation__invite_members(ctx context.Context, field
return ec.marshalNResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐResponse(ctx, field.Selections, res) return ec.marshalNResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐResponse(ctx, field.Selections, res)
} }
func (ec *executionContext) _Mutation__revoke_access(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__revoke_access_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().RevokeAccess(rctx, args["param"].(model.UpdateAccessInput))
})
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.Response)
fc.Result = res
return ec.marshalNResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐResponse(ctx, field.Selections, res)
}
func (ec *executionContext) _Mutation__enable_access(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__enable_access_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().EnableAccess(rctx, args["param"].(model.UpdateAccessInput))
})
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.Response)
fc.Result = res
return ec.marshalNResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐResponse(ctx, field.Selections, res)
}
func (ec *executionContext) _Pagination_limit(ctx context.Context, field graphql.CollectedField, obj *model.Pagination) (ret graphql.Marshaler) { func (ec *executionContext) _Pagination_limit(ctx context.Context, field graphql.CollectedField, obj *model.Pagination) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -5510,6 +5667,38 @@ func (ec *executionContext) _User_updated_at(ctx context.Context, field graphql.
return ec.marshalOInt642ᚖint64(ctx, field.Selections, res) return ec.marshalOInt642ᚖint64(ctx, field.Selections, res)
} }
func (ec *executionContext) _User_revoked_timestamp(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "User",
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.RevokedTimestamp, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*int64)
fc.Result = res
return ec.marshalOInt642ᚖint64(ctx, field.Selections, res)
}
func (ec *executionContext) _Users_pagination(ctx context.Context, field graphql.CollectedField, obj *model.Users) (ret graphql.Marshaler) { func (ec *executionContext) _Users_pagination(ctx context.Context, field graphql.CollectedField, obj *model.Users) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -7644,6 +7833,29 @@ func (ec *executionContext) unmarshalInputSignUpInput(ctx context.Context, obj i
return it, nil return it, nil
} }
func (ec *executionContext) unmarshalInputUpdateAccessInput(ctx context.Context, obj interface{}) (model.UpdateAccessInput, error) {
var it model.UpdateAccessInput
asMap := map[string]interface{}{}
for k, v := range obj.(map[string]interface{}) {
asMap[k] = v
}
for k, v := range asMap {
switch k {
case "user_id":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("user_id"))
it.UserID, err = ec.unmarshalNString2string(ctx, v)
if err != nil {
return it, err
}
}
}
return it, nil
}
func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, obj interface{}) (model.UpdateEnvInput, error) { func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, obj interface{}) (model.UpdateEnvInput, error) {
var it model.UpdateEnvInput var it model.UpdateEnvInput
asMap := map[string]interface{}{} asMap := map[string]interface{}{}
@ -8572,6 +8784,16 @@ 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 "_revoke_access":
out.Values[i] = ec._Mutation__revoke_access(ctx, field)
if out.Values[i] == graphql.Null {
invalids++
}
case "_enable_access":
out.Values[i] = ec._Mutation__enable_access(ctx, field)
if out.Values[i] == graphql.Null {
invalids++
}
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
} }
@ -8854,6 +9076,8 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj
out.Values[i] = ec._User_created_at(ctx, field, obj) out.Values[i] = ec._User_created_at(ctx, field, obj)
case "updated_at": case "updated_at":
out.Values[i] = ec._User_updated_at(ctx, field, obj) out.Values[i] = ec._User_updated_at(ctx, field, obj)
case "revoked_timestamp":
out.Values[i] = ec._User_revoked_timestamp(ctx, field, obj)
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
} }
@ -9466,6 +9690,11 @@ func (ec *executionContext) marshalNString2ᚕstringᚄ(ctx context.Context, sel
return ret return ret
} }
func (ec *executionContext) unmarshalNUpdateAccessInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐUpdateAccessInput(ctx context.Context, v interface{}) (model.UpdateAccessInput, error) {
res, err := ec.unmarshalInputUpdateAccessInput(ctx, v)
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) unmarshalNUpdateEnvInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐUpdateEnvInput(ctx context.Context, v interface{}) (model.UpdateEnvInput, error) { func (ec *executionContext) unmarshalNUpdateEnvInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐUpdateEnvInput(ctx context.Context, v interface{}) (model.UpdateEnvInput, error) {
res, err := ec.unmarshalInputUpdateEnvInput(ctx, v) res, err := ec.unmarshalInputUpdateEnvInput(ctx, v)
return res, graphql.ErrorOnPath(ctx, err) return res, graphql.ErrorOnPath(ctx, err)

View File

@ -164,6 +164,10 @@ type SignUpInput struct {
RedirectURI *string `json:"redirect_uri"` RedirectURI *string `json:"redirect_uri"`
} }
type UpdateAccessInput struct {
UserID string `json:"user_id"`
}
type UpdateEnvInput struct { type UpdateEnvInput struct {
AdminSecret *string `json:"ADMIN_SECRET"` AdminSecret *string `json:"ADMIN_SECRET"`
CustomAccessTokenScript *string `json:"CUSTOM_ACCESS_TOKEN_SCRIPT"` CustomAccessTokenScript *string `json:"CUSTOM_ACCESS_TOKEN_SCRIPT"`
@ -249,6 +253,7 @@ type User struct {
Roles []string `json:"roles"` Roles []string `json:"roles"`
CreatedAt *int64 `json:"created_at"` CreatedAt *int64 `json:"created_at"`
UpdatedAt *int64 `json:"updated_at"` UpdatedAt *int64 `json:"updated_at"`
RevokedTimestamp *int64 `json:"revoked_timestamp"`
} }
type Users struct { type Users struct {

View File

@ -43,6 +43,7 @@ type User {
roles: [String!]! roles: [String!]!
created_at: Int64 created_at: Int64
updated_at: Int64 updated_at: Int64
revoked_timestamp: Int64
} }
type Users { type Users {
@ -285,6 +286,10 @@ input InviteMemberInput {
redirect_uri: String redirect_uri: String
} }
input UpdateAccessInput {
user_id: String!
}
input ValidateJWTTokenInput { input ValidateJWTTokenInput {
token_type: String! token_type: String!
token: String! token: String!
@ -310,6 +315,8 @@ type Mutation {
_admin_logout: Response! _admin_logout: Response!
_update_env(params: UpdateEnvInput!): Response! _update_env(params: UpdateEnvInput!): Response!
_invite_members(params: InviteMemberInput!): Response! _invite_members(params: InviteMemberInput!): Response!
_revoke_access(param: UpdateAccessInput!): Response!
_enable_access(param: UpdateAccessInput!): Response!
} }
type Query { type Query {

View File

@ -79,6 +79,14 @@ func (r *mutationResolver) InviteMembers(ctx context.Context, params model.Invit
return resolvers.InviteMembersResolver(ctx, params) return resolvers.InviteMembersResolver(ctx, params)
} }
func (r *mutationResolver) RevokeAccess(ctx context.Context, param model.UpdateAccessInput) (*model.Response, error) {
return resolvers.RevokeAccessResolver(ctx, param)
}
func (r *mutationResolver) EnableAccess(ctx context.Context, param model.UpdateAccessInput) (*model.Response, error) {
return resolvers.EnableAccessResolver(ctx, param)
}
func (r *queryResolver) Meta(ctx context.Context) (*model.Meta, error) { func (r *queryResolver) Meta(ctx context.Context) (*model.Meta, error) {
return resolvers.MetaResolver(ctx) return resolvers.MetaResolver(ctx)
} }
@ -117,7 +125,5 @@ func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResol
// Query returns generated.QueryResolver implementation. // Query returns generated.QueryResolver implementation.
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} } func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
type ( type mutationResolver struct{ *Resolver }
mutationResolver struct{ *Resolver } type queryResolver struct{ *Resolver }
queryResolver struct{ *Resolver }
)

View File

@ -95,9 +95,12 @@ func OAuthCallbackHandler() gin.HandlerFunc {
user.EmailVerifiedAt = &now user.EmailVerifiedAt = &now
user, _ = db.Provider.AddUser(user) user, _ = db.Provider.AddUser(user)
} else { } else {
if user.RevokedTimestamp != nil {
c.JSON(400, gin.H{"error": "user access has been revoked"})
}
// user exists in db, check if method was google // user exists in db, check if method was google
// if not append google to existing signup method and save it // if not append google to existing signup method and save it
signupMethod := existingUser.SignupMethods signupMethod := existingUser.SignupMethods
if !strings.Contains(signupMethod, provider) { if !strings.Contains(signupMethod, provider) {
signupMethod = signupMethod + "," + provider signupMethod = signupMethod + "," + provider

View File

@ -0,0 +1,44 @@
package resolvers
import (
"context"
"fmt"
"log"
"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"
)
// EnableAccessResolver is a resolver for enabling user access
func EnableAccessResolver(ctx context.Context, params model.UpdateAccessInput) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response
if err != nil {
return res, err
}
if !token.IsSuperAdmin(gc) {
return res, fmt.Errorf("unauthorized")
}
user, err := db.Provider.GetUserByID(params.UserID)
if err != nil {
return res, err
}
user.RevokedTimestamp = nil
user, err = db.Provider.UpdateUser(user)
if err != nil {
log.Println("error updating user:", err)
return res, err
}
res = &model.Response{
Message: `user access enabled successfully`,
}
return res, nil
}

View File

@ -35,6 +35,10 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
return res, fmt.Errorf(`user with this email not found`) return res, fmt.Errorf(`user with this email not found`)
} }
if user.RevokedTimestamp != nil {
return res, fmt.Errorf(`user access has been revoked`)
}
if !strings.Contains(user.SignupMethods, constants.SignupMethodBasicAuth) { if !strings.Contains(user.SignupMethods, constants.SignupMethodBasicAuth) {
return res, fmt.Errorf(`user has not signed up email & password`) return res, fmt.Errorf(`user has not signed up email & password`)
} }

View File

@ -70,6 +70,10 @@ func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInpu
// 2. user has not signed up for one of the available role but trying to signup. // 2. user has not signed up for one of the available role but trying to signup.
// Need to modify roles in this case // Need to modify roles in this case
if user.RevokedTimestamp != nil {
return res, fmt.Errorf(`user access has been revoked`)
}
// find the unassigned roles // find the unassigned roles
if len(params.Roles) <= 0 { if len(params.Roles) <= 0 {
inputRoles = envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles) inputRoles = envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles)

View File

@ -0,0 +1,49 @@
package resolvers
import (
"context"
"fmt"
"log"
"time"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils"
)
// RevokeAccessResolver is a resolver for revoking user access
func RevokeAccessResolver(ctx context.Context, params model.UpdateAccessInput) (*model.Response, error) {
gc, err := utils.GinContextFromContext(ctx)
var res *model.Response
if err != nil {
return res, err
}
if !token.IsSuperAdmin(gc) {
return res, fmt.Errorf("unauthorized")
}
user, err := db.Provider.GetUserByID(params.UserID)
if err != nil {
return res, err
}
now := time.Now().Unix()
user.RevokedTimestamp = &now
user, err = db.Provider.UpdateUser(user)
if err != nil {
log.Println("error updating user:", err)
return res, err
}
go sessionstore.DeleteAllUserSession(fmt.Sprintf("%x", user.ID))
res = &model.Response{
Message: `user access revoked successfully`,
}
return res, nil
}