Fix Trigger Setup Robot Permissions for Private Base (#2543)

This commit is contained in:
Alec Merdler 2017-04-14 12:10:00 -07:00 committed by GitHub
parent 2f757faa40
commit 581d7c67a7
5 changed files with 210 additions and 91 deletions

View file

@ -356,7 +356,10 @@
total-count="$ctrl.local.orderedRobotAccounts.entries.length"
current-page="$ctrl.local.robotOptions.page"
page-size="$ctrl.robotsPerPage"></span>
<input class="form-control" type="text" ng-model="$ctrl.local.robotOptions.filter" placeholder="Filter robot accounts...">
<input class="form-control"
type="text"
ng-model="$ctrl.local.robotOptions.filter"
placeholder="Filter robot accounts...">
</div>
</div>

View file

@ -1,27 +1,30 @@
import { ManageTriggerGithostComponent, Local, Trigger } from './manage-trigger-githost.component';
import { ManageTriggerGithostComponent } from './manage-trigger-githost.component';
import { Local, Trigger, Repository } from '../../../types/common.types';
import { Mock } from 'ts-mocks';
import Spy = jasmine.Spy;
describe("ManageTriggerGithostComponent", () => {
var component: ManageTriggerGithostComponent;
var apiServiceMock: any;
var tableServiceMock: any;
var triggerServiceMock: any;
var rolesServiceMock: any;
var apiServiceMock: Mock<any>;
var tableServiceMock: Mock<any>;
var triggerServiceMock: Mock<any>;
var rolesServiceMock: Mock<any>;
var repository: any;
var trigger: Trigger;
var $scope: ng.IScope;
var $scopeMock: Mock<ng.IScope>;
beforeEach(inject(($injector: ng.auto.IInjectorService) => {
apiServiceMock = jasmine.createSpyObj('apiServiceMock', ['listTriggerBuildSourceNamespaces']);
tableServiceMock = jasmine.createSpyObj('tableServiceMock', ['buildOrderedItems']);
triggerServiceMock = jasmine.createSpyObj('triggerServiceMock', ['getIcon']);
rolesServiceMock = jasmine.createSpyObj('rolesServiceMock', ['setRepositoryRole']);
$scope = $injector.get('$rootScope');
component = new ManageTriggerGithostComponent(apiServiceMock,
tableServiceMock,
triggerServiceMock,
rolesServiceMock,
$scope);
apiServiceMock = new Mock<any>();
tableServiceMock = new Mock<any>();
triggerServiceMock = new Mock<any>();
rolesServiceMock = new Mock<any>();
$scopeMock = new Mock<ng.IScope>();
component = new ManageTriggerGithostComponent(apiServiceMock.Object,
tableServiceMock.Object,
triggerServiceMock.Object,
rolesServiceMock.Object,
$scopeMock.Object);
trigger = {service: "serviceMock", id: 1};
component.trigger = trigger;
}));
@ -36,14 +39,75 @@ describe("ManageTriggerGithostComponent", () => {
describe("getTriggerIcon", () => {
it("calls trigger service to get icon", () => {
component.getTriggerIcon();
beforeEach(() => {
triggerServiceMock.setup(mock => mock.getIcon).is((service: any) => null);
});
expect(triggerServiceMock.getIcon.calls.argsFor(0)[0]).toEqual(component.trigger.service);
it("calls trigger service to get icon", () => {
const icon: any = component.getTriggerIcon();
expect((<Spy>triggerServiceMock.Object.getIcon).calls.argsFor(0)[0]).toEqual(component.trigger.service);
});
});
describe("createTrigger", () => {
// TODO
beforeEach(() => {
component.local.selectedRepository = new Mock<Repository>().Object;
component.local.selectedRepository.full_name = "someorg/some-repository";
component.local.dockerfilePath = "/Dockerfile";
component.local.dockerContext = "/";
component.local.triggerOptions = {};
component.local.triggerAnalysis = {};
rolesServiceMock.setup(mock => mock.setRepositoryRole).is((repo, role, entityKind, entityName, callback) => {
callback();
});
});
it("calls roles service to grant read access to selected robot if robot is required and cannot read", (done) => {
component.local.triggerAnalysis = {status: 'requiresrobot', name: 'privatebase', namespace: 'someorg'};
component.local.robotAccount = {can_read: false, is_robot: true, kind: 'user', name: 'test-robot'};
component.activateTrigger.subscribe((event: {config: any, pull_robot: any}) => {
expect((<Spy>rolesServiceMock.Object.setRepositoryRole).calls.argsFor(0)[0]).toEqual({
name: component.local.triggerAnalysis.name,
namespace: component.local.triggerAnalysis.namespace,
});
expect((<Spy>rolesServiceMock.Object.setRepositoryRole).calls.argsFor(0)[1]).toEqual('read');
expect((<Spy>rolesServiceMock.Object.setRepositoryRole).calls.argsFor(0)[2]).toEqual('robot');
done();
});
component.createTrigger();
});
it("does not call roles service if robot is required but already has read access", (done) => {
component.local.robotAccount = {can_read: true, is_robot: true, kind: 'user', name: 'test-robot'};
component.activateTrigger.subscribe((event: {config: any, pull_robot: any}) => {
expect((<Spy>rolesServiceMock.Object.setRepositoryRole)).not.toHaveBeenCalled();
done();
});
component.createTrigger();
});
it("does not call roles service if robot is not required", (done) => {
component.activateTrigger.subscribe((event: {config: any, pull_robot: any}) => {
expect((<Spy>rolesServiceMock.Object.setRepositoryRole)).not.toHaveBeenCalled();
done();
});
component.createTrigger();
});
it("emits output event with config and pull robot", (done) => {
component.activateTrigger.subscribe((event: {config: any, pull_robot: any}) => {
expect(event.config.build_source).toEqual(component.local.selectedRepository.full_name);
expect(event.config.dockerfile_path).toEqual(component.local.dockerfilePath);
expect(event.config.context).toEqual(component.local.dockerContext);
done();
});
component.createTrigger();
});
});
});

View file

@ -1,5 +1,6 @@
import { Input, Output, Component, Inject, EventEmitter, OnInit } from 'ng-metadata/core';
import * as moment from 'moment';
import { Local, Trigger, Repository, Namespace } from '../../../types/common.types';
/**
@ -12,30 +13,14 @@ import * as moment from 'moment';
export class ManageTriggerGithostComponent implements OnInit {
// FIXME: Use one-way data binding
@Input('=') public repository: any;
@Input('=') public repository: Repository;
@Input('=') public trigger: Trigger;
@Output() public activateTrigger: EventEmitter<{config: any, pull_robot?: any}> = new EventEmitter();
private config: any;
private local: any = {
namespaceOptions: {
filter: '',
predicate: 'score',
reverse: false,
page: 0
},
repositoryOptions: {
filter: '',
predicate: 'score',
reverse: false,
page: 0,
hideStale: true
},
robotOptions: {
filter: '',
predicate: 'score',
reverse: false,
page: 0
}
public config: any;
public local: Local = {
namespaceOptions: {filter: '', predicate: 'score', reverse: false, page: 0},
repositoryOptions: {filter: '', predicate: 'score', reverse: false, page: 0, hideStale: true},
robotOptions: {filter: '', predicate: 'score', reverse: false, page: 0},
};
private currentState: any | null;
private namespacesPerPage: number = 10;
@ -128,23 +113,21 @@ export class ManageTriggerGithostComponent implements OnInit {
context: this.local.dockerContext
};
if (this.local.triggerOptions.hasBranchTagFilter &&
this.local.triggerOptions.branchTagFilter) {
config['branchtag_regex'] = this.local.triggerOptions.branchTagFilter;
if (this.local.triggerOptions['hasBranchTagFilter'] && this.local.triggerOptions['branchTagFilter']) {
config['branchtag_regex'] = this.local.triggerOptions['branchTagFilter'];
}
var activate = () => {
const activate = () => {
this.activateTrigger.emit({config: config, pull_robot: this.local.robotAccount});
};
if (this.local.robotAccount && this.local.triggerAnalysis.status == 'requiresrobot') {
if (this.local.triggerAnalysis.status == 'requiresrobot' && this.local.robotAccount) {
if (this.local.robotAccount.can_read) {
activate();
} else {
// Add read permission onto the base repository for the robot and then activate the
// trigger.
var robot_name = this.local.robotAccount.name;
this.RolesService.setRepositoryRole(this.repository, 'read', 'robot', robot_name, activate);
// Add read permission onto the base repository for the robot and then activate the trigger.
const baseRepo: any = {name: this.local.triggerAnalysis.name, namespace: this.local.triggerAnalysis.namespace};
this.RolesService.setRepositoryRole(baseRepo, 'read', 'robot', this.local.robotAccount.name, activate);
}
} else {
activate();
@ -156,7 +139,7 @@ export class ManageTriggerGithostComponent implements OnInit {
return;
}
var namespaces = this.local.namespaces || [];
var namespaces: Namespace[] = this.local.namespaces || [];
this.local.orderedNamespaces = this.TableService.buildOrderedItems(namespaces,
this.local.namespaceOptions,
['id'],
@ -305,9 +288,9 @@ export class ManageTriggerGithostComponent implements OnInit {
var robots = this.local.triggerAnalysis.robots;
this.local.orderedRobotAccounts = this.TableService.buildOrderedItems(robots,
this.local.robotOptions,
['name'],
[]);
this.local.robotOptions,
['name'],
[]);
}
private checkDockerfilePath(repository: any, path: string, context: string): void {
@ -342,38 +325,3 @@ export class ManageTriggerGithostComponent implements OnInit {
}
}
}
/**
* A type representing local application data.
*/
export type Local = {
namespaceOptions: {
filter: string;
predicate: string;
reverse: boolean;
page: number;
};
repositoryOptions: {
filter: string;
predicate: string;
reverse: boolean;
page: number;
hideStale: boolean;
};
robotOptions: {
filter: string;
predicate: string;
reverse: boolean;
page: number;
};
};
/**
* A type representing a trigger.
*/
export type Trigger = {
id: number;
service: any;
};

View file

@ -0,0 +1,104 @@
import { ViewArray } from '../services/view-array/view-array';
/**
* A type representing local application data.
*/
export type Local = {
contexts?: string[];
dockerContext?: string;
dockerfileLocations?: any;
dockerfilePath?: string;
hasValidContextLocation?: boolean;
hasValidDockerfilePath?: boolean;
maxScore?: number;
namespaceOptions?: {
filter: string;
predicate: string;
reverse: boolean;
page: number;
};
namespaces?: Namespace[];
orderedNamespaces?: ViewArray;
orderedRepositories?: ViewArray;
orderedRobotAccounts?: ViewArray;
repositories?: Repository[];
repositoryFullRefs?: {
icon: string;
title: string;
value: string;
}[];
repositoryOptions?: {
filter: string;
predicate: string;
reverse: boolean;
page: number;
hideStale: boolean;
};
repositoryRefs?: {
kind: string;
name: string;
}[];
robotAccount?: RobotAccount;
robotOptions?: {
filter: string;
predicate: string;
reverse: boolean;
page: number;
};
selectedNamespace?: Namespace;
selectedRepository?: Repository;
triggerAnalysis?: any;
triggerOptions?: {
[key: string]: boolean;
};
};
/**
* A type representing a robot account.
*/
export type RobotAccount = {
can_read: boolean;
is_robot: boolean;
kind: string;
name: string;
};
/**
* A type representing a Git repository.
*/
export type Repository = {
description: string;
full_name: string;
has_admin_permissions: boolean;
last_updated: number;
last_updated_datetime: Date;
name: string;
private: boolean;
url: string;
namespace?: string;
}
/**
* A type representing a repository namespace.
*/
export type Namespace = {
avatar_url: string;
id: string;
personal: boolean;
score: number;
title: string;
url: string;
};
/**
* A type representing a trigger.
*/
export type Trigger = {
id: number;
service: any;
};

Binary file not shown.