Add filter on API endpoints to mitigate XSRF (#3542)

Add filter for all API endpoints to allow the POST requests which have
application/json header.
Make update to UI code to make sure all requests contain the header.
This commit is contained in:
Daniel Jiang 2017-11-03 14:43:27 +08:00 committed by GitHub
parent 877e918f85
commit 795d33a45a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 237 additions and 148 deletions

View File

@ -0,0 +1,43 @@
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package filter
import (
beegoctx "github.com/astaxie/beego/context"
"net/http"
"strings"
)
//MediaTypeFilter filters the POST request, it returns 415 if the content type of the request
//doesn't match the preset ones.
func MediaTypeFilter(mediaType ...string) func(*beegoctx.Context) {
return func(ctx *beegoctx.Context) {
filterContentType(ctx.Request, ctx.ResponseWriter, mediaType...)
}
}
func filterContentType(req *http.Request, resp http.ResponseWriter, mediaType ...string) {
if req.Method != http.MethodPost {
return
}
v := req.Header.Get("Content-Type")
mimeType := strings.Split(v, ";")[0]
for _, t := range mediaType {
if t == mimeType {
return
}
}
resp.WriteHeader(http.StatusUnsupportedMediaType)
}

View File

@ -0,0 +1,42 @@
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package filter
import (
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"testing"
)
func TestMediaTypeFilter(t *testing.T) {
assert := assert.New(t)
getReq := httptest.NewRequest(http.MethodGet, "/the/path", nil)
rec := httptest.NewRecorder()
filterContentType(getReq, rec, "application/json")
assert.Equal(http.StatusOK, rec.Code)
postReq := httptest.NewRequest(http.MethodPost, "/the/path", nil)
postReq.Header.Set("Content-Type", "text/html")
rec2 := httptest.NewRecorder()
filterContentType(postReq, rec2, "application/json")
assert.Equal(http.StatusUnsupportedMediaType, rec2.Code)
postReq2 := httptest.NewRequest(http.MethodPost, "/the/path", nil)
postReq2.Header.Set("Content-Type", "application/json; charset=utf-8")
rec3 := httptest.NewRecorder()
filterContentType(postReq2, rec3, "application/json")
assert.Equal(http.StatusOK, rec3.Code)
}

View File

@ -132,6 +132,7 @@ func main() {
filter.Init()
beego.InsertFilter("/*", beego.BeforeRouter, filter.SecurityFilter)
beego.InsertFilter("/api/*", beego.BeforeRouter, filter.MediaTypeFilter("application/json"))
initRouters()
if err := api.SyncRegistry(config.GlobalProjectMgr); err != nil {

View File

@ -85,9 +85,8 @@ export class ProjectPolicyConfigComponent implements OnInit {
response => {
this.orgProjectPolicy.initByProject(response);
this.projectPolicy.initByProject(response);
},
error => this.errorHandler.error(error)
);
})
.catch(error => this.errorHandler.error(error));
}
updateProjectPolicy(projectId: string|number, pp: ProjectPolicy) {
@ -125,7 +124,6 @@ export class ProjectPolicyConfigComponent implements OnInit {
this.refresh();
})
.catch(error => {
console.log(error);
this.onGoing = false;
this.errorHandler.error(error);
});

View File

@ -5,7 +5,7 @@ import { Injectable, Inject } from "@angular/core";
import 'rxjs/add/observable/of';
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
import { Http, URLSearchParams } from '@angular/http';
import { HTTP_JSON_OPTIONS, buildHttpRequestOptions } from '../utils';
import { buildHttpRequestOptions, HTTP_GET_OPTIONS } from '../utils';
/**
* Define service methods to handle the access log related things.
@ -67,7 +67,7 @@ export class AccessLogDefaultService extends AccessLogService {
url = '/api/logs';
}
return this.http.get(url, queryParams ? buildHttpRequestOptions(queryParams) : HTTP_JSON_OPTIONS).toPromise()
return this.http.get(url, queryParams ? buildHttpRequestOptions(queryParams) : HTTP_GET_OPTIONS).toPromise()
.then(response => {
let result: AccessLog = {
metadata: {

View File

@ -3,8 +3,8 @@ import { Injectable, Inject } from "@angular/core";
import 'rxjs/add/observable/of';
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
import { Http } from '@angular/http';
import { HTTP_JSON_OPTIONS } from '../utils';
import { Configuration } from '../config/config'
import { HTTP_JSON_OPTIONS, HTTP_GET_OPTIONS } from '../utils';
import { Configuration } from '../config/config';
/**
@ -51,7 +51,7 @@ export class ConfigurationDefaultService extends ConfigurationService {
}
getConfigurations(): Observable<Configuration> | Promise<Configuration> | Configuration {
return this.http.get(this._baseUrl, HTTP_JSON_OPTIONS).toPromise()
return this.http.get(this._baseUrl, HTTP_GET_OPTIONS).toPromise()
.then(response => response.json() as Configuration)
.catch(error => Promise.reject(error));
}

View File

@ -7,7 +7,7 @@ import 'rxjs/add/observable/of';
import { IServiceConfig, SERVICE_CONFIG } from '../service.config';
import { buildHttpRequestOptions } from '../utils';
import {buildHttpRequestOptions, HTTP_JSON_OPTIONS, HTTP_GET_OPTIONS} from '../utils';
/**
* Define the service methods to handle the endpoint related things.
@ -136,7 +136,7 @@ export class EndpointDefaultService extends EndpointService {
}
let requestUrl: string = `${this._endpointUrl}/${endpointId}`;
return this.http
.get(requestUrl)
.get(requestUrl, HTTP_GET_OPTIONS)
.toPromise()
.then(response=>response.json() as Endpoint)
.catch(error=>Promise.reject(error));
@ -148,7 +148,7 @@ export class EndpointDefaultService extends EndpointService {
}
let requestUrl: string = `${this._endpointUrl}`;
return this.http
.post(requestUrl, JSON.stringify(endpoint))
.post(requestUrl, JSON.stringify(endpoint), HTTP_JSON_OPTIONS)
.toPromise()
.then(response=>response.status)
.catch(error=>Promise.reject(error));
@ -163,7 +163,7 @@ export class EndpointDefaultService extends EndpointService {
}
let requestUrl: string = `${this._endpointUrl}/${endpointId}`;
return this.http
.put(requestUrl, JSON.stringify(endpoint))
.put(requestUrl, JSON.stringify(endpoint), HTTP_JSON_OPTIONS)
.toPromise()
.then(response=>response.status)
.catch(error=>Promise.reject(error));
@ -189,14 +189,14 @@ export class EndpointDefaultService extends EndpointService {
if(endpoint.id) {
requestUrl = `${this._endpointUrl}/${endpoint.id}/ping`;
return this.http
.post(requestUrl, {})
.post(requestUrl, HTTP_JSON_OPTIONS)
.toPromise()
.then(response=>response.status)
.catch(error=>Promise.reject(error));
} else {
requestUrl = `${this._endpointUrl}/ping`;
return this.http
.post(requestUrl, endpoint)
.post(requestUrl, endpoint, HTTP_JSON_OPTIONS)
.toPromise()
.then(response=>response.status)
.catch(error=>Promise.reject(error));
@ -209,7 +209,7 @@ export class EndpointDefaultService extends EndpointService {
}
let requestUrl: string = `${this._endpointUrl}/${endpointId}/policies`;
return this.http
.get(requestUrl)
.get(requestUrl, HTTP_GET_OPTIONS)
.toPromise()
.then(response=>response.json() as ReplicationRule[])
.catch(error=>Promise.reject(error));

View File

@ -5,7 +5,7 @@ import { Injectable, Inject } from "@angular/core";
import 'rxjs/add/observable/of';
import { Http, RequestOptions } from '@angular/http';
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
import { buildHttpRequestOptions, HTTP_JSON_OPTIONS } from '../utils';
import { buildHttpRequestOptions, HTTP_JSON_OPTIONS, HTTP_GET_OPTIONS } from '../utils';
/**
* Define the service methods to handle the job log related things.
@ -53,7 +53,7 @@ export class JobLogDefaultService extends JobLogService {
}
_getJobLog(logUrl: string): Observable<string> | Promise<string> | string {
return this.http.get(logUrl).toPromise()
return this.http.get(logUrl, HTTP_GET_OPTIONS).toPromise()
.then(response => response.text())
.catch(error => Promise.reject(error));
}

View File

@ -6,6 +6,7 @@ import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
import { Project } from '../project-policy-config/project';
import { ProjectPolicy } from '../project-policy-config/project-policy-config.component';
import {HTTP_JSON_OPTIONS, HTTP_GET_OPTIONS} from "../utils";
/**
* Define the service methods to handle the Prject related things.
@ -49,9 +50,6 @@ export abstract class ProjectService {
@Injectable()
export class ProjectDefaultService extends ProjectService {
headers = new Headers({'Content-type': 'application/json'});
options = new RequestOptions({'headers': this.headers});
constructor(
private http: Http,
@Inject(SERVICE_CONFIG) private config: IServiceConfig
@ -65,7 +63,7 @@ export class ProjectDefaultService extends ProjectService {
}
return this.http
.get(`/api/projects/${projectId}`)
.get(`/api/projects/${projectId}`, HTTP_GET_OPTIONS)
.map(response => response.json())
.catch(error => Observable.throw(error));
}
@ -78,7 +76,7 @@ export class ProjectDefaultService extends ProjectService {
'prevent_vul': projectPolicy.PreventVulImg ? 'true' : 'false',
'severity': projectPolicy.PreventVulImgServerity,
'auto_scan': projectPolicy.ScanImgOnPush ? 'true' : 'false'
} }, this.options)
} }, HTTP_JSON_OPTIONS)
.map(response => response.status)
.catch(error => Observable.throw(error));
}

View File

@ -5,7 +5,7 @@ import { Injectable, Inject } from "@angular/core";
import 'rxjs/add/observable/of';
import { Http, RequestOptions } from '@angular/http';
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
import { buildHttpRequestOptions, HTTP_JSON_OPTIONS } from '../utils';
import { buildHttpRequestOptions, HTTP_JSON_OPTIONS, HTTP_GET_OPTIONS } from '../utils';
/**
* Define the service methods to handle the replication (rule and job) related things.
@ -179,7 +179,7 @@ export class ReplicationDefaultService extends ReplicationService {
}
let url: string = `${this._ruleBaseUrl}/${ruleId}`;
return this.http.get(url).toPromise()
return this.http.get(url, HTTP_GET_OPTIONS).toPromise()
.then(response => response.json() as ReplicationRule)
.catch(error => Promise.reject(error));
}
@ -281,7 +281,7 @@ export class ReplicationDefaultService extends ReplicationService {
}
let logUrl: string = `${this._jobBaseUrl}/${jobId}/log`;
return this.http.get(logUrl).toPromise()
return this.http.get(logUrl, HTTP_GET_OPTIONS).toPromise()
.then(response => response.text())
.catch(error => Promise.reject(error));
}

View File

@ -101,13 +101,13 @@ export class ScanningResultDefaultService extends ScanningResultService {
return Promise.reject('Bad argument');
}
return this.http.post(`${this._baseUrl}/${repoName}/tags/${tagId}/scan`, null).toPromise()
return this.http.post(`${this._baseUrl}/${repoName}/tags/${tagId}/scan`, HTTP_JSON_OPTIONS).toPromise()
.then(() => { return true })
.catch(error => Promise.reject(error));
}
startScanningAll(): Observable<any> | Promise<any> | any {
return this.http.post(`${this._baseUrl}/scanAll`,{}).toPromise()
return this.http.post(`${this._baseUrl}/scanAll`, HTTP_JSON_OPTIONS).toPromise()
.then(() => {return true})
.catch(error => Promise.reject(error));
}

View File

@ -3,6 +3,7 @@ import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { SystemInfo } from './interface';
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
import {HTTP_GET_OPTIONS} from "../utils";
/**
* Get System information about current backend server.
* @abstract
@ -26,7 +27,7 @@ export class SystemInfoDefaultService extends SystemInfoService {
}
getSystemInfo(): Observable<SystemInfo> | Promise<SystemInfo> | SystemInfo {
let url = this.config.systemInfoEndpoint ? this.config.systemInfoEndpoint : '/api/systeminfo';
return this.http.get(url)
return this.http.get(url, HTTP_GET_OPTIONS)
.toPromise()
.then(systemInfo=>systemInfo.json() as SystemInfo)
.catch(error=>Promise.reject(error));

View File

@ -5,7 +5,7 @@ import { Injectable, Inject } from "@angular/core";
import 'rxjs/add/observable/of';
import { Http } from '@angular/http';
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
import { buildHttpRequestOptions, HTTP_JSON_OPTIONS } from '../utils';
import { buildHttpRequestOptions, HTTP_JSON_OPTIONS, HTTP_GET_OPTIONS } from '../utils';
/**
* For getting tag signatures.
@ -103,7 +103,7 @@ export class TagDefaultService extends TagService {
_getSignatures(repositoryName: string): Promise<VerifiedSignature[]> {
let url: string = `${this._baseUrl}/${repositoryName}/signatures`;
return this.http.get(url, HTTP_JSON_OPTIONS).toPromise()
return this.http.get(url, HTTP_GET_OPTIONS).toPromise()
.then(response => response.json() as VerifiedSignature[])
.catch(error => Promise.reject(error))
}
@ -132,7 +132,7 @@ export class TagDefaultService extends TagService {
}
let url: string = `${this._baseUrl}/${repositoryName}/tags/${tag}`;
return this.http.get(url, HTTP_JSON_OPTIONS).toPromise()
return this.http.get(url, HTTP_GET_OPTIONS).toPromise()
.then(response => response.json() as Tag)
.catch(error => Promise.reject(error));
}

View File

@ -48,6 +48,15 @@ export const HTTP_JSON_OPTIONS: RequestOptions = new RequestOptions({
})
});
export const HTTP_GET_OPTIONS: RequestOptions = new RequestOptions({
headers: new Headers({
"Content-Type": 'application/json',
"Accept": 'application/json',
"Cache-Control": 'no-cache',
"Pragma": 'no-cache'
})
});
/**
* Build http request options
*
@ -59,7 +68,9 @@ export function buildHttpRequestOptions(params: RequestQueryParams): RequestOpti
let reqOptions: RequestOptions = new RequestOptions({
headers: new Headers({
"Content-Type": 'application/json',
"Accept": 'application/json'
"Accept": 'application/json',
"Cache-Control": 'no-cache',
"Pragma": 'no-cache'
})
});

View File

@ -31,7 +31,7 @@
"clarity-icons": "^0.9.8",
"clarity-ui": "^0.9.8",
"core-js": "^2.4.1",
"harbor-ui": "0.4.97",
"harbor-ui": "0.5.0",
"intl": "^1.2.5",
"mutationobserver-shim": "^0.3.2",
"ngx-cookie": "^1.0.0",

View File

@ -16,6 +16,7 @@ import { Headers, Http, RequestOptions, URLSearchParams } from '@angular/http';
import 'rxjs/add/operator/toPromise';
import { PasswordSetting } from './password-setting';
import {HTTP_FORM_OPTIONS, HTTP_JSON_OPTIONS, HTTP_GET_OPTIONS} from "../../shared/shared.utils";
const passwordChangeEndpoint = "/api/users/:user_id/password";
const sendEmailEndpoint = "/sendEmail";
@ -23,13 +24,6 @@ const resetPasswordEndpoint = "/reset";
@Injectable()
export class PasswordSettingService {
headers: Headers = new Headers({
"Accept": 'application/json',
"Content-Type": 'application/json'
});
options: RequestOptions = new RequestOptions({
'headers': this.headers
});
constructor(private http: Http) { }
@ -39,7 +33,7 @@ export class PasswordSettingService {
}
let putUrl = passwordChangeEndpoint.replace(":user_id", userId + "");
return this.http.put(putUrl, JSON.stringify(setting), this.options)
return this.http.put(putUrl, JSON.stringify(setting), HTTP_JSON_OPTIONS)
.toPromise()
.then(() => null)
.catch(error => {
@ -53,7 +47,7 @@ export class PasswordSettingService {
}
let getUrl = sendEmailEndpoint + "?email=" + email;
return this.http.get(getUrl, this.options).toPromise()
return this.http.get(getUrl, HTTP_GET_OPTIONS).toPromise()
.then(response => response)
.catch(error => {
return Promise.reject(error);
@ -65,18 +59,12 @@ export class PasswordSettingService {
return Promise.reject("Invalid reset uuid or password");
}
let formHeaders = new Headers({
"Content-Type": 'application/x-www-form-urlencoded'
});
let formOptions: RequestOptions = new RequestOptions({
headers: formHeaders
});
let body: URLSearchParams = new URLSearchParams();
body.set("reset_uuid", uuid);
body.set("password", newPassword);
return this.http.post(resetPasswordEndpoint, body.toString(), formOptions)
return this.http.post(resetPasswordEndpoint, body.toString(), HTTP_FORM_OPTIONS)
.toPromise()
.then(response => response)
.catch(error => {

View File

@ -16,6 +16,7 @@ import { Headers, Http, URLSearchParams } from '@angular/http';
import 'rxjs/add/operator/toPromise';
import { SignInCredential } from '../../shared/sign-in-credential';
import {HTTP_FORM_OPTIONS} from "../../shared/shared.utils";
const signInUrl = '/login';
/**
@ -27,9 +28,6 @@ const signInUrl = '/login';
*/
@Injectable()
export class SignInService {
headers = new Headers({
"Content-Type": 'application/x-www-form-urlencoded'
});
constructor(private http: Http) {}
@ -46,7 +44,7 @@ export class SignInService {
body.set('password', signInCredential.password);
//Trigger Http
return this.http.post(signInUrl, body.toString(), { headers: this.headers })
return this.http.post(signInUrl, body.toString(), HTTP_FORM_OPTIONS)
.toPromise()
.then(()=>null)
.catch(this.handleError);

View File

@ -18,7 +18,7 @@ import 'rxjs/add/operator/toPromise';
import { AppConfig } from './app-config';
import { CookieService } from 'ngx-cookie';
import { CookieKeyOfAdmiral, HarborQueryParamKey } from './shared/shared.const';
import { maintainUrlQueryParmas } from './shared/shared.utils';
import {HTTP_JSON_OPTIONS, maintainUrlQueryParmas, HTTP_GET_OPTIONS} from './shared/shared.utils';
export const systemInfoEndpoint = "/api/systeminfo";
/**
@ -30,12 +30,6 @@ export const systemInfoEndpoint = "/api/systeminfo";
*/
@Injectable()
export class AppConfigService {
headers = new Headers({
"Content-Type": 'application/json'
});
options = new RequestOptions({
headers: this.headers
});
//Store the application configuration
configurations: AppConfig = new AppConfig();
@ -45,7 +39,7 @@ export class AppConfigService {
private cookie: CookieService) { }
public load(): Promise<AppConfig> {
return this.http.get(systemInfoEndpoint, this.options).toPromise()
return this.http.get(systemInfoEndpoint, HTTP_GET_OPTIONS).toPromise()
.then(response => {
this.configurations = response.json() as AppConfig;
@ -90,7 +84,7 @@ export class AppConfigService {
}
//Save back to cookie
this.cookie.put(CookieKeyOfAdmiral, endpoint);
this.cookie.put(CookieKeyOfAdmiral, endpoint, HTTP_JSON_OPTIONS);
this.configurations.admiral_endpoint = endpoint;
}
}

View File

@ -16,6 +16,7 @@ import { Headers, Http, RequestOptions } from '@angular/http';
import 'rxjs/add/operator/toPromise';
import { SearchResults } from './search-results';
import {HTTP_GET_OPTIONS} from "../../shared/shared.utils";
const searchEndpoint = "/api/search";
/**
@ -27,12 +28,6 @@ const searchEndpoint = "/api/search";
*/
@Injectable()
export class GlobalSearchService {
headers = new Headers({
"Content-Type": 'application/json'
});
options = new RequestOptions({
headers: this.headers
});
constructor(private http: Http) { }
@ -47,7 +42,7 @@ export class GlobalSearchService {
doSearch(term: string): Promise<SearchResults> {
let searchUrl = searchEndpoint + "?q=" + term;
return this.http.get(searchUrl, this.options).toPromise()
return this.http.get(searchUrl, HTTP_GET_OPTIONS).toPromise()
.then(response => response.json() as SearchResults)
.catch(error => Promise.reject(error));
}

View File

@ -16,6 +16,7 @@ import { Headers, Http, RequestOptions } from '@angular/http';
import 'rxjs/add/operator/toPromise';
import { Configuration } from 'harbor-ui';
import {HTTP_GET_OPTIONS, HTTP_JSON_OPTIONS} from "../shared/shared.utils";
const configEndpoint = "/api/configurations";
const emailEndpoint = "/api/email/ping";
@ -23,38 +24,31 @@ const ldapEndpoint = "/api/ldap/ping";
@Injectable()
export class ConfigurationService {
headers: Headers = new Headers({
"Accept": 'application/json',
"Content-Type": 'application/json'
});
options: RequestOptions = new RequestOptions({
'headers': this.headers
});
constructor(private http: Http) { }
public getConfiguration(): Promise<Configuration> {
return this.http.get(configEndpoint, this.options).toPromise()
return this.http.get(configEndpoint, HTTP_GET_OPTIONS).toPromise()
.then(response => response.json() as Configuration)
.catch(error => Promise.reject(error));
}
public saveConfiguration(values: any): Promise<any> {
return this.http.put(configEndpoint, JSON.stringify(values), this.options)
return this.http.put(configEndpoint, JSON.stringify(values), HTTP_JSON_OPTIONS)
.toPromise()
.then(response => response)
.catch(error => Promise.reject(error));
}
public testMailServer(mailSettings: any): Promise<any> {
return this.http.post(emailEndpoint, JSON.stringify(mailSettings), this.options)
return this.http.post(emailEndpoint, JSON.stringify(mailSettings), HTTP_JSON_OPTIONS)
.toPromise()
.then(response => response)
.catch(error => Promise.reject(error));
}
public testLDAPServer(ldapSettings: any): Promise<any> {
return this.http.post(ldapEndpoint, JSON.stringify(ldapSettings), this.options)
return this.http.post(ldapEndpoint, JSON.stringify(ldapSettings), HTTP_JSON_OPTIONS)
.toPromise()
.then(response => response)
.catch(error => Promise.reject(error));

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { Http, Headers, RequestOptions, URLSearchParams } from '@angular/http';
import { Http, URLSearchParams } from '@angular/http';
import { AuditLog } from './audit-log';
@ -20,45 +20,43 @@ import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/throw';
import {buildHttpRequestOptions} from '../shared/shared.utils';
import {RequestQueryParams} from 'harbor-ui';
export const logEndpoint = "/api/logs";
export const logEndpoint = '/api/logs';
@Injectable()
export class AuditLogService {
httpOptions = new RequestOptions({
headers: new Headers({
"Content-Type": 'application/json',
"Accept": 'application/json'
})
});
constructor(private http: Http) {}
listAuditLogs(queryParam: AuditLog): Observable<any> {
let params: URLSearchParams = new URLSearchParams(queryParam.keywords);
if(queryParam.begin_timestamp) {
params.set('begin_timestamp', <string>queryParam.begin_timestamp);
if (queryParam.begin_timestamp) {
params.set('begin_timestamp', <string>queryParam.begin_timestamp);
}
if(queryParam.end_timestamp) {
if (queryParam.end_timestamp) {
params.set('end_timestamp', <string>queryParam.end_timestamp);
}
if(queryParam.username) {
if (queryParam.username) {
params.set('username', queryParam.username);
}
if(queryParam.page) {
if (queryParam.page) {
params.set('page', <string>queryParam.page);
}
if(queryParam.page_size) {
if (queryParam.page_size) {
params.set('page_size', <string>queryParam.page_size);
}
return this.http
.get(`/api/projects/${queryParam.project_id}/logs`, {params: params})
.get(`/api/projects/${queryParam.project_id}/logs`, buildHttpRequestOptions(params))
.map(response => response)
.catch(error => Observable.throw(error));
}
getRecentLogs(lines: number): Observable<AuditLog[]> {
return this.http.get(logEndpoint + "?page_size=" + lines, this.httpOptions)
let params: RequestQueryParams = new RequestQueryParams();
params.set('page_size', '' + lines);
return this.http.get(logEndpoint, buildHttpRequestOptions(params))
.map(response => response.json() as AuditLog[])
.catch(error => Observable.throw(error));
}

View File

@ -20,6 +20,7 @@ import 'rxjs/add/operator/map';
import 'rxjs/add/observable/throw';
import { Member } from './member';
import {HTTP_JSON_OPTIONS, HTTP_GET_OPTIONS} from "../../shared/shared.utils";
@Injectable()
export class MemberService {
@ -28,21 +29,21 @@ export class MemberService {
listMembers(projectId: number, username: string): Observable<Member[]> {
return this.http
.get(`/api/projects/${projectId}/members?username=${username}`)
.get(`/api/projects/${projectId}/members?username=${username}`, HTTP_GET_OPTIONS)
.map(response=>response.json() as Member[])
.catch(error=>Observable.throw(error));
}
addMember(projectId: number, username: string, roleId: number): Observable<any> {
return this.http
.post(`/api/projects/${projectId}/members`, { username: username, roles: [ roleId ] })
.post(`/api/projects/${projectId}/members`, { username: username, roles: [ roleId ] }, HTTP_JSON_OPTIONS)
.map(response=>response.status)
.catch(error=>Observable.throw(error));
}
changeMemberRole(projectId: number, userId: number, roleId: number): Observable<any> {
return this.http
.put(`/api/projects/${projectId}/members/${userId}`, { roles: [ roleId ]})
.put(`/api/projects/${projectId}/members/${userId}`, { roles: [ roleId ]}, HTTP_JSON_OPTIONS)
.map(response=>response.status)
.catch(error=>Observable.throw(error));
}

View File

@ -22,20 +22,16 @@ import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/throw';
import {HTTP_JSON_OPTIONS, buildHttpRequestOptions, HTTP_GET_OPTIONS} from "../shared/shared.utils";
@Injectable()
export class ProjectService {
headers = new Headers({'Content-type': 'application/json'});
options = new RequestOptions({'headers': this.headers});
constructor(private http: Http) {}
getProject(projectId: number): Observable<any> {
return this.http
.get(`/api/projects/${projectId}`)
.get(`/api/projects/${projectId}`, HTTP_GET_OPTIONS)
.map(response=>response.json())
.catch(error=>Observable.throw(error));
}
@ -52,8 +48,10 @@ export class ProjectService {
if(isPublic !== undefined){
params.set('public', ''+isPublic);
}
//let options = new RequestOptions({ headers: this.getHeaders, search: params });
return this.http
.get(`/api/projects`, {search: params})
.get(`/api/projects`, buildHttpRequestOptions(params))
.map(response=>response)
.catch(error=>Observable.throw(error));
}
@ -64,14 +62,14 @@ export class ProjectService {
JSON.stringify({'project_name': name, 'metadata': {
public: metadata.public ? 'true' : 'false',
}})
, this.options)
, HTTP_JSON_OPTIONS)
.map(response=>response.status)
.catch(error=>Observable.throw(error));
}
toggleProjectPublic(projectId: number, isPublic: string): Observable<any> {
return this.http
.put(`/api/projects/${projectId}`, { 'metadata': {'public': isPublic} }, this.options)
.put(`/api/projects/${projectId}`, { 'metadata': {'public': isPublic} }, HTTP_JSON_OPTIONS)
.map(response => response.status)
.catch(error => Observable.throw(error));
}
@ -92,7 +90,7 @@ export class ProjectService {
checkProjectMember(projectId: number): Observable<any> {
return this.http
.get(`/api/projects/${projectId}/members`)
.get(`/api/projects/${projectId}/members`, HTTP_GET_OPTIONS)
.map(response=>response.json())
.catch(error=>Observable.throw(error));
}

View File

@ -16,6 +16,7 @@ import { Headers, Http, RequestOptions } from '@angular/http';
import 'rxjs/add/operator/toPromise';
import { Repository } from 'harbor-ui';
import {HTTP_GET_OPTIONS} from "../../shared/shared.utils";
export const topRepoEndpoint = "/api/repositories/top";
/**
@ -27,12 +28,6 @@ export const topRepoEndpoint = "/api/repositories/top";
*/
@Injectable()
export class TopRepoService {
headers = new Headers({
"Content-Type": 'application/json'
});
options = new RequestOptions({
headers: this.headers
});
constructor(private http: Http) { }
@ -45,7 +40,7 @@ export class TopRepoService {
* @memberOf GlobalSearchService
*/
getTopRepos(): Promise<Repository[]> {
return this.http.get(topRepoEndpoint, this.options).toPromise()
return this.http.get(topRepoEndpoint, HTTP_GET_OPTIONS).toPromise()
.then(response => response.json() as Repository[])
.catch(error => Promise.reject(error));
}

View File

@ -19,7 +19,8 @@ import { SessionUser } from './session-user';
import { Member } from '../project/member/member';
import { SignInCredential } from './sign-in-credential';
import { enLang } from '../shared/shared.const'
import { enLang } from '../shared/shared.const';
import {HTTP_FORM_OPTIONS, HTTP_JSON_OPTIONS, HTTP_GET_OPTIONS} from "./shared.utils";
const signInUrl = '/login';
const currentUserEndpint = "/api/users/current";
@ -44,13 +45,9 @@ export class SessionService {
projectMembers: Member[];
headers = new Headers({
"Content-Type": 'application/json'
});
formHeaders = new Headers({
/*formHeaders = new Headers({
"Content-Type": 'application/x-www-form-urlencoded'
});
});*/
constructor(private http: Http) { }
@ -72,7 +69,7 @@ export class SessionService {
'&password=' + encodeURIComponent(signInCredential.password);
//Trigger Http
return this.http.post(signInUrl, queryParam, { headers: this.formHeaders })
return this.http.post(signInUrl, queryParam, HTTP_FORM_OPTIONS)
.toPromise()
.then(() => null)
.catch(error => this.handleError(error));
@ -86,7 +83,7 @@ export class SessionService {
* @memberOf SessionService
*/
retrieveUser(): Promise<SessionUser> {
return this.http.get(currentUserEndpint, { headers: this.headers }).toPromise()
return this.http.get(currentUserEndpint, HTTP_GET_OPTIONS).toPromise()
.then(response => this.currentUser = response.json() as SessionUser)
.catch(error => this.handleError(error))
}
@ -102,7 +99,7 @@ export class SessionService {
* Log out the system
*/
signOff(): Promise<any> {
return this.http.get(signOffEndpoint, { headers: this.headers }).toPromise()
return this.http.get(signOffEndpoint, HTTP_GET_OPTIONS).toPromise()
.then(() => {
//Destroy current session cache
//this.currentUser = null;
@ -124,7 +121,7 @@ export class SessionService {
return Promise.reject("Invalid account settings");
}
let putUrl = accountEndpoint.replace(":id", account.user_id + "");
return this.http.put(putUrl, JSON.stringify(account), { headers: this.headers }).toPromise()
return this.http.put(putUrl, JSON.stringify(account), HTTP_JSON_OPTIONS).toPromise()
.then(() => {
//Retrieve current session user
return this.retrieveUser();
@ -146,7 +143,7 @@ export class SessionService {
}
let getUrl = langEndpoint + "?lang=" + backendLang;
return this.http.get(getUrl).toPromise()
return this.http.get(getUrl, HTTP_GET_OPTIONS).toPromise()
.then(() => null)
.catch(error => this.handleError(error))
}
@ -158,7 +155,7 @@ export class SessionService {
body.set('value', value);
//Trigger Http
return this.http.post(userExistsEndpoint, body.toString(), { headers: this.formHeaders })
return this.http.post(userExistsEndpoint, body.toString(), HTTP_FORM_OPTIONS)
.toPromise()
.then(response => {
return response.json();

View File

@ -15,6 +15,8 @@ import { NgForm } from '@angular/forms';
import { httpStatusCode, AlertType } from './shared.const';
import { MessageService } from '../global-message/message.service';
import { Comparator, State } from 'clarity-angular';
import {RequestOptions, Headers} from "@angular/http";
import {RequestQueryParams} from "harbor-ui";
/**
* To handle the error message body
@ -155,6 +157,50 @@ export class CustomComparator<T> implements Comparator<T> {
}
}
export const HTTP_JSON_OPTIONS: RequestOptions = new RequestOptions({
headers: new Headers({
"Content-Type": 'application/json',
"Accept": 'application/json',
})
});
export const HTTP_GET_OPTIONS: RequestOptions = new RequestOptions({
headers: new Headers({
"Content-Type": 'application/json',
"Accept": 'application/json',
"Cache-Control": 'no-cache',
"Pragma": 'no-cache'
})
});
export const HTTP_FORM_OPTIONS: RequestOptions = new RequestOptions({
headers: new Headers({
"Content-Type": 'application/x-www-form-urlencoded'
})
});
/**
* Build http request options
*
* @export
* @param {RequestQueryParams} params
* @returns {RequestOptions}
*/
export function buildHttpRequestOptions(params: RequestQueryParams): RequestOptions {
let reqOptions: RequestOptions = new RequestOptions({
headers: new Headers({
"Content-Type": 'application/json',
"Accept": 'application/json',
"Cache-Control": 'no-cache',
"Pragma": 'no-cache'
})
});
if (params) {
reqOptions.search = params;
}
return reqOptions;
}
/**
* Filter columns via RegExp
*

View File

@ -17,6 +17,7 @@ import 'rxjs/add/operator/toPromise';
import { Statistics } from './statistics';
import { Volumes } from './volumes';
import {HTTP_GET_OPTIONS} from "../shared.utils";
const statisticsEndpoint = "/api/statistics";
const volumesEndpoint = "/api/systeminfo/volumes";
@ -29,23 +30,17 @@ const volumesEndpoint = "/api/systeminfo/volumes";
*/
@Injectable()
export class StatisticsService {
headers = new Headers({
"Content-Type": 'application/json'
});
options = new RequestOptions({
headers: this.headers
});
constructor(private http: Http) { }
getStatistics(): Promise<Statistics> {
return this.http.get(statisticsEndpoint, this.options).toPromise()
return this.http.get(statisticsEndpoint, HTTP_GET_OPTIONS).toPromise()
.then(response => response.json() as Statistics)
.catch(error => Promise.reject(error));
}
getVolumes(): Promise<Volumes> {
return this.http.get(volumesEndpoint, this.options).toPromise()
return this.http.get(volumesEndpoint, HTTP_GET_OPTIONS).toPromise()
.then(response => response.json() as Volumes)
.catch(error => Promise.reject(error));
}

View File

@ -16,6 +16,7 @@ import { Headers, Http, RequestOptions } from '@angular/http';
import 'rxjs/add/operator/toPromise';
import { User } from './user';
import {HTTP_JSON_OPTIONS, HTTP_GET_OPTIONS} from "../shared/shared.utils";
const userMgmtEndpoint = '/api/users';
@ -27,11 +28,6 @@ const userMgmtEndpoint = '/api/users';
*/
@Injectable()
export class UserService {
httpOptions = new RequestOptions({
headers: new Headers({
"Content-Type": 'application/json'
})
});
constructor(private http: Http) { }
@ -42,21 +38,21 @@ export class UserService {
//Get the user list
getUsers(): Promise<User[]> {
return this.http.get(userMgmtEndpoint, this.httpOptions).toPromise()
return this.http.get(userMgmtEndpoint, HTTP_GET_OPTIONS).toPromise()
.then(response => response.json() as User[])
.catch(error => this.handleError(error));
}
//Add new user
addUser(user: User): Promise<any> {
return this.http.post(userMgmtEndpoint, JSON.stringify(user), this.httpOptions).toPromise()
return this.http.post(userMgmtEndpoint, JSON.stringify(user), HTTP_JSON_OPTIONS).toPromise()
.then(() => null)
.catch(error => this.handleError(error));
}
//Delete the specified user
deleteUser(userId: number): Promise<any> {
return this.http.delete(userMgmtEndpoint + "/" + userId, this.httpOptions)
return this.http.delete(userMgmtEndpoint + "/" + userId, HTTP_JSON_OPTIONS)
.toPromise()
.then(() => null)
.catch(error => this.handleError(error));
@ -64,7 +60,7 @@ export class UserService {
//Update user to enable/disable the admin role
updateUser(user: User): Promise<any> {
return this.http.put(userMgmtEndpoint + "/" + user.user_id, JSON.stringify(user), this.httpOptions)
return this.http.put(userMgmtEndpoint + "/" + user.user_id, JSON.stringify(user), HTTP_JSON_OPTIONS)
.toPromise()
.then(() => null)
.catch(error => this.handleError(error));
@ -72,7 +68,7 @@ export class UserService {
//Set user admin role
updateUserRole(user: User): Promise<any> {
return this.http.put(userMgmtEndpoint + "/" + user.user_id + "/sysadmin", JSON.stringify(user), this.httpOptions)
return this.http.put(userMgmtEndpoint + "/" + user.user_id + "/sysadmin", JSON.stringify(user), HTTP_JSON_OPTIONS)
.toPromise()
.then(() => null)
.catch(error => this.handleError(error));