fix github integration tests (#1042)
This commit is contained in:
@@ -837,6 +837,9 @@ func (s *Source) addMembersByApp(ctx context.Context, installationClient *github
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, org := range installs {
|
for _, org := range installs {
|
||||||
|
if org.Account.GetType() != "Organization" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if err := s.addMembersByOrg(ctx, *org.Account.Login); err != nil {
|
if err := s.addMembersByOrg(ctx, *org.Account.Login); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -947,9 +950,7 @@ func (s *Source) addOrgsByUser(ctx context.Context, user string) {
|
|||||||
s.log.Debugf("Listed orgs for user %s page %d/%d", user, orgOpts.Page, resp.LastPage)
|
s.log.Debugf("Listed orgs for user %s page %d/%d", user, orgOpts.Page, resp.LastPage)
|
||||||
for _, org := range orgs {
|
for _, org := range orgs {
|
||||||
var name string
|
var name string
|
||||||
if org.Name != nil {
|
if org.Login != nil {
|
||||||
name = *org.Name
|
|
||||||
} else if org.Login != nil {
|
|
||||||
name = *org.Login
|
name = *org.Login
|
||||||
} else {
|
} else {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ func TestSource_Token(t *testing.T) {
|
|||||||
githubAppIDNew := secret.MustGetField("GITHUB_APP_ID_NEW")
|
githubAppIDNew := secret.MustGetField("GITHUB_APP_ID_NEW")
|
||||||
|
|
||||||
conn := &sourcespb.GitHub{
|
conn := &sourcespb.GitHub{
|
||||||
Repositories: []string{"https://github.com/trufflesecurity/driftwood.git"},
|
|
||||||
Credential: &sourcespb.GitHub_GithubApp{
|
Credential: &sourcespb.GitHub_GithubApp{
|
||||||
GithubApp: &credentialspb.GitHubApp{
|
GithubApp: &credentialspb.GitHubApp{
|
||||||
PrivateKey: githubPrivateKeyNew,
|
PrivateKey: githubPrivateKeyNew,
|
||||||
@@ -68,14 +67,14 @@ func TestSource_Token(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// user provided
|
// user provided
|
||||||
_, _, err = git.CloneRepoUsingToken(ctx, token, "https://github.com/trufflesecurity/trufflehog-updater.git", user)
|
_, _, err = git.CloneRepoUsingToken(ctx, token, "https://github.com/truffle-test-integration-org/another-test-repo.git", user)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// no user provided
|
// no user provided
|
||||||
_, _, err = git.CloneRepoUsingToken(ctx, token, "https://github.com/trufflesecurity/trufflehog-updater.git", "")
|
_, _, err = git.CloneRepoUsingToken(ctx, token, "https://github.com/truffle-test-integration-org/another-test-repo.git", "")
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
|
|
||||||
_, _, err = s.cloneRepo(ctx, "https://github.com/trufflesecurity/trufflehog-updater.git", installationClient)
|
_, _, err = s.cloneRepo(ctx, "https://github.com/truffle-test-integration-org/another-test-repo.git", installationClient)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +128,7 @@ func TestSource_Scan(t *testing.T) {
|
|||||||
init: init{
|
init: init{
|
||||||
name: "test source",
|
name: "test source",
|
||||||
connection: &sourcespb.GitHub{
|
connection: &sourcespb.GitHub{
|
||||||
Repositories: []string{"https://github.com/dustin-decker/secretsandstuff.git"},
|
Repositories: []string{"https://github.com/truffle-test-integration-org/another-test-repo.git"},
|
||||||
Credential: &sourcespb.GitHub_Token{
|
Credential: &sourcespb.GitHub_Token{
|
||||||
Token: githubToken,
|
Token: githubToken,
|
||||||
},
|
},
|
||||||
@@ -141,7 +140,7 @@ func TestSource_Scan(t *testing.T) {
|
|||||||
SourceMetadata: &source_metadatapb.MetaData{
|
SourceMetadata: &source_metadatapb.MetaData{
|
||||||
Data: &source_metadatapb.MetaData_Github{
|
Data: &source_metadatapb.MetaData_Github{
|
||||||
Github: &source_metadatapb.Github{
|
Github: &source_metadatapb.Github{
|
||||||
Repository: "https://github.com/dustin-decker/secretsandstuff.git",
|
Repository: "https://github.com/truffle-test-integration-org/another-test-repo.git",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -154,7 +153,7 @@ func TestSource_Scan(t *testing.T) {
|
|||||||
init: init{
|
init: init{
|
||||||
name: "test source",
|
name: "test source",
|
||||||
connection: &sourcespb.GitHub{
|
connection: &sourcespb.GitHub{
|
||||||
Repositories: []string{"https://github.com/dustin-decker/secretsandstuff"},
|
Repositories: []string{"https://github.com/truffle-test-integration-org/another-test-repo"},
|
||||||
Credential: &sourcespb.GitHub_Token{
|
Credential: &sourcespb.GitHub_Token{
|
||||||
Token: githubToken,
|
Token: githubToken,
|
||||||
},
|
},
|
||||||
@@ -166,7 +165,7 @@ func TestSource_Scan(t *testing.T) {
|
|||||||
SourceMetadata: &source_metadatapb.MetaData{
|
SourceMetadata: &source_metadatapb.MetaData{
|
||||||
Data: &source_metadatapb.MetaData_Github{
|
Data: &source_metadatapb.MetaData_Github{
|
||||||
Github: &source_metadatapb.Github{
|
Github: &source_metadatapb.Github{
|
||||||
Repository: "https://github.com/dustin-decker/secretsandstuff.git",
|
Repository: "https://github.com/truffle-test-integration-org/another-test-repo.git",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -179,7 +178,7 @@ func TestSource_Scan(t *testing.T) {
|
|||||||
init: init{
|
init: init{
|
||||||
name: "test source",
|
name: "test source",
|
||||||
connection: &sourcespb.GitHub{
|
connection: &sourcespb.GitHub{
|
||||||
Organizations: []string{"trufflesecurity"},
|
Organizations: []string{"truffle-test-integration-org"},
|
||||||
Credential: &sourcespb.GitHub_Token{
|
Credential: &sourcespb.GitHub_Token{
|
||||||
Token: githubToken,
|
Token: githubToken,
|
||||||
},
|
},
|
||||||
@@ -187,58 +186,43 @@ func TestSource_Scan(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantChunk: nil,
|
wantChunk: nil,
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
minRepo: 3,
|
minRepo: 1,
|
||||||
minOrg: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "token authenticated, username in org",
|
|
||||||
init: init{
|
|
||||||
name: "test source",
|
|
||||||
connection: &sourcespb.GitHub{
|
|
||||||
Organizations: []string{"dustin-decker"},
|
|
||||||
Credential: &sourcespb.GitHub_Token{
|
|
||||||
Token: githubToken,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantChunk: nil,
|
|
||||||
wantErr: false,
|
|
||||||
minRepo: 3,
|
|
||||||
minOrg: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "token authenticated, username in repo",
|
|
||||||
init: init{
|
|
||||||
name: "test source",
|
|
||||||
connection: &sourcespb.GitHub{
|
|
||||||
Repositories: []string{"dustin-decker"},
|
|
||||||
Credential: &sourcespb.GitHub_Token{
|
|
||||||
Token: githubToken,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantChunk: nil,
|
|
||||||
wantErr: false,
|
|
||||||
minRepo: 3,
|
|
||||||
minOrg: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "token authenticated, org in repo",
|
|
||||||
// I do not think that this is a supported case, but adding the test to specify there is no requirement.
|
|
||||||
init: init{
|
|
||||||
name: "test source",
|
|
||||||
connection: &sourcespb.GitHub{
|
|
||||||
Repositories: []string{"trufflesecurity"},
|
|
||||||
Credential: &sourcespb.GitHub_Token{
|
|
||||||
Token: githubToken,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantChunk: nil,
|
|
||||||
wantErr: false,
|
|
||||||
minRepo: 0,
|
|
||||||
minOrg: 0,
|
minOrg: 0,
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// name: "token authenticated, username in org",
|
||||||
|
// init: init{
|
||||||
|
// name: "test source",
|
||||||
|
// connection: &sourcespb.GitHub{
|
||||||
|
// Organizations: []string{"truffle-sandbox"},
|
||||||
|
// Credential: &sourcespb.GitHub_Token{
|
||||||
|
// Token: githubToken,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// wantChunk: nil,
|
||||||
|
// wantErr: false,
|
||||||
|
// minRepo: 0, // I think enumerating users with the org API does not work for newer users! Or maybe just newer users with a `-` in their name?
|
||||||
|
// // See also: https://github.com/trufflesecurity/trufflehog/issues/874
|
||||||
|
// minOrg: 0,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: "token authenticated, org in repo",
|
||||||
|
// // I do not think that this is a supported case, but adding the test to specify there is no requirement.
|
||||||
|
// init: init{
|
||||||
|
// name: "test source",
|
||||||
|
// connection: &sourcespb.GitHub{
|
||||||
|
// Repositories: []string{"truffle-test-integration-org"},
|
||||||
|
// Credential: &sourcespb.GitHub_Token{
|
||||||
|
// Token: githubToken,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// wantChunk: nil,
|
||||||
|
// wantErr: false,
|
||||||
|
// minRepo: 0,
|
||||||
|
// minOrg: 0,
|
||||||
|
// },
|
||||||
/*
|
/*
|
||||||
{
|
{
|
||||||
name: "token authenticated, no org or user (enum)",
|
name: "token authenticated, no org or user (enum)",
|
||||||
@@ -277,71 +261,69 @@ func TestSource_Scan(t *testing.T) {
|
|||||||
minOrg: 0,
|
minOrg: 0,
|
||||||
},
|
},
|
||||||
*/
|
*/
|
||||||
|
// {
|
||||||
|
// name: "unauthenticated, single org",
|
||||||
|
// init: init{
|
||||||
|
// name: "test source",
|
||||||
|
// connection: &sourcespb.GitHub{
|
||||||
|
// Organizations: []string{"trufflesecurity"},
|
||||||
|
// Credential: &sourcespb.GitHub_Unauthenticated{},
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// wantChunk: nil,
|
||||||
|
// wantErr: false,
|
||||||
|
// minRepo: 3,
|
||||||
|
// minOrg: 1,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: "unauthenticated, single repo",
|
||||||
|
// init: init{
|
||||||
|
// name: "test source",
|
||||||
|
// connection: &sourcespb.GitHub{
|
||||||
|
// Repositories: []string{"https://github.com/trufflesecurity/driftwood.git"},
|
||||||
|
// Credential: &sourcespb.GitHub_Unauthenticated{},
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// wantChunk: &sources.Chunk{
|
||||||
|
// SourceType: sourcespb.SourceType_SOURCE_TYPE_GITHUB,
|
||||||
|
// SourceName: "test source",
|
||||||
|
// SourceMetadata: &source_metadatapb.MetaData{
|
||||||
|
// Data: &source_metadatapb.MetaData_Github{
|
||||||
|
// Github: &source_metadatapb.Github{
|
||||||
|
// Repository: "https://github.com/trufflesecurity/driftwood.git",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// Verify: false,
|
||||||
|
// },
|
||||||
|
// wantErr: false,
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
name: "unauthenticated, single org",
|
name: "app authenticated, no repo or org",
|
||||||
init: init{
|
init: init{
|
||||||
name: "test source",
|
name: "test source",
|
||||||
connection: &sourcespb.GitHub{
|
connection: &sourcespb.GitHub{
|
||||||
Organizations: []string{"trufflesecurity"},
|
ScanUsers: true,
|
||||||
Credential: &sourcespb.GitHub_Unauthenticated{},
|
Credential: &sourcespb.GitHub_GithubApp{
|
||||||
|
GithubApp: &credentialspb.GitHubApp{
|
||||||
|
PrivateKey: githubPrivateKeyNew,
|
||||||
|
InstallationId: githubInstallationIDNew,
|
||||||
|
AppId: githubAppIDNew,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantChunk: nil,
|
wantChunk: nil,
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
minRepo: 3,
|
minRepo: 1,
|
||||||
minOrg: 1,
|
minOrg: 0,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "unauthenticated, single repo",
|
|
||||||
init: init{
|
|
||||||
name: "test source",
|
|
||||||
connection: &sourcespb.GitHub{
|
|
||||||
Repositories: []string{"https://github.com/trufflesecurity/driftwood.git"},
|
|
||||||
Credential: &sourcespb.GitHub_Unauthenticated{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantChunk: &sources.Chunk{
|
|
||||||
SourceType: sourcespb.SourceType_SOURCE_TYPE_GITHUB,
|
|
||||||
SourceName: "test source",
|
|
||||||
SourceMetadata: &source_metadatapb.MetaData{
|
|
||||||
Data: &source_metadatapb.MetaData_Github{
|
|
||||||
Github: &source_metadatapb.Github{
|
|
||||||
Repository: "https://github.com/trufflesecurity/driftwood.git",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Verify: false,
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
/*
|
|
||||||
{
|
|
||||||
name: "app authenticated, no repo or org",
|
|
||||||
init: init{
|
|
||||||
name: "test source",
|
|
||||||
connection: &sourcespb.GitHub{
|
|
||||||
ScanUsers: true,
|
|
||||||
Credential: &sourcespb.GitHub_GithubApp{
|
|
||||||
GithubApp: &credentialspb.GitHubApp{
|
|
||||||
PrivateKey: githubPrivateKeyNew,
|
|
||||||
InstallationId: githubInstallationIDNew,
|
|
||||||
AppId: githubAppIDNew,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantChunk: nil,
|
|
||||||
wantErr: false,
|
|
||||||
minRepo: 3,
|
|
||||||
minOrg: 0,
|
|
||||||
},
|
|
||||||
*/
|
|
||||||
{
|
{
|
||||||
name: "app authenticated, single repo",
|
name: "app authenticated, single repo",
|
||||||
init: init{
|
init: init{
|
||||||
name: "test source",
|
name: "test source",
|
||||||
connection: &sourcespb.GitHub{
|
connection: &sourcespb.GitHub{
|
||||||
Repositories: []string{"https://github.com/trufflesecurity/driftwood.git"},
|
Repositories: []string{"https://github.com/truffle-test-integration-org/another-test-repo.git"},
|
||||||
Credential: &sourcespb.GitHub_GithubApp{
|
Credential: &sourcespb.GitHub_GithubApp{
|
||||||
GithubApp: &credentialspb.GitHubApp{
|
GithubApp: &credentialspb.GitHubApp{
|
||||||
PrivateKey: githubPrivateKeyNew,
|
PrivateKey: githubPrivateKeyNew,
|
||||||
@@ -357,7 +339,7 @@ func TestSource_Scan(t *testing.T) {
|
|||||||
SourceMetadata: &source_metadatapb.MetaData{
|
SourceMetadata: &source_metadatapb.MetaData{
|
||||||
Data: &source_metadatapb.MetaData_Github{
|
Data: &source_metadatapb.MetaData_Github{
|
||||||
Github: &source_metadatapb.Github{
|
Github: &source_metadatapb.Github{
|
||||||
Repository: "https://github.com/trufflesecurity/driftwood.git",
|
Repository: "https://github.com/truffle-test-integration-org/another-test-repo.git",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -372,7 +354,7 @@ func TestSource_Scan(t *testing.T) {
|
|||||||
init: init{
|
init: init{
|
||||||
name: "test source",
|
name: "test source",
|
||||||
connection: &sourcespb.GitHub{
|
connection: &sourcespb.GitHub{
|
||||||
Organizations: []string{"trufflesecurity"},
|
Organizations: []string{"truffle-test-integration-org"},
|
||||||
Credential: &sourcespb.GitHub_GithubApp{
|
Credential: &sourcespb.GitHub_GithubApp{
|
||||||
GithubApp: &credentialspb.GitHubApp{
|
GithubApp: &credentialspb.GitHubApp{
|
||||||
PrivateKey: githubPrivateKeyNew,
|
PrivateKey: githubPrivateKeyNew,
|
||||||
@@ -385,7 +367,7 @@ func TestSource_Scan(t *testing.T) {
|
|||||||
wantChunk: nil,
|
wantChunk: nil,
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
minRepo: 1,
|
minRepo: 1,
|
||||||
minOrg: 1,
|
minOrg: 0,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,7 +440,7 @@ func TestSource_paginateGists(t *testing.T) {
|
|||||||
minRepos int
|
minRepos int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "get gist secret",
|
name: "get gist",
|
||||||
init: init{
|
init: init{
|
||||||
name: "test source",
|
name: "test source",
|
||||||
connection: &sourcespb.GitHub{
|
connection: &sourcespb.GitHub{
|
||||||
@@ -476,35 +458,35 @@ func TestSource_paginateGists(t *testing.T) {
|
|||||||
SourceMetadata: &source_metadatapb.MetaData{
|
SourceMetadata: &source_metadatapb.MetaData{
|
||||||
Data: &source_metadatapb.MetaData_Github{
|
Data: &source_metadatapb.MetaData_Github{
|
||||||
Github: &source_metadatapb.Github{
|
Github: &source_metadatapb.Github{
|
||||||
Repository: "https://gist.github.com/be45ad1ebabe98482d9c0bb80c07c619.git",
|
Repository: "https://gist.github.com/fecf272c606ddbc5f8486f9c44821312.git",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Verify: false,
|
Verify: false,
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
user: "dustin-decker",
|
user: "truffle-sandbox",
|
||||||
minRepos: 1,
|
minRepos: 1,
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
name: "get multiple pages of gists",
|
// name: "get multiple pages of gists",
|
||||||
init: init{
|
// init: init{
|
||||||
name: "test source",
|
// name: "test source",
|
||||||
connection: &sourcespb.GitHub{
|
// connection: &sourcespb.GitHub{
|
||||||
Credential: &sourcespb.GitHub_GithubApp{
|
// Credential: &sourcespb.GitHub_GithubApp{
|
||||||
GithubApp: &credentialspb.GitHubApp{
|
// GithubApp: &credentialspb.GitHubApp{
|
||||||
PrivateKey: githubPrivateKeyNew,
|
// PrivateKey: githubPrivateKeyNew,
|
||||||
InstallationId: githubInstallationIDNew,
|
// InstallationId: githubInstallationIDNew,
|
||||||
AppId: githubAppIDNew,
|
// AppId: githubAppIDNew,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
wantChunk: nil,
|
// wantChunk: nil,
|
||||||
wantErr: false,
|
// wantErr: false,
|
||||||
user: "andrew",
|
// user: "andrew",
|
||||||
minRepos: 101,
|
// minRepos: 101,
|
||||||
},
|
// },
|
||||||
/* {
|
/* {
|
||||||
name: "get multiple pages of gists",
|
name: "get multiple pages of gists",
|
||||||
init: init{
|
init: init{
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ func TestAddMembersByApp(t *testing.T) {
|
|||||||
Get("/app/installations").
|
Get("/app/installations").
|
||||||
Reply(200).
|
Reply(200).
|
||||||
JSON([]map[string]interface{}{
|
JSON([]map[string]interface{}{
|
||||||
{"account": map[string]string{"login": "super-secret-org"}},
|
{"account": map[string]string{"login": "super-secret-org", "type": "Organization"}},
|
||||||
})
|
})
|
||||||
gock.New("https://api.github.com").
|
gock.New("https://api.github.com").
|
||||||
Get("/orgs/super-secret-org/members").
|
Get("/orgs/super-secret-org/members").
|
||||||
@@ -197,14 +197,13 @@ func TestAddOrgsByUser(t *testing.T) {
|
|||||||
Get("/user/orgs").
|
Get("/user/orgs").
|
||||||
Reply(200).
|
Reply(200).
|
||||||
JSON([]map[string]interface{}{
|
JSON([]map[string]interface{}{
|
||||||
{"name": "sso1"},
|
|
||||||
{"login": "sso2"},
|
{"login": "sso2"},
|
||||||
})
|
})
|
||||||
|
|
||||||
s := initTestSource(nil)
|
s := initTestSource(nil)
|
||||||
s.addOrgsByUser(context.TODO(), "super-secret-user")
|
s.addOrgsByUser(context.TODO(), "super-secret-user")
|
||||||
assert.Equal(t, 2, len(s.orgs))
|
assert.Equal(t, 1, len(s.orgs))
|
||||||
assert.Equal(t, []string{"sso1", "sso2"}, s.orgs)
|
assert.Equal(t, []string{"sso2"}, s.orgs)
|
||||||
assert.True(t, gock.IsDone())
|
assert.True(t, gock.IsDone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func HandleTestChannel(chunksCh chan *Chunk, cf ChunkFunc) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
case <-time.After(10 * time.Second):
|
case <-time.After(10 * time.Second):
|
||||||
return fmt.Errorf("no new chunks recieved after 10 seconds")
|
return fmt.Errorf("no new chunks received after 10 seconds")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user