Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api,ui): allow to disable vcs management on project #6408

Merged
merged 1 commit into from
Jan 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions engine/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,9 @@ type Configuration struct {
TemplateBulkRunnerCount int64 `toml:"templateBulkRunnerCount" comment:"The count of runner that will execute the workflow template bulk operation." json:"templateBulkRunnerCount" default:"10"`
} `toml:"workflow" comment:"######################\n 'Workflow' global configuration \n######################" json:"workflow"`
Project struct {
CreationDisabled bool `toml:"creationDisabled" comment:"Disable project creation for CDS non admin users." json:"creationDisabled" default:"false" commented:"true"`
InfoCreationDisabled string `toml:"infoCreationDisabled" comment:"Optional message to display if project creation is disabled." json:"infoCreationDisabled" default:"" commented:"true"`
CreationDisabled bool `toml:"creationDisabled" comment:"Disable project creation for CDS non admin users." json:"creationDisabled" default:"false" commented:"true"`
InfoCreationDisabled string `toml:"infoCreationDisabled" comment:"Optional message to display if project creation is disabled." json:"infoCreationDisabled" default:"" commented:"true"`
VCSManagementDisabled bool `toml:"vcsManagementDisabled" comment:"Disable VCS management on project for CDS non admin users." json:"vcsManagementDisabled" default:"false" commented:"true"`
} `toml:"project" comment:"######################\n 'Project' global configuration \n######################" json:"project"`
EventBus event.Config `toml:"events" comment:"######################\n Event bus configuration \n######################" json:"events" mapstructure:"events"`
}
Expand Down
7 changes: 4 additions & 3 deletions engine/api/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,10 @@ func (api *API) configCDNHandler() service.Handler {
func (api *API) configAPIHandler() service.Handler {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
return service.WriteJSON(w, sdk.APIConfig{
DefaultRunRetentionPolicy: api.Config.Workflow.DefaultRetentionPolicy,
ProjectCreationDisabled: api.Config.Project.CreationDisabled,
ProjectInfoCreationDisabled: api.Config.Project.InfoCreationDisabled,
DefaultRunRetentionPolicy: api.Config.Workflow.DefaultRetentionPolicy,
ProjectCreationDisabled: api.Config.Project.CreationDisabled,
ProjectInfoCreationDisabled: api.Config.Project.InfoCreationDisabled,
ProjectVCSManagementDisabled: api.Config.Project.VCSManagementDisabled,
}, http.StatusOK)
}
}
16 changes: 16 additions & 0 deletions engine/api/repositories_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ func (api *API) repositoriesManagerAuthorizeHandler() service.Handler {
key := vars[permProjectKey]
rmName := vars["name"]

if api.Config.Project.VCSManagementDisabled && !isAdmin(ctx) {
return sdk.NewErrorFrom(sdk.ErrForbidden, "vcs management is disabled")
}

proj, err := project.Load(ctx, api.mustDB(), key)
if err != nil {
return sdk.WrapError(err, "cannot load project")
Expand Down Expand Up @@ -206,6 +210,10 @@ func (api *API) repositoriesManagerAuthorizeBasicHandler() service.Handler {
projectKey := vars["permProjectKey"]
rmName := vars["name"]

if api.Config.Project.VCSManagementDisabled && !isAdmin(ctx) {
return sdk.NewErrorFrom(sdk.ErrForbidden, "vcs management is disabled")
}

var tv map[string]interface{}
if err := service.UnmarshalBody(r, &tv); err != nil {
return err
Expand Down Expand Up @@ -272,6 +280,10 @@ func (api *API) repositoriesManagerAuthorizeCallbackHandler() service.Handler {
projectKey := vars[permProjectKey]
rmName := vars["name"]

if api.Config.Project.VCSManagementDisabled && !isAdmin(ctx) {
return sdk.NewErrorFrom(sdk.ErrForbidden, "vcs management is disabled")
}

var tv map[string]interface{}
if err := service.UnmarshalBody(r, &tv); err != nil {
return err
Expand Down Expand Up @@ -346,6 +358,10 @@ func (api *API) deleteRepositoriesManagerHandler() service.Handler {
projectKey := vars[permProjectKey]
rmName := vars["name"]

if api.Config.Project.VCSManagementDisabled && !isAdmin(ctx) {
return sdk.NewErrorFrom(sdk.ErrForbidden, "vcs management is disabled")
}

force := service.FormBool(r, "force")

p, err := project.Load(ctx, api.mustDB(), projectKey)
Expand Down
7 changes: 4 additions & 3 deletions sdk/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ type ConfigUser struct {
}

type APIConfig struct {
DefaultRunRetentionPolicy string `json:"default_run_retention_policy"`
ProjectCreationDisabled bool `json:"project_creation_disabled"`
ProjectInfoCreationDisabled string `json:"project_info_creation_disabled,omitempty"`
DefaultRunRetentionPolicy string `json:"default_run_retention_policy"`
ProjectCreationDisabled bool `json:"project_creation_disabled"`
ProjectInfoCreationDisabled string `json:"project_info_creation_disabled,omitempty"`
ProjectVCSManagementDisabled bool `json:"project_vcs_management_disabled,omitempty"`
}

type TCPServer struct {
Expand Down
1 change: 1 addition & 0 deletions ui/src/app/model/config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export class APIConfig {
default_run_retention_policy: string;
project_creation_disabled: boolean;
project_info_creation_disabled: string;
project_vcs_management_disabled: boolean;
}
19 changes: 15 additions & 4 deletions ui/src/app/views/project/show/admin/project.admin.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit }
import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { Store } from '@ngxs/store';
import { APIConfig } from 'app/model/config.service';
import { Project } from 'app/model/project.model';
import { AutoUnsubscribe } from 'app/shared/decorator/autoUnsubscribe';
import { ToastService } from 'app/shared/toast/ToastService';
import { ConfigState } from 'app/store/config.state';
import { DeleteProject, UpdateProject } from 'app/store/project.action';
import { Subscription } from 'rxjs';
import { finalize } from 'rxjs/operators';

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

@Input() project: Project;

loading = false;
fileTooLarge = false;
configSubscription: Subscription;
apiConfig: APIConfig;

constructor(
private _toast: ToastService,
public _translate: TranslateService,
private _router: Router,
private _store: Store,
private _cd: ChangeDetectorRef
) {
}
) { }

ngOnInit(): void {
if (!this.project.permissions.writable) {
this._router.navigate(['/project', this.project.key], {queryParams: {tab: 'applications'}});
this._router.navigate(['/project', this.project.key], { queryParams: { tab: 'applications' } });
}

this.configSubscription = this._store.select(ConfigState.api).subscribe(c => {
this.apiConfig = c;
this._cd.markForCheck();
});
}

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

deleteProject(): void {
this.loading = true;
this._store.dispatch(new DeleteProject({projectKey: this.project.key}))
this._store.dispatch(new DeleteProject({ projectKey: this.project.key }))
.pipe(finalize(() => {
this.loading = false;
this._cd.markForCheck();
Expand Down
40 changes: 18 additions & 22 deletions ui/src/app/views/project/show/admin/project.admin.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,17 @@
<nz-form-item>
<nz-form-label [nzSpan]="3">Project name</nz-form-label>
<nz-form-control>
<input nz-input type="text" name="formProjectUpdateName"
placeholder="Project name"
[(ngModel)]="project.name"
[disabled]="loading"
required
#formProjectUpdateName="ngModel">
<input nz-input type="text" name="formProjectUpdateName" placeholder="Project name"
[(ngModel)]="project.name" [disabled]="loading" required #formProjectUpdateName="ngModel">
</nz-form-control>
<nz-alert *ngIf="formProjectUpdateName.invalid && !formProjectUpdateName.pristine"
nzType="error" nzMessage="Project name is mandatory and must respect the alphanumeric pattern ([a-zA-Z0-9]*)"></nz-alert>
<nz-alert *ngIf="formProjectUpdateName.invalid && !formProjectUpdateName.pristine" nzType="error"
nzMessage="Project name is mandatory and must respect the alphanumeric pattern ([a-zA-Z0-9]*)"></nz-alert>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSpan]="3">Description</nz-form-label>
<nz-form-control>
<textarea nz-input name="formProjectUpdateDescription"
placeholder="Description"
[(ngModel)]="project.description"
[disabled]="loading"
#formProjectUpdateDescription="ngModel">
<textarea nz-input name="formProjectUpdateDescription" placeholder="Description"
[(ngModel)]="project.description" [disabled]="loading" #formProjectUpdateDescription="ngModel">
</textarea>
</nz-form-control>
</nz-form-item>
Expand All @@ -31,20 +24,23 @@
<div>
<img class="proj-icon" [src]="project.icon" alt="project icon" *ngIf="project.icon">
</div>
<app-upload-button accept=".png,.jpg,.jpeg" image="true" (event)="fileEvent($event)"></app-upload-button>
<nz-alert *ngIf="fileTooLarge" nzType="error" nzMessage="Your file is too large (max 100Ko)"></nz-alert>
<app-upload-button accept=".png,.jpg,.jpeg" image="true"
(event)="fileEvent($event)"></app-upload-button>
<nz-alert *ngIf="fileTooLarge" nzType="error"
nzMessage="Your file is too large (max 100Ko)"></nz-alert>
</nz-form-control>
</nz-form-item>
<nz-form-item nzJustify="end">
<button nz-button nzType="primary" name="btnrename" [nzLoading]="loading" [disabled]="projectUpdateFrom.invalid">Rename</button>
<button nz-button nzType="primary" name="btnrename" [nzLoading]="loading"
[disabled]="projectUpdateFrom.invalid">Rename</button>
</nz-form-item>
</form>
</nz-card>
<nz-card nzTitle="Link to a repository manager" class="coloredTitle">
<app-repomanager-form [project]="project"></app-repomanager-form>
<app-project-repomanager-list *ngIf="project.vcs_servers && project.vcs_servers.length > 0"
[project]="project" [reposmanagers]="project.vcs_servers"
></app-project-repomanager-list>
<app-repomanager-form *ngIf="apiConfig && !apiConfig.project_vcs_management_disabled"
[project]="project"></app-repomanager-form>
<app-project-repomanager-list *ngIf="project.vcs_servers && project.vcs_servers.length > 0" [project]="project"
[reposmanagers]="project.vcs_servers"></app-project-repomanager-list>
</nz-card>
<nz-card nzTitle="Danger zone" class="redTitle">
<nz-row>
Expand All @@ -53,8 +49,8 @@
<div class="description">Once you delete a project, there is no going back. Please be certain.</div>
</nz-col>
<nz-col [nzSpan]="12" class="alignRight">
<button nz-button nzDanger nzType="primary" [nzLoading]="loading"
nz-popconfirm nzPopconfirmTitle="Are you sure you want to delete this project ?"
<button nz-button nzDanger nzType="primary" [nzLoading]="loading" nz-popconfirm
nzPopconfirmTitle="Are you sure you want to delete this project ?"
(nzOnConfirm)="deleteProject()">Delete</button>
</nz-col>
</nz-row>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { Store } from '@ngxs/store';
import { APIConfig } from 'app/model/config.service';
import { Project } from 'app/model/project.model';
import { RepositoriesManager } from 'app/model/repositories.model';
import { RepoManagerService } from 'app/service/repomanager/project.repomanager.service';
import { AutoUnsubscribe } from 'app/shared/decorator/autoUnsubscribe';
import { ToastService } from 'app/shared/toast/ToastService';
import { ConfigState } from 'app/store/config.state';
import { DisconnectRepositoryManagerInProject } from 'app/store/project.action';
import { Subscription } from 'rxjs';
import { finalize } from 'rxjs/operators';

@Component({
Expand All @@ -14,7 +18,8 @@ import { finalize } from 'rxjs/operators';
styleUrls: ['./project.repomanager.list.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProjectRepoManagerComponent {
@AutoUnsubscribe()
export class ProjectRepoManagerComponent implements OnInit {

@Input() project: Project;
@Input() reposmanagers: RepositoriesManager[];
Expand All @@ -24,14 +29,22 @@ export class ProjectRepoManagerComponent {
repoNameToDelete: string;
confirmationMessage: string;
deleteModal: boolean;
apiConfig: APIConfig;
configSubscription: Subscription;

constructor(
private _toast: ToastService,
public _translate: TranslateService,
private repoManagerService: RepoManagerService,
private store: Store,
private _store: Store,
private _cd: ChangeDetectorRef
) {
) { }

ngOnInit(): void {
this.configSubscription = this._store.select(ConfigState.api).subscribe(c => {
this.apiConfig = c;
this._cd.markForCheck();
});
}

clickDeleteButton(repoName: string): void {
Expand Down Expand Up @@ -59,7 +72,7 @@ export class ProjectRepoManagerComponent {
return;
}
this.deleteLoading = true;
this.store.dispatch(new DisconnectRepositoryManagerInProject({
this._store.dispatch(new DisconnectRepositoryManagerInProject({
projectKey: this.project.key,
repoManager: this.repoNameToDelete
}))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
<div id="projectRepoManagers">
<nz-table [nzData]="reposmanagers" nzSize="small" #listrepos>
<thead>
<tr>
<th nzWidth="400px">Name</th>
<th nzWidth="50px"></th>
</tr>
<tr>
<th nzWidth="400px">Name</th>
<th nzWidth="50px"></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let r of listrepos.data">
<td>
{{r.name}}
<span *ngIf="r.created_by !== ''"> -{{ 'project_repoman_created_by' | translate }}{{r.created_by}}</span>
<span *ngIf="r.auth && r.auth.username && r.auth.username !== ''"> - User: {{r.auth.username}}</span>
<span *ngIf="r.auth && r.auth.sshUsername && r.auth.sshUsername !== ''"> - SSH User: {{r.auth.sshUsername}}</span>
<span *ngIf="r.auth && r.auth.sshKeyName && r.auth.sshKeyName !== ''"> - SSH Key: {{r.auth.sshKeyName}}</span>
</td>
<td class="rightAlign">
<button nz-button nzDanger nzType="primary" [nzLoading]="deleteLoading" *ngIf="r.type === ''" (click)="clickDeleteButton(r.name)">Remove</button>
</td>
</tr>
<tr *ngFor="let r of listrepos.data">
<td>
{{r.name}}
<span *ngIf="r.created_by !== ''"> -{{ 'project_repoman_created_by' | translate
}}{{r.created_by}}</span>
<span *ngIf="r.auth && r.auth.username && r.auth.username !== ''"> - User:
{{r.auth.username}}</span>
<span *ngIf="r.auth && r.auth.sshUsername && r.auth.sshUsername !== ''"> - SSH User:
{{r.auth.sshUsername}}</span>
<span *ngIf="r.auth && r.auth.sshKeyName && r.auth.sshKeyName !== ''"> - SSH Key:
{{r.auth.sshKeyName}}</span>
</td>
<td class="rightAlign">
<button *ngIf="r.type === '' && apiConfig && !apiConfig.project_vcs_management_disabled" nz-button
nzDanger nzType="primary" [nzLoading]="deleteLoading"
(click)="clickDeleteButton(r.name)">Remove</button>
</td>
</tr>
</tbody>
</nz-table>
</div>
Expand Down