mirror of
https://github.com/minio/minio.git
synced 2024-12-24 22:25:54 -05:00
parent
76b21de0c6
commit
3bd9636a5b
@ -102,6 +102,18 @@ func toAdminAPIErr(ctx context.Context, err error) APIError {
|
|||||||
Description: err.Error(),
|
Description: err.Error(),
|
||||||
HTTPStatusCode: http.StatusForbidden,
|
HTTPStatusCode: http.StatusForbidden,
|
||||||
}
|
}
|
||||||
|
case errors.Is(err, errIAMServiceAccount):
|
||||||
|
apiErr = APIError{
|
||||||
|
Code: "XMinioIAMServiceAccount",
|
||||||
|
Description: err.Error(),
|
||||||
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
|
}
|
||||||
|
case errors.Is(err, errIAMServiceAccountUsed):
|
||||||
|
apiErr = APIError{
|
||||||
|
Code: "XMinioIAMServiceAccountUsed",
|
||||||
|
Description: err.Error(),
|
||||||
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
|
}
|
||||||
case errors.Is(err, errIAMNotInitialized):
|
case errors.Is(err, errIAMNotInitialized):
|
||||||
apiErr = APIError{
|
apiErr = APIError{
|
||||||
Code: "XMinioIAMNotInitialized",
|
Code: "XMinioIAMNotInitialized",
|
||||||
|
@ -816,18 +816,15 @@ func (a adminAPIHandlers) InfoServiceAccount(w http.ResponseWriter, r *http.Requ
|
|||||||
|
|
||||||
var svcAccountPolicy iampolicy.Policy
|
var svcAccountPolicy iampolicy.Policy
|
||||||
|
|
||||||
impliedPolicy := policy == nil
|
if policy != nil {
|
||||||
|
svcAccountPolicy = *policy
|
||||||
// If policy is empty, check for policy of the parent user
|
|
||||||
if !impliedPolicy {
|
|
||||||
svcAccountPolicy = svcAccountPolicy.Merge(*policy)
|
|
||||||
} else {
|
} else {
|
||||||
policiesNames, err := globalIAMSys.PolicyDBGet(svcAccount.ParentUser, false)
|
policiesNames, err := globalIAMSys.PolicyDBGet(svcAccount.ParentUser, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
svcAccountPolicy = svcAccountPolicy.Merge(globalIAMSys.GetCombinedPolicy(policiesNames...))
|
svcAccountPolicy = globalIAMSys.GetCombinedPolicy(policiesNames...)
|
||||||
}
|
}
|
||||||
|
|
||||||
policyJSON, err := json.MarshalIndent(svcAccountPolicy, "", " ")
|
policyJSON, err := json.MarshalIndent(svcAccountPolicy, "", " ")
|
||||||
@ -839,7 +836,7 @@ func (a adminAPIHandlers) InfoServiceAccount(w http.ResponseWriter, r *http.Requ
|
|||||||
infoResp := madmin.InfoServiceAccountResp{
|
infoResp := madmin.InfoServiceAccountResp{
|
||||||
ParentUser: svcAccount.ParentUser,
|
ParentUser: svcAccount.ParentUser,
|
||||||
AccountStatus: svcAccount.Status,
|
AccountStatus: svcAccount.Status,
|
||||||
ImpliedPolicy: impliedPolicy,
|
ImpliedPolicy: policy == nil,
|
||||||
Policy: string(policyJSON),
|
Policy: string(policyJSON),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,20 +37,25 @@ import (
|
|||||||
func getAnonReadOnlyBucketPolicy(bucketName string) *policy.Policy {
|
func getAnonReadOnlyBucketPolicy(bucketName string) *policy.Policy {
|
||||||
return &policy.Policy{
|
return &policy.Policy{
|
||||||
Version: policy.DefaultVersion,
|
Version: policy.DefaultVersion,
|
||||||
Statements: []policy.Statement{policy.NewStatement(
|
Statements: []policy.Statement{
|
||||||
|
policy.NewStatement(
|
||||||
|
"",
|
||||||
policy.Allow,
|
policy.Allow,
|
||||||
policy.NewPrincipal("*"),
|
policy.NewPrincipal("*"),
|
||||||
policy.NewActionSet(policy.GetBucketLocationAction, policy.ListBucketAction),
|
policy.NewActionSet(policy.GetBucketLocationAction, policy.ListBucketAction),
|
||||||
policy.NewResourceSet(policy.NewResource(bucketName, "")),
|
policy.NewResourceSet(policy.NewResource(bucketName, "")),
|
||||||
condition.NewFunctions(),
|
condition.NewFunctions(),
|
||||||
)},
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAnonWriteOnlyBucketPolicy(bucketName string) *policy.Policy {
|
func getAnonWriteOnlyBucketPolicy(bucketName string) *policy.Policy {
|
||||||
return &policy.Policy{
|
return &policy.Policy{
|
||||||
Version: policy.DefaultVersion,
|
Version: policy.DefaultVersion,
|
||||||
Statements: []policy.Statement{policy.NewStatement(
|
Statements: []policy.Statement{
|
||||||
|
policy.NewStatement(
|
||||||
|
"",
|
||||||
policy.Allow,
|
policy.Allow,
|
||||||
policy.NewPrincipal("*"),
|
policy.NewPrincipal("*"),
|
||||||
policy.NewActionSet(
|
policy.NewActionSet(
|
||||||
@ -59,27 +64,33 @@ func getAnonWriteOnlyBucketPolicy(bucketName string) *policy.Policy {
|
|||||||
),
|
),
|
||||||
policy.NewResourceSet(policy.NewResource(bucketName, "")),
|
policy.NewResourceSet(policy.NewResource(bucketName, "")),
|
||||||
condition.NewFunctions(),
|
condition.NewFunctions(),
|
||||||
)},
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAnonReadOnlyObjectPolicy(bucketName, prefix string) *policy.Policy {
|
func getAnonReadOnlyObjectPolicy(bucketName, prefix string) *policy.Policy {
|
||||||
return &policy.Policy{
|
return &policy.Policy{
|
||||||
Version: policy.DefaultVersion,
|
Version: policy.DefaultVersion,
|
||||||
Statements: []policy.Statement{policy.NewStatement(
|
Statements: []policy.Statement{
|
||||||
|
policy.NewStatement(
|
||||||
|
"",
|
||||||
policy.Allow,
|
policy.Allow,
|
||||||
policy.NewPrincipal("*"),
|
policy.NewPrincipal("*"),
|
||||||
policy.NewActionSet(policy.GetObjectAction),
|
policy.NewActionSet(policy.GetObjectAction),
|
||||||
policy.NewResourceSet(policy.NewResource(bucketName, prefix)),
|
policy.NewResourceSet(policy.NewResource(bucketName, prefix)),
|
||||||
condition.NewFunctions(),
|
condition.NewFunctions(),
|
||||||
)},
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAnonWriteOnlyObjectPolicy(bucketName, prefix string) *policy.Policy {
|
func getAnonWriteOnlyObjectPolicy(bucketName, prefix string) *policy.Policy {
|
||||||
return &policy.Policy{
|
return &policy.Policy{
|
||||||
Version: policy.DefaultVersion,
|
Version: policy.DefaultVersion,
|
||||||
Statements: []policy.Statement{policy.NewStatement(
|
Statements: []policy.Statement{
|
||||||
|
policy.NewStatement(
|
||||||
|
"",
|
||||||
policy.Allow,
|
policy.Allow,
|
||||||
policy.NewPrincipal("*"),
|
policy.NewPrincipal("*"),
|
||||||
policy.NewActionSet(
|
policy.NewActionSet(
|
||||||
@ -90,7 +101,8 @@ func getAnonWriteOnlyObjectPolicy(bucketName, prefix string) *policy.Policy {
|
|||||||
),
|
),
|
||||||
policy.NewResourceSet(policy.NewResource(bucketName, prefix)),
|
policy.NewResourceSet(policy.NewResource(bucketName, prefix)),
|
||||||
condition.NewFunctions(),
|
condition.NewFunctions(),
|
||||||
)},
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1416,6 +1416,7 @@ func (a *azureObjects) GetBucketPolicy(ctx context.Context, bucket string) (*pol
|
|||||||
Version: policy.DefaultVersion,
|
Version: policy.DefaultVersion,
|
||||||
Statements: []policy.Statement{
|
Statements: []policy.Statement{
|
||||||
policy.NewStatement(
|
policy.NewStatement(
|
||||||
|
"",
|
||||||
policy.Allow,
|
policy.Allow,
|
||||||
policy.NewPrincipal("*"),
|
policy.NewPrincipal("*"),
|
||||||
policy.NewActionSet(
|
policy.NewActionSet(
|
||||||
|
@ -1470,6 +1470,7 @@ func (l *gcsGateway) GetBucketPolicy(ctx context.Context, bucket string) (*polic
|
|||||||
Version: policy.DefaultVersion,
|
Version: policy.DefaultVersion,
|
||||||
Statements: []policy.Statement{
|
Statements: []policy.Statement{
|
||||||
policy.NewStatement(
|
policy.NewStatement(
|
||||||
|
"",
|
||||||
policy.Allow,
|
policy.Allow,
|
||||||
policy.NewPrincipal("*"),
|
policy.NewPrincipal("*"),
|
||||||
actionSet,
|
actionSet,
|
||||||
|
@ -1084,13 +1084,14 @@ func filterPolicies(cache *iamCache, policyName string, bucketName string) (stri
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
p, found := cache.iamPolicyDocsMap[policy]
|
p, found := cache.iamPolicyDocsMap[policy]
|
||||||
if found {
|
if !found {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if bucketName == "" || p.Policy.MatchResource(bucketName) {
|
if bucketName == "" || p.Policy.MatchResource(bucketName) {
|
||||||
policies = append(policies, policy)
|
policies = append(policies, policy)
|
||||||
combinedPolicy = combinedPolicy.Merge(p.Policy)
|
combinedPolicy = combinedPolicy.Merge(p.Policy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return strings.Join(policies, ","), combinedPolicy
|
return strings.Join(policies, ","), combinedPolicy
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1511,13 +1512,16 @@ func (store *IAMStoreSys) AddServiceAccount(ctx context.Context, cred auth.Crede
|
|||||||
// Found newly requested service account, to be an existing account -
|
// Found newly requested service account, to be an existing account -
|
||||||
// reject such operation (updates to the service account are handled in
|
// reject such operation (updates to the service account are handled in
|
||||||
// a different API).
|
// a different API).
|
||||||
if _, found := cache.iamUsersMap[accessKey]; found {
|
if scred, found := cache.iamUsersMap[accessKey]; found {
|
||||||
return errIAMActionNotAllowed
|
if scred.ParentUser != parentUser {
|
||||||
|
return errIAMServiceAccountUsed
|
||||||
|
}
|
||||||
|
return errIAMServiceAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parent user must not be a service account.
|
// Parent user must not be a service account.
|
||||||
if cr, found := cache.iamUsersMap[parentUser]; found && cr.IsServiceAccount() {
|
if cr, found := cache.iamUsersMap[parentUser]; found && cr.IsServiceAccount() {
|
||||||
return errIAMActionNotAllowed
|
return errIAMServiceAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
u := newUserIdentity(cred)
|
u := newUserIdentity(cred)
|
||||||
|
15
cmd/iam.go
15
cmd/iam.go
@ -883,18 +883,19 @@ func (sys *IAMSys) getServiceAccount(ctx context.Context, accessKey string) (aut
|
|||||||
var embeddedPolicy *iampolicy.Policy
|
var embeddedPolicy *iampolicy.Policy
|
||||||
|
|
||||||
jwtClaims, err := auth.ExtractClaims(sa.SessionToken, globalActiveCred.SecretKey)
|
jwtClaims, err := auth.ExtractClaims(sa.SessionToken, globalActiveCred.SecretKey)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
|
return auth.Credentials{}, nil, err
|
||||||
|
}
|
||||||
pt, ptok := jwtClaims.Lookup(iamPolicyClaimNameSA())
|
pt, ptok := jwtClaims.Lookup(iamPolicyClaimNameSA())
|
||||||
sp, spok := jwtClaims.Lookup(iampolicy.SessionPolicyName)
|
sp, spok := jwtClaims.Lookup(iampolicy.SessionPolicyName)
|
||||||
if ptok && spok && pt == "embedded-policy" {
|
if ptok && spok && pt == "embedded-policy" {
|
||||||
policyBytes, err := base64.StdEncoding.DecodeString(sp)
|
policyBytes, err := base64.StdEncoding.DecodeString(sp)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
p, err := iampolicy.ParseConfig(bytes.NewReader(policyBytes))
|
return auth.Credentials{}, nil, err
|
||||||
if err == nil {
|
|
||||||
policy := iampolicy.Policy{}.Merge(*p)
|
|
||||||
embeddedPolicy = &policy
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
embeddedPolicy, err = iampolicy.ParseConfig(bytes.NewReader(policyBytes))
|
||||||
|
if err != nil {
|
||||||
|
return auth.Credentials{}, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,14 +31,14 @@ func TestPolicySysIsAllowed(t *testing.T) {
|
|||||||
p := &policy.Policy{
|
p := &policy.Policy{
|
||||||
Version: policy.DefaultVersion,
|
Version: policy.DefaultVersion,
|
||||||
Statements: []policy.Statement{
|
Statements: []policy.Statement{
|
||||||
policy.NewStatement(
|
policy.NewStatement("",
|
||||||
policy.Allow,
|
policy.Allow,
|
||||||
policy.NewPrincipal("*"),
|
policy.NewPrincipal("*"),
|
||||||
policy.NewActionSet(policy.GetBucketLocationAction),
|
policy.NewActionSet(policy.GetBucketLocationAction),
|
||||||
policy.NewResourceSet(policy.NewResource("mybucket", "")),
|
policy.NewResourceSet(policy.NewResource("mybucket", "")),
|
||||||
condition.NewFunctions(),
|
condition.NewFunctions(),
|
||||||
),
|
),
|
||||||
policy.NewStatement(
|
policy.NewStatement("",
|
||||||
policy.Allow,
|
policy.Allow,
|
||||||
policy.NewPrincipal("*"),
|
policy.NewPrincipal("*"),
|
||||||
policy.NewActionSet(policy.PutObjectAction),
|
policy.NewActionSet(policy.PutObjectAction),
|
||||||
@ -164,14 +164,14 @@ func TestPolicyToBucketAccessPolicy(t *testing.T) {
|
|||||||
case1Policy := &policy.Policy{
|
case1Policy := &policy.Policy{
|
||||||
Version: policy.DefaultVersion,
|
Version: policy.DefaultVersion,
|
||||||
Statements: []policy.Statement{
|
Statements: []policy.Statement{
|
||||||
policy.NewStatement(
|
policy.NewStatement("",
|
||||||
policy.Allow,
|
policy.Allow,
|
||||||
policy.NewPrincipal("*"),
|
policy.NewPrincipal("*"),
|
||||||
policy.NewActionSet(policy.GetBucketLocationAction, policy.ListBucketAction),
|
policy.NewActionSet(policy.GetBucketLocationAction, policy.ListBucketAction),
|
||||||
policy.NewResourceSet(policy.NewResource("mybucket", "")),
|
policy.NewResourceSet(policy.NewResource("mybucket", "")),
|
||||||
condition.NewFunctions(),
|
condition.NewFunctions(),
|
||||||
),
|
),
|
||||||
policy.NewStatement(
|
policy.NewStatement("",
|
||||||
policy.Allow,
|
policy.Allow,
|
||||||
policy.NewPrincipal("*"),
|
policy.NewPrincipal("*"),
|
||||||
policy.NewActionSet(policy.GetObjectAction),
|
policy.NewActionSet(policy.GetObjectAction),
|
||||||
@ -199,7 +199,7 @@ func TestPolicyToBucketAccessPolicy(t *testing.T) {
|
|||||||
case3Policy := &policy.Policy{
|
case3Policy := &policy.Policy{
|
||||||
Version: "12-10-2012",
|
Version: "12-10-2012",
|
||||||
Statements: []policy.Statement{
|
Statements: []policy.Statement{
|
||||||
policy.NewStatement(
|
policy.NewStatement("",
|
||||||
policy.Allow,
|
policy.Allow,
|
||||||
policy.NewPrincipal("*"),
|
policy.NewPrincipal("*"),
|
||||||
policy.NewActionSet(policy.PutObjectAction),
|
policy.NewActionSet(policy.PutObjectAction),
|
||||||
@ -244,14 +244,14 @@ func TestBucketAccessPolicyToPolicy(t *testing.T) {
|
|||||||
case1Result := &policy.Policy{
|
case1Result := &policy.Policy{
|
||||||
Version: policy.DefaultVersion,
|
Version: policy.DefaultVersion,
|
||||||
Statements: []policy.Statement{
|
Statements: []policy.Statement{
|
||||||
policy.NewStatement(
|
policy.NewStatement("",
|
||||||
policy.Allow,
|
policy.Allow,
|
||||||
policy.NewPrincipal("*"),
|
policy.NewPrincipal("*"),
|
||||||
policy.NewActionSet(policy.GetBucketLocationAction, policy.ListBucketAction),
|
policy.NewActionSet(policy.GetBucketLocationAction, policy.ListBucketAction),
|
||||||
policy.NewResourceSet(policy.NewResource("mybucket", "")),
|
policy.NewResourceSet(policy.NewResource("mybucket", "")),
|
||||||
condition.NewFunctions(),
|
condition.NewFunctions(),
|
||||||
),
|
),
|
||||||
policy.NewStatement(
|
policy.NewStatement("",
|
||||||
policy.Allow,
|
policy.Allow,
|
||||||
policy.NewPrincipal("*"),
|
policy.NewPrincipal("*"),
|
||||||
policy.NewActionSet(policy.GetObjectAction),
|
policy.NewActionSet(policy.GetObjectAction),
|
||||||
|
@ -91,6 +91,12 @@ var errTooManyPolicies = errors.New("Only a single policy may be specified here.
|
|||||||
// error returned in IAM subsystem when an external users systems is configured.
|
// error returned in IAM subsystem when an external users systems is configured.
|
||||||
var errIAMActionNotAllowed = errors.New("Specified IAM action is not allowed")
|
var errIAMActionNotAllowed = errors.New("Specified IAM action is not allowed")
|
||||||
|
|
||||||
|
// error returned in IAM service account
|
||||||
|
var errIAMServiceAccount = errors.New("Specified service account cannot be updated in this API call")
|
||||||
|
|
||||||
|
// error returned in IAM service account is already used.
|
||||||
|
var errIAMServiceAccountUsed = errors.New("Specified service account is used by another user")
|
||||||
|
|
||||||
// error returned in IAM subsystem when IAM sub-system is still being initialized.
|
// error returned in IAM subsystem when IAM sub-system is still being initialized.
|
||||||
var errIAMNotInitialized = errors.New("IAM sub-system is being initialized, please try again")
|
var errIAMNotInitialized = errors.New("IAM sub-system is being initialized, please try again")
|
||||||
|
|
||||||
|
2
go.mod
2
go.mod
@ -52,7 +52,7 @@ require (
|
|||||||
github.com/minio/madmin-go v1.2.4
|
github.com/minio/madmin-go v1.2.4
|
||||||
github.com/minio/minio-go/v7 v7.0.20
|
github.com/minio/minio-go/v7 v7.0.20
|
||||||
github.com/minio/parquet-go v1.1.0
|
github.com/minio/parquet-go v1.1.0
|
||||||
github.com/minio/pkg v1.1.11
|
github.com/minio/pkg v1.1.14
|
||||||
github.com/minio/selfupdate v0.4.0
|
github.com/minio/selfupdate v0.4.0
|
||||||
github.com/minio/sha256-simd v1.0.0
|
github.com/minio/sha256-simd v1.0.0
|
||||||
github.com/minio/simdjson-go v0.2.1
|
github.com/minio/simdjson-go v0.2.1
|
||||||
|
3
go.sum
3
go.sum
@ -1114,8 +1114,9 @@ github.com/minio/pkg v1.0.3/go.mod h1:obU54TZ9QlMv0TRaDgQ/JTzf11ZSXxnSfLrm4tMtBP
|
|||||||
github.com/minio/pkg v1.0.4/go.mod h1:obU54TZ9QlMv0TRaDgQ/JTzf11ZSXxnSfLrm4tMtBP8=
|
github.com/minio/pkg v1.0.4/go.mod h1:obU54TZ9QlMv0TRaDgQ/JTzf11ZSXxnSfLrm4tMtBP8=
|
||||||
github.com/minio/pkg v1.0.11/go.mod h1:32x/3OmGB0EOi1N+3ggnp+B5VFkSBBB9svPMVfpnf14=
|
github.com/minio/pkg v1.0.11/go.mod h1:32x/3OmGB0EOi1N+3ggnp+B5VFkSBBB9svPMVfpnf14=
|
||||||
github.com/minio/pkg v1.1.3/go.mod h1:32x/3OmGB0EOi1N+3ggnp+B5VFkSBBB9svPMVfpnf14=
|
github.com/minio/pkg v1.1.3/go.mod h1:32x/3OmGB0EOi1N+3ggnp+B5VFkSBBB9svPMVfpnf14=
|
||||||
github.com/minio/pkg v1.1.11 h1:FQMYrOZzZenQUVwvxM0YjoAZB15Wb+m3TZSu33Lynx0=
|
|
||||||
github.com/minio/pkg v1.1.11/go.mod h1:2WJAxesjzmPK9MnLZKm5n1hVYfBg04f2GQs6N5ImNU8=
|
github.com/minio/pkg v1.1.11/go.mod h1:2WJAxesjzmPK9MnLZKm5n1hVYfBg04f2GQs6N5ImNU8=
|
||||||
|
github.com/minio/pkg v1.1.14 h1:9tGPH6p0YEsIOHOY3sh2Yl5Dn0JNTkNOdZW/p+MKIi4=
|
||||||
|
github.com/minio/pkg v1.1.14/go.mod h1:2WJAxesjzmPK9MnLZKm5n1hVYfBg04f2GQs6N5ImNU8=
|
||||||
github.com/minio/selfupdate v0.3.1/go.mod h1:b8ThJzzH7u2MkF6PcIra7KaXO9Khf6alWPvMSyTDCFM=
|
github.com/minio/selfupdate v0.3.1/go.mod h1:b8ThJzzH7u2MkF6PcIra7KaXO9Khf6alWPvMSyTDCFM=
|
||||||
github.com/minio/selfupdate v0.4.0 h1:A7t07pN4Ch1tBTIRStW0KhUVyykz+2muCqFsITQeEW8=
|
github.com/minio/selfupdate v0.4.0 h1:A7t07pN4Ch1tBTIRStW0KhUVyykz+2muCqFsITQeEW8=
|
||||||
github.com/minio/selfupdate v0.4.0/go.mod h1:mcDkzMgq8PRcpCRJo/NlPY7U45O5dfYl2Y0Rg7IustY=
|
github.com/minio/selfupdate v0.4.0/go.mod h1:mcDkzMgq8PRcpCRJo/NlPY7U45O5dfYl2Y0Rg7IustY=
|
||||||
|
Loading…
Reference in New Issue
Block a user