Skip to content

Commit

Permalink
fix(): restricted access to private repositories, builds and jobs
Browse files Browse the repository at this point in the history
  • Loading branch information
Izak88 committed Sep 1, 2017
1 parent ed50cb9 commit 40f5ca7
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 5 deletions.
7 changes: 7 additions & 0 deletions src/api/db/build.ts
Expand Up @@ -220,6 +220,13 @@ export function updateBuild(data: any): Promise<boolean> {
});
}

export function getBuildRepositoryId(buildId: number): Promise<any> {
return new Promise((resolve, reject) => {
new Build({ id: buildId }).fetch()
.then(build => !build ? reject(build) : resolve(build.toJSON().repositories_id));
});
}

export function getBuildStatus(buildId: number): Promise<any> {
return new Promise((resolve, reject) => {
new Job()
Expand Down
7 changes: 7 additions & 0 deletions src/api/db/job.ts
Expand Up @@ -132,3 +132,10 @@ export function resetJob(jobId: number): Promise<any> {
.then(job => resolve(job.toJSON()));
});
}

export function getJobRepositoryId(jobId: number): Promise<any> {
return new Promise((resolve, reject) => {
new Job({ id: jobId }).fetch({ withRelated: ['build'] })
.then(job => !job ? reject(job) : resolve(job.toJSON().build.repositories_id));
});
}
2 changes: 2 additions & 0 deletions src/api/db/model.ts
Expand Up @@ -55,4 +55,6 @@ export class JobRun extends Bookshelf.Model<any> {
export class Permission extends Bookshelf.Model<any> {
get tableName() { return 'permissions'; }
get hasTimestamps() { return true; }
repository() { return this.belongsTo(Repository, 'repositories_id'); }
user() { return this.belongsTo(User, 'users_id'); }
}
33 changes: 33 additions & 0 deletions src/api/db/permission.ts
@@ -1,6 +1,8 @@
import { Permission } from './model';
import { getUsers } from './user';
import { getRepositories } from './repository';
import { getBuildRepositoryId } from './build';
import { getJobRepositoryId } from './job';

export function getPermission(id: number): Promise<any> {
return new Promise((resolve, reject) => {
Expand All @@ -16,6 +18,37 @@ export function getRepositoryPermissions(repoId: number): Promise<any> {
});
}

export function getUserRepositoryPermissions(userId: number, repoId: number): Promise<any> {
return new Promise((resolve, reject) => {
new Permission({ repositories_id: repoId, users_id: userId })
.fetch({ withRelated: ['repository'] })
.then(permission => {
if (permission) {
permission = permission.toJSON();
permission.repository.private ? resolve(permission.permission) : resolve(true);
}

reject(permission);
});
});
}

export function getUserBuildPermissions(userId: number, buildId: number): Promise<any> {
return new Promise((resolve, reject) => {
getBuildRepositoryId(buildId)
.then(repoId => resolve(getUserRepositoryPermissions(userId, repoId)))
.catch(() => resolve(false));
});
}

export function getUserJobPermissions(userId: number, jobId: number): Promise<any> {
return new Promise((resolve, reject) => {
getJobRepositoryId(jobId)
.then(repoId => resolve(getUserRepositoryPermissions(userId, repoId)))
.catch(() => resolve(false));
});
}

export function updatePermission(data: any): Promise<any> {
return new Promise((resolve, reject) => {
let updateData = {
Expand Down
31 changes: 30 additions & 1 deletion src/api/server-routes.ts
Expand Up @@ -29,7 +29,12 @@ import {
import { getBuilds, getBuild, getLastBuild } from './db/build';
import { getJob } from './db/job';
import { insertAccessToken, getAccessTokens } from './db/access-token';
import { updatePermission } from './db/permission';
import {
updatePermission,
getUserRepositoryPermissions,
getUserBuildPermissions,
getUserJobPermissions
} from './db/permission';
import { imageExists } from './docker';
import { checkApiRequestAuth } from './security';
import * as multer from 'multer';
Expand Down Expand Up @@ -371,6 +376,30 @@ export function setupRoutes(): express.Router {
return router;
}

export function permissionRoutes(): express.Router {
const router = express.Router();

router.get('/repository/:repoId/user/:userId', (req: express.Request, res: express.Response) => {
getUserRepositoryPermissions(req.params.userId, req.params.repoId).then(perm => {
return res.status(200).json({ data: perm });
}).catch(err => res.status(200).json({ status: false }));
});

router.get('/build/:buildId/user/:userId', (req: express.Request, res: express.Response) => {
getUserBuildPermissions(req.params.userId, req.params.buildId).then(perm => {
return res.status(200).json({ data: perm });
}).catch(err => res.status(200).json({ status: false }));
});

router.get('/job/:jobId/user/:userId', (req: express.Request, res: express.Response) => {
getUserJobPermissions(req.params.userId, req.params.jobId).then(perm => {
return res.status(200).json({ data: perm });
}).catch(err => res.status(200).json({ status: false }));
});

return router;
}

function index(req: express.Request, res: express.Response): void {
return res.status(200).sendFile(resolve(__dirname, '../app/index.html'));
}
1 change: 1 addition & 0 deletions src/api/server.ts
Expand Up @@ -33,6 +33,7 @@ export class ExpressServer implements IExpressServer {
app.use('/api/repositories', routes.repositoryRoutes());
app.use('/api/builds', routes.buildRoutes());
app.use('/api/jobs', routes.jobRoutes());
app.use('/api/permissions', routes.permissionRoutes());
app.use('/badge', routes.badgeRoutes());
app.use(routes.webRoutes());
app.listen(this.config.port, () => {
Expand Down
10 changes: 6 additions & 4 deletions src/app/app.module.ts
Expand Up @@ -9,6 +9,7 @@ import { ConfigServiceProvider } from './services/config.service';
import { ApiServiceProvider } from './services/api.service';
import { SocketServiceProvider } from './services/socket.service';
import { AuthGuardProvider, AuthGuard } from './services/auth-guard.service';
import { AccessGuardProvider, AccessGuard } from './services/access-guard.service';
import { AuthServiceProvider } from './services/auth.service';
import { EqualValidator } from './directives/equal-validator.directive';
import { SafeHtmlPipe } from './pipes/safe-html.pipe';
Expand Down Expand Up @@ -68,12 +69,12 @@ import { AppTeamComponent } from './components/app-team';
{
path: 'build/:id',
component: AppBuildDetailsComponent,
canActivate: [AuthGuard]
canActivate: [AuthGuard, AccessGuard]
},
{
path: 'job/:id',
component: AppJobComponent,
canActivate: [AuthGuard]
canActivate: [AuthGuard, AccessGuard]
},
{
path: 'repositories',
Expand All @@ -83,7 +84,7 @@ import { AppTeamComponent } from './components/app-team';
{
path: 'repo/:id',
component: AppRepositoryComponent,
canActivate: [AuthGuard]
canActivate: [AuthGuard, AccessGuard]
},
{
path: 'settings',
Expand Down Expand Up @@ -115,7 +116,8 @@ import { AppTeamComponent } from './components/app-team';
ApiServiceProvider,
SocketServiceProvider,
AuthServiceProvider,
AuthGuardProvider
AuthGuardProvider,
AccessGuardProvider
],
bootstrap: [ AppComponent ]
})
Expand Down
64 changes: 64 additions & 0 deletions src/app/services/access-guard.service.ts
@@ -0,0 +1,64 @@
import { Injectable, Provider } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { ApiService } from './api.service';
import { AuthService } from './auth.service';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/combineLatest';

@Injectable()
export class AccessGuard implements CanActivate {
constructor(
private apiService: ApiService,
private authService: AuthService,
private router: Router
) { }

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
return new Promise((resolve) => {
let userData: any = this.authService.getData();
if (route.url && route.url[0]) {
if (route.url[0].path === 'repo') {
return this.apiService.getUserRepositoryPermission(
route.params.id, userData.id).toPromise()
.then(permission => {
if (!!permission) {
resolve(!!permission);
} else {
this.router.navigate(['/']);
resolve(false);
}
});
} else if (route.url[0].path === 'build') {
return this.apiService.getUserBuildPermission(route.params.id, userData.id).toPromise()
.then(permission => {
if (!!permission) {
resolve(!!permission);
} else {
this.router.navigate(['/']);
resolve(false);
}
});
} else if (route.url[0].path === 'job') {
return this.apiService.getUserJobPermission(route.params.id, userData.id).toPromise()
.then(permission => {
if (!!permission) {
resolve(!!permission);
} else {
this.router.navigate(['/']);
resolve(false);
}
});
}
} else {
this.router.navigate(['/']);
resolve(false);
}
});
}
}

export const AccessGuardProvider: Provider = {
provide: AccessGuard, useClass: AccessGuard
};
12 changes: 12 additions & 0 deletions src/app/services/api.service.ts
Expand Up @@ -139,6 +139,18 @@ export class ApiService {
return this.post(`${this.url}/repositories/permission`, data);
}

getUserRepositoryPermission(repoId: string, userId: number): Observable<any> {
return this.get(`${this.url}/permissions/repository/${repoId}/user/${userId}`, null, true);
}

getUserBuildPermission(buildId: string, userId: number): Observable<any> {
return this.get(`${this.url}/permissions/build/${buildId}/user/${userId}`, null, true);
}

getUserJobPermission(jobId: string, userId: number): Observable<any> {
return this.get(`${this.url}/permissions/job/${jobId}/user/${userId}`, null, true);
}

getGithubUserData(username: string): Observable<any> {
return this.http.get(`https://api.github.com/users/${username}`);
}
Expand Down

0 comments on commit 40f5ca7

Please sign in to comment.