Skip to content

Commit 0a427bf

Browse files
authored
feat(api,ui): allow to disable vcs management on project (#6408)
1 parent 7470a39 commit 0a427bf

File tree

9 files changed

+101
-55
lines changed

9 files changed

+101
-55
lines changed

engine/api/api.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,9 @@ type Configuration struct {
221221
TemplateBulkRunnerCount int64 `toml:"templateBulkRunnerCount" comment:"The count of runner that will execute the workflow template bulk operation." json:"templateBulkRunnerCount" default:"10"`
222222
} `toml:"workflow" comment:"######################\n 'Workflow' global configuration \n######################" json:"workflow"`
223223
Project struct {
224-
CreationDisabled bool `toml:"creationDisabled" comment:"Disable project creation for CDS non admin users." json:"creationDisabled" default:"false" commented:"true"`
225-
InfoCreationDisabled string `toml:"infoCreationDisabled" comment:"Optional message to display if project creation is disabled." json:"infoCreationDisabled" default:"" commented:"true"`
224+
CreationDisabled bool `toml:"creationDisabled" comment:"Disable project creation for CDS non admin users." json:"creationDisabled" default:"false" commented:"true"`
225+
InfoCreationDisabled string `toml:"infoCreationDisabled" comment:"Optional message to display if project creation is disabled." json:"infoCreationDisabled" default:"" commented:"true"`
226+
VCSManagementDisabled bool `toml:"vcsManagementDisabled" comment:"Disable VCS management on project for CDS non admin users." json:"vcsManagementDisabled" default:"false" commented:"true"`
226227
} `toml:"project" comment:"######################\n 'Project' global configuration \n######################" json:"project"`
227228
EventBus event.Config `toml:"events" comment:"######################\n Event bus configuration \n######################" json:"events" mapstructure:"events"`
228229
}

engine/api/config.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,10 @@ func (api *API) configCDNHandler() service.Handler {
9191
func (api *API) configAPIHandler() service.Handler {
9292
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
9393
return service.WriteJSON(w, sdk.APIConfig{
94-
DefaultRunRetentionPolicy: api.Config.Workflow.DefaultRetentionPolicy,
95-
ProjectCreationDisabled: api.Config.Project.CreationDisabled,
96-
ProjectInfoCreationDisabled: api.Config.Project.InfoCreationDisabled,
94+
DefaultRunRetentionPolicy: api.Config.Workflow.DefaultRetentionPolicy,
95+
ProjectCreationDisabled: api.Config.Project.CreationDisabled,
96+
ProjectInfoCreationDisabled: api.Config.Project.InfoCreationDisabled,
97+
ProjectVCSManagementDisabled: api.Config.Project.VCSManagementDisabled,
9798
}, http.StatusOK)
9899
}
99100
}

engine/api/repositories_manager.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ func (api *API) repositoriesManagerAuthorizeHandler() service.Handler {
5555
key := vars[permProjectKey]
5656
rmName := vars["name"]
5757

58+
if api.Config.Project.VCSManagementDisabled && !isAdmin(ctx) {
59+
return sdk.NewErrorFrom(sdk.ErrForbidden, "vcs management is disabled")
60+
}
61+
5862
proj, err := project.Load(ctx, api.mustDB(), key)
5963
if err != nil {
6064
return sdk.WrapError(err, "cannot load project")
@@ -206,6 +210,10 @@ func (api *API) repositoriesManagerAuthorizeBasicHandler() service.Handler {
206210
projectKey := vars["permProjectKey"]
207211
rmName := vars["name"]
208212

213+
if api.Config.Project.VCSManagementDisabled && !isAdmin(ctx) {
214+
return sdk.NewErrorFrom(sdk.ErrForbidden, "vcs management is disabled")
215+
}
216+
209217
var tv map[string]interface{}
210218
if err := service.UnmarshalBody(r, &tv); err != nil {
211219
return err
@@ -272,6 +280,10 @@ func (api *API) repositoriesManagerAuthorizeCallbackHandler() service.Handler {
272280
projectKey := vars[permProjectKey]
273281
rmName := vars["name"]
274282

283+
if api.Config.Project.VCSManagementDisabled && !isAdmin(ctx) {
284+
return sdk.NewErrorFrom(sdk.ErrForbidden, "vcs management is disabled")
285+
}
286+
275287
var tv map[string]interface{}
276288
if err := service.UnmarshalBody(r, &tv); err != nil {
277289
return err
@@ -346,6 +358,10 @@ func (api *API) deleteRepositoriesManagerHandler() service.Handler {
346358
projectKey := vars[permProjectKey]
347359
rmName := vars["name"]
348360

361+
if api.Config.Project.VCSManagementDisabled && !isAdmin(ctx) {
362+
return sdk.NewErrorFrom(sdk.ErrForbidden, "vcs management is disabled")
363+
}
364+
349365
force := service.FormBool(r, "force")
350366

351367
p, err := project.Load(ctx, api.mustDB(), projectKey)

sdk/config.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ type ConfigUser struct {
1212
}
1313

1414
type APIConfig struct {
15-
DefaultRunRetentionPolicy string `json:"default_run_retention_policy"`
16-
ProjectCreationDisabled bool `json:"project_creation_disabled"`
17-
ProjectInfoCreationDisabled string `json:"project_info_creation_disabled,omitempty"`
15+
DefaultRunRetentionPolicy string `json:"default_run_retention_policy"`
16+
ProjectCreationDisabled bool `json:"project_creation_disabled"`
17+
ProjectInfoCreationDisabled string `json:"project_info_creation_disabled,omitempty"`
18+
ProjectVCSManagementDisabled bool `json:"project_vcs_management_disabled,omitempty"`
1819
}
1920

2021
type TCPServer struct {

ui/src/app/model/config.service.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ export class APIConfig {
22
default_run_retention_policy: string;
33
project_creation_disabled: boolean;
44
project_info_creation_disabled: string;
5+
project_vcs_management_disabled: boolean;
56
}

ui/src/app/views/project/show/admin/project.admin.component.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit }
22
import { Router } from '@angular/router';
33
import { TranslateService } from '@ngx-translate/core';
44
import { Store } from '@ngxs/store';
5+
import { APIConfig } from 'app/model/config.service';
56
import { Project } from 'app/model/project.model';
7+
import { AutoUnsubscribe } from 'app/shared/decorator/autoUnsubscribe';
68
import { ToastService } from 'app/shared/toast/ToastService';
9+
import { ConfigState } from 'app/store/config.state';
710
import { DeleteProject, UpdateProject } from 'app/store/project.action';
11+
import { Subscription } from 'rxjs';
812
import { finalize } from 'rxjs/operators';
913

1014
@Component({
@@ -13,26 +17,33 @@ import { finalize } from 'rxjs/operators';
1317
styleUrls: ['./project.admin.scss'],
1418
changeDetection: ChangeDetectionStrategy.OnPush
1519
})
20+
@AutoUnsubscribe()
1621
export class ProjectAdminComponent implements OnInit {
1722

1823
@Input() project: Project;
1924

2025
loading = false;
2126
fileTooLarge = false;
27+
configSubscription: Subscription;
28+
apiConfig: APIConfig;
2229

2330
constructor(
2431
private _toast: ToastService,
2532
public _translate: TranslateService,
2633
private _router: Router,
2734
private _store: Store,
2835
private _cd: ChangeDetectorRef
29-
) {
30-
}
36+
) { }
3137

3238
ngOnInit(): void {
3339
if (!this.project.permissions.writable) {
34-
this._router.navigate(['/project', this.project.key], {queryParams: {tab: 'applications'}});
40+
this._router.navigate(['/project', this.project.key], { queryParams: { tab: 'applications' } });
3541
}
42+
43+
this.configSubscription = this._store.select(ConfigState.api).subscribe(c => {
44+
this.apiConfig = c;
45+
this._cd.markForCheck();
46+
});
3647
}
3748

3849
onSubmitProjectUpdate() {
@@ -47,7 +58,7 @@ export class ProjectAdminComponent implements OnInit {
4758

4859
deleteProject(): void {
4960
this.loading = true;
50-
this._store.dispatch(new DeleteProject({projectKey: this.project.key}))
61+
this._store.dispatch(new DeleteProject({ projectKey: this.project.key }))
5162
.pipe(finalize(() => {
5263
this.loading = false;
5364
this._cd.markForCheck();

ui/src/app/views/project/show/admin/project.admin.html

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,17 @@
44
<nz-form-item>
55
<nz-form-label [nzSpan]="3">Project name</nz-form-label>
66
<nz-form-control>
7-
<input nz-input type="text" name="formProjectUpdateName"
8-
placeholder="Project name"
9-
[(ngModel)]="project.name"
10-
[disabled]="loading"
11-
required
12-
#formProjectUpdateName="ngModel">
7+
<input nz-input type="text" name="formProjectUpdateName" placeholder="Project name"
8+
[(ngModel)]="project.name" [disabled]="loading" required #formProjectUpdateName="ngModel">
139
</nz-form-control>
14-
<nz-alert *ngIf="formProjectUpdateName.invalid && !formProjectUpdateName.pristine"
15-
nzType="error" nzMessage="Project name is mandatory and must respect the alphanumeric pattern ([a-zA-Z0-9]*)"></nz-alert>
10+
<nz-alert *ngIf="formProjectUpdateName.invalid && !formProjectUpdateName.pristine" nzType="error"
11+
nzMessage="Project name is mandatory and must respect the alphanumeric pattern ([a-zA-Z0-9]*)"></nz-alert>
1612
</nz-form-item>
1713
<nz-form-item>
1814
<nz-form-label [nzSpan]="3">Description</nz-form-label>
1915
<nz-form-control>
20-
<textarea nz-input name="formProjectUpdateDescription"
21-
placeholder="Description"
22-
[(ngModel)]="project.description"
23-
[disabled]="loading"
24-
#formProjectUpdateDescription="ngModel">
16+
<textarea nz-input name="formProjectUpdateDescription" placeholder="Description"
17+
[(ngModel)]="project.description" [disabled]="loading" #formProjectUpdateDescription="ngModel">
2518
</textarea>
2619
</nz-form-control>
2720
</nz-form-item>
@@ -31,20 +24,23 @@
3124
<div>
3225
<img class="proj-icon" [src]="project.icon" alt="project icon" *ngIf="project.icon">
3326
</div>
34-
<app-upload-button accept=".png,.jpg,.jpeg" image="true" (event)="fileEvent($event)"></app-upload-button>
35-
<nz-alert *ngIf="fileTooLarge" nzType="error" nzMessage="Your file is too large (max 100Ko)"></nz-alert>
27+
<app-upload-button accept=".png,.jpg,.jpeg" image="true"
28+
(event)="fileEvent($event)"></app-upload-button>
29+
<nz-alert *ngIf="fileTooLarge" nzType="error"
30+
nzMessage="Your file is too large (max 100Ko)"></nz-alert>
3631
</nz-form-control>
3732
</nz-form-item>
3833
<nz-form-item nzJustify="end">
39-
<button nz-button nzType="primary" name="btnrename" [nzLoading]="loading" [disabled]="projectUpdateFrom.invalid">Rename</button>
34+
<button nz-button nzType="primary" name="btnrename" [nzLoading]="loading"
35+
[disabled]="projectUpdateFrom.invalid">Rename</button>
4036
</nz-form-item>
4137
</form>
4238
</nz-card>
4339
<nz-card nzTitle="Link to a repository manager" class="coloredTitle">
44-
<app-repomanager-form [project]="project"></app-repomanager-form>
45-
<app-project-repomanager-list *ngIf="project.vcs_servers && project.vcs_servers.length > 0"
46-
[project]="project" [reposmanagers]="project.vcs_servers"
47-
></app-project-repomanager-list>
40+
<app-repomanager-form *ngIf="apiConfig && !apiConfig.project_vcs_management_disabled"
41+
[project]="project"></app-repomanager-form>
42+
<app-project-repomanager-list *ngIf="project.vcs_servers && project.vcs_servers.length > 0" [project]="project"
43+
[reposmanagers]="project.vcs_servers"></app-project-repomanager-list>
4844
</nz-card>
4945
<nz-card nzTitle="Danger zone" class="redTitle">
5046
<nz-row>
@@ -53,8 +49,8 @@
5349
<div class="description">Once you delete a project, there is no going back. Please be certain.</div>
5450
</nz-col>
5551
<nz-col [nzSpan]="12" class="alignRight">
56-
<button nz-button nzDanger nzType="primary" [nzLoading]="loading"
57-
nz-popconfirm nzPopconfirmTitle="Are you sure you want to delete this project ?"
52+
<button nz-button nzDanger nzType="primary" [nzLoading]="loading" nz-popconfirm
53+
nzPopconfirmTitle="Are you sure you want to delete this project ?"
5854
(nzOnConfirm)="deleteProject()">Delete</button>
5955
</nz-col>
6056
</nz-row>

ui/src/app/views/project/show/admin/repomanager/list/project.repomanager.list.component.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input } from '@angular/core';
1+
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
22
import { TranslateService } from '@ngx-translate/core';
33
import { Store } from '@ngxs/store';
4+
import { APIConfig } from 'app/model/config.service';
45
import { Project } from 'app/model/project.model';
56
import { RepositoriesManager } from 'app/model/repositories.model';
67
import { RepoManagerService } from 'app/service/repomanager/project.repomanager.service';
8+
import { AutoUnsubscribe } from 'app/shared/decorator/autoUnsubscribe';
79
import { ToastService } from 'app/shared/toast/ToastService';
10+
import { ConfigState } from 'app/store/config.state';
811
import { DisconnectRepositoryManagerInProject } from 'app/store/project.action';
12+
import { Subscription } from 'rxjs';
913
import { finalize } from 'rxjs/operators';
1014

1115
@Component({
@@ -14,7 +18,8 @@ import { finalize } from 'rxjs/operators';
1418
styleUrls: ['./project.repomanager.list.scss'],
1519
changeDetection: ChangeDetectionStrategy.OnPush
1620
})
17-
export class ProjectRepoManagerComponent {
21+
@AutoUnsubscribe()
22+
export class ProjectRepoManagerComponent implements OnInit {
1823

1924
@Input() project: Project;
2025
@Input() reposmanagers: RepositoriesManager[];
@@ -24,14 +29,22 @@ export class ProjectRepoManagerComponent {
2429
repoNameToDelete: string;
2530
confirmationMessage: string;
2631
deleteModal: boolean;
32+
apiConfig: APIConfig;
33+
configSubscription: Subscription;
2734

2835
constructor(
2936
private _toast: ToastService,
3037
public _translate: TranslateService,
3138
private repoManagerService: RepoManagerService,
32-
private store: Store,
39+
private _store: Store,
3340
private _cd: ChangeDetectorRef
34-
) {
41+
) { }
42+
43+
ngOnInit(): void {
44+
this.configSubscription = this._store.select(ConfigState.api).subscribe(c => {
45+
this.apiConfig = c;
46+
this._cd.markForCheck();
47+
});
3548
}
3649

3750
clickDeleteButton(repoName: string): void {
@@ -59,7 +72,7 @@ export class ProjectRepoManagerComponent {
5972
return;
6073
}
6174
this.deleteLoading = true;
62-
this.store.dispatch(new DisconnectRepositoryManagerInProject({
75+
this._store.dispatch(new DisconnectRepositoryManagerInProject({
6376
projectKey: this.project.key,
6477
repoManager: this.repoNameToDelete
6578
}))

ui/src/app/views/project/show/admin/repomanager/list/project.repomanager.list.html

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
11
<div id="projectRepoManagers">
22
<nz-table [nzData]="reposmanagers" nzSize="small" #listrepos>
33
<thead>
4-
<tr>
5-
<th nzWidth="400px">Name</th>
6-
<th nzWidth="50px"></th>
7-
</tr>
4+
<tr>
5+
<th nzWidth="400px">Name</th>
6+
<th nzWidth="50px"></th>
7+
</tr>
88
</thead>
99
<tbody>
10-
<tr *ngFor="let r of listrepos.data">
11-
<td>
12-
{{r.name}}
13-
<span *ngIf="r.created_by !== ''"> -{{ 'project_repoman_created_by' | translate }}{{r.created_by}}</span>
14-
<span *ngIf="r.auth && r.auth.username && r.auth.username !== ''"> - User: {{r.auth.username}}</span>
15-
<span *ngIf="r.auth && r.auth.sshUsername && r.auth.sshUsername !== ''"> - SSH User: {{r.auth.sshUsername}}</span>
16-
<span *ngIf="r.auth && r.auth.sshKeyName && r.auth.sshKeyName !== ''"> - SSH Key: {{r.auth.sshKeyName}}</span>
17-
</td>
18-
<td class="rightAlign">
19-
<button nz-button nzDanger nzType="primary" [nzLoading]="deleteLoading" *ngIf="r.type === ''" (click)="clickDeleteButton(r.name)">Remove</button>
20-
</td>
21-
</tr>
10+
<tr *ngFor="let r of listrepos.data">
11+
<td>
12+
{{r.name}}
13+
<span *ngIf="r.created_by !== ''"> -{{ 'project_repoman_created_by' | translate
14+
}}{{r.created_by}}</span>
15+
<span *ngIf="r.auth && r.auth.username && r.auth.username !== ''"> - User:
16+
{{r.auth.username}}</span>
17+
<span *ngIf="r.auth && r.auth.sshUsername && r.auth.sshUsername !== ''"> - SSH User:
18+
{{r.auth.sshUsername}}</span>
19+
<span *ngIf="r.auth && r.auth.sshKeyName && r.auth.sshKeyName !== ''"> - SSH Key:
20+
{{r.auth.sshKeyName}}</span>
21+
</td>
22+
<td class="rightAlign">
23+
<button *ngIf="r.type === '' && apiConfig && !apiConfig.project_vcs_management_disabled" nz-button
24+
nzDanger nzType="primary" [nzLoading]="deleteLoading"
25+
(click)="clickDeleteButton(r.name)">Remove</button>
26+
</td>
27+
</tr>
2228
</tbody>
2329
</nz-table>
2430
</div>

0 commit comments

Comments
 (0)