Merge "Support names and ids for users, projects and domains in Keystone"
This commit is contained in:
commit
8828ec6382
|
@ -70,62 +70,77 @@ export default class Keystone extends AbstractService {
|
|||
|
||||
/**
|
||||
* Issue a token from the provided credentials. Credentials will be read from the
|
||||
* configuration, unless they have been explicitly provided. Note that both the userDomainName
|
||||
* and the projectDomainName are only required if the user/project names are given, rather
|
||||
* than the explicit user/domain ID's.
|
||||
* configuration, unless they have been explicitly provided.
|
||||
*
|
||||
* NOTE: This method is only applicable if the password auth plugin on keystone is enabled.
|
||||
* Other auth methods will have to be provided by third-party developers.
|
||||
*
|
||||
* @param {String} username An optional user name or ID.
|
||||
* @param {String} password An optional password.
|
||||
* @param {String} projectName An optional project name or ID.
|
||||
* @param {String} userDomainName Domain name for the user, not required if a user id is given.
|
||||
* @param {String} projectDomainName Domain name for the project, not required with project ID.
|
||||
* @param {Object} credentials Optional credentials.
|
||||
* @param {String} credentials.user_id An optional user ID.
|
||||
* @param {String} credentials.username An optional user name.
|
||||
* @param {String} credentials.password An optional password.
|
||||
* @param {String} credentials.user_domain_id An optional user domain ID.
|
||||
* Not required if a user ID is given.
|
||||
* @param {String} credentials.user_domain_name An optional user domain name.
|
||||
* Not required if a user ID is given.
|
||||
* @param {String} credentials.project_id An optional project ID.
|
||||
* @param {String} credentials.project_name An optional project name.
|
||||
* @param {String} credentials.project_domain_id An optional project domain ID.
|
||||
* Not required if a project ID is given.
|
||||
* @param {String} credentials.project_domain_name An optional project domain name.
|
||||
* Not required if a project ID is given.
|
||||
* @returns {Promise.<T>} A promise which will resolve with a valid token.
|
||||
*/
|
||||
tokenIssue(username = this._safeConfigGet('auth.username'),
|
||||
password = this._safeConfigGet('auth.password'),
|
||||
projectName = this._safeConfigGet('auth.project_name'),
|
||||
userDomainName = this._safeConfigGet('auth.user_domain_id'),
|
||||
projectDomainName = this._safeConfigGet('auth.project_domain_id')) {
|
||||
tokenIssue({
|
||||
user_id: userId = this._safeConfigGet('auth.user_id'),
|
||||
username = this._safeConfigGet('auth.username'),
|
||||
password = this._safeConfigGet('auth.password'),
|
||||
user_domain_id: userDomainId = this._safeConfigGet('auth.user_domain_id'),
|
||||
user_domain_name: userDomainName = this._safeConfigGet('auth.user_domain_name'),
|
||||
project_id: projectId = this._safeConfigGet('auth.project_id'),
|
||||
project_name: projectName = this._safeConfigGet('auth.project_name'),
|
||||
project_domain_id: projectDomainId = this._safeConfigGet('auth.project_domain_id'),
|
||||
project_domain_name: projectDomainName = this._safeConfigGet('auth.project_domain_name')
|
||||
} = {}) {
|
||||
let project;
|
||||
let user = {password};
|
||||
|
||||
if (userId) {
|
||||
user.id = userId;
|
||||
} else if (username) {
|
||||
user.name = username;
|
||||
if (userDomainId) {
|
||||
user.domain = {id: userDomainId};
|
||||
} else if (userDomainName) {
|
||||
user.domain = {name: userDomainName};
|
||||
} else {
|
||||
user.domain = {id: 'default'};
|
||||
}
|
||||
}
|
||||
|
||||
if (projectId) {
|
||||
project = {id: projectId};
|
||||
} else if (projectName) {
|
||||
project = {name: projectName};
|
||||
if (projectDomainId) {
|
||||
project.domain = {id: projectDomainId};
|
||||
} else if (projectDomainName) {
|
||||
project.domain = {name: projectDomainName};
|
||||
} else {
|
||||
project.domain = {id: 'default'};
|
||||
}
|
||||
}
|
||||
|
||||
const body = {
|
||||
auth: {
|
||||
identity: {
|
||||
methods: ['password'],
|
||||
password: {
|
||||
user: {
|
||||
name: username,
|
||||
password: password
|
||||
}
|
||||
}
|
||||
}
|
||||
password: {user}
|
||||
},
|
||||
scope: project ? {project} : 'unscoped'
|
||||
}
|
||||
};
|
||||
|
||||
if (userDomainName) {
|
||||
body.auth.identity.password.user.domain = {
|
||||
id: userDomainName
|
||||
};
|
||||
}
|
||||
|
||||
if (!projectName) {
|
||||
body.auth.scope = "unscoped";
|
||||
} else {
|
||||
body.auth.scope = {
|
||||
project: {
|
||||
name: projectName
|
||||
}
|
||||
};
|
||||
|
||||
if (projectDomainName) {
|
||||
body.auth.scope.project.domain = {
|
||||
id: projectDomainName
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return this
|
||||
.serviceEndpoint()
|
||||
.then((url) => this.http.httpPost(`${url}auth/tokens`, body))
|
||||
|
|
|
@ -94,12 +94,7 @@ describe("Keystone", () => {
|
|||
|
||||
it("should permit passing your own user, password, and project.", (done) => {
|
||||
keystone
|
||||
.tokenIssue(
|
||||
adminConfig.auth.username,
|
||||
adminConfig.auth.password,
|
||||
adminConfig.auth.project_name,
|
||||
adminConfig.auth.user_domain_id,
|
||||
adminConfig.auth.project_domain_id)
|
||||
.tokenIssue(adminConfig.auth)
|
||||
.then((token) => {
|
||||
expect(token).not.toBeNull();
|
||||
done();
|
||||
|
@ -109,9 +104,38 @@ describe("Keystone", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("should throw an exception if invalid credentials are provided.", (done) => {
|
||||
it("should throw an exception if invalid username and password are provided.", (done) => {
|
||||
keystone
|
||||
.tokenIssue('foo', 'bar', 'lolProject', 'notADomain', 'notADomain')
|
||||
.tokenIssue({
|
||||
username: 'foo',
|
||||
password: 'bar'
|
||||
})
|
||||
.then((token) => done.fail(token))
|
||||
.catch((error) => {
|
||||
expect(error).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should throw an exception if invalid project is provided.", (done) => {
|
||||
keystone
|
||||
.tokenIssue({
|
||||
project_id: 'foo',
|
||||
project_name: 'bar'
|
||||
})
|
||||
.then((token) => done.fail(token))
|
||||
.catch((error) => {
|
||||
expect(error).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should throw an exception if invalid user domain is provided.", (done) => {
|
||||
keystone
|
||||
.tokenIssue({
|
||||
user_domain_id: 'foo',
|
||||
user_domain_name: 'bar'
|
||||
})
|
||||
.then((token) => done.fail(token))
|
||||
.catch((error) => {
|
||||
expect(error).not.toBeNull();
|
||||
|
|
|
@ -70,8 +70,10 @@ describe('Keystone', () => {
|
|||
describe("tokenIssue()", () => {
|
||||
|
||||
it("should 'just work' by using provided credentials from the config.", (done) => {
|
||||
let mockOptions = mockData.tokenIssue();
|
||||
fetchMock.mock(mockData.root());
|
||||
fetchMock.mock(mockData.tokenIssue());
|
||||
fetchMock.mock(mockOptions);
|
||||
|
||||
const keystone = new Keystone(mockData.config);
|
||||
keystone
|
||||
.tokenIssue()
|
||||
|
@ -82,6 +84,138 @@ describe('Keystone', () => {
|
|||
.catch((error) => done.fail(error));
|
||||
});
|
||||
|
||||
it("should support authentication with a user ID", (done) => {
|
||||
let mockOptions = mockData.tokenIssue();
|
||||
fetchMock.mock(mockData.root());
|
||||
fetchMock.mock(mockOptions);
|
||||
|
||||
const userId = 'userId';
|
||||
|
||||
const keystone = new Keystone(mockData.config);
|
||||
keystone
|
||||
.tokenIssue({
|
||||
user_id: userId
|
||||
})
|
||||
.then(() => {
|
||||
const requestBody = JSON.parse(fetchMock.lastCall(mockOptions.matcher)[1].body);
|
||||
expect(requestBody.auth.identity.password.user.id).toEqual(userId);
|
||||
done();
|
||||
})
|
||||
.catch((error) => done.fail(error));
|
||||
});
|
||||
|
||||
it("should support authentication with a username and a user domain ID", (done) => {
|
||||
let mockOptions = mockData.tokenIssue();
|
||||
fetchMock.mock(mockData.root());
|
||||
fetchMock.mock(mockOptions);
|
||||
|
||||
const username = 'username';
|
||||
const userDomainId = 'userDomainId';
|
||||
|
||||
const keystone = new Keystone(mockData.config);
|
||||
keystone
|
||||
.tokenIssue({
|
||||
username: username,
|
||||
user_domain_id: userDomainId
|
||||
})
|
||||
.then(() => {
|
||||
const requestBody = JSON.parse(fetchMock.lastCall(mockOptions.matcher)[1].body);
|
||||
expect(requestBody.auth.identity.password.user.name).toEqual(username);
|
||||
expect(requestBody.auth.identity.password.user.domain.id).toEqual(userDomainId);
|
||||
done();
|
||||
})
|
||||
.catch((error) => done.fail(error));
|
||||
});
|
||||
|
||||
it("should support authentication with a username and a user domain name", (done) => {
|
||||
let mockOptions = mockData.tokenIssue();
|
||||
fetchMock.mock(mockData.root());
|
||||
fetchMock.mock(mockOptions);
|
||||
|
||||
const username = 'username';
|
||||
const userDomainName = 'userDomainName';
|
||||
|
||||
const keystone = new Keystone(mockData.config);
|
||||
keystone
|
||||
.tokenIssue({
|
||||
username: username,
|
||||
user_domain_name: userDomainName
|
||||
})
|
||||
.then(() => {
|
||||
const requestBody = JSON.parse(fetchMock.lastCall(mockOptions.matcher)[1].body);
|
||||
expect(requestBody.auth.identity.password.user.name).toEqual(username);
|
||||
expect(requestBody.auth.identity.password.user.domain.name).toEqual(userDomainName);
|
||||
done();
|
||||
})
|
||||
.catch((error) => done.fail(error));
|
||||
});
|
||||
|
||||
it("should support authentication with a project ID", (done) => {
|
||||
let mockOptions = mockData.tokenIssue();
|
||||
fetchMock.mock(mockData.root());
|
||||
fetchMock.mock(mockOptions);
|
||||
|
||||
const projectId = 'projectId';
|
||||
|
||||
const keystone = new Keystone(mockData.config);
|
||||
keystone
|
||||
.tokenIssue({
|
||||
project_id: projectId,
|
||||
})
|
||||
.then(() => {
|
||||
const requestBody = JSON.parse(fetchMock.lastCall(mockOptions.matcher)[1].body);
|
||||
expect(requestBody.auth.scope.project.id).toEqual(projectId);
|
||||
done();
|
||||
})
|
||||
.catch((error) => done.fail(error));
|
||||
});
|
||||
|
||||
it("should support authentication with a project name and a project domain ID", (done) => {
|
||||
let mockOptions = mockData.tokenIssue();
|
||||
fetchMock.mock(mockData.root());
|
||||
fetchMock.mock(mockOptions);
|
||||
|
||||
const projectName = 'projectName';
|
||||
const projectDomainId = 'projectDomainId';
|
||||
|
||||
const keystone = new Keystone(mockData.config);
|
||||
keystone
|
||||
.tokenIssue({
|
||||
project_name: projectName,
|
||||
project_domain_id: projectDomainId
|
||||
})
|
||||
.then(() => {
|
||||
const requestBody = JSON.parse(fetchMock.lastCall(mockOptions.matcher)[1].body);
|
||||
expect(requestBody.auth.scope.project.name).toEqual(projectName);
|
||||
expect(requestBody.auth.scope.project.domain.id).toEqual(projectDomainId);
|
||||
done();
|
||||
})
|
||||
.catch((error) => done.fail(error));
|
||||
});
|
||||
|
||||
it("should support authentication with a project name and a project domain name", (done) => {
|
||||
let mockOptions = mockData.tokenIssue();
|
||||
fetchMock.mock(mockData.root());
|
||||
fetchMock.mock(mockOptions);
|
||||
|
||||
const projectName = 'projectName';
|
||||
const projectDomainName = 'projectDomainName';
|
||||
|
||||
const keystone = new Keystone(mockData.config);
|
||||
keystone
|
||||
.tokenIssue({
|
||||
project_name: projectName,
|
||||
project_domain_name: projectDomainName
|
||||
})
|
||||
.then(() => {
|
||||
const requestBody = JSON.parse(fetchMock.lastCall(mockOptions.matcher)[1].body);
|
||||
expect(requestBody.auth.scope.project.name).toEqual(projectName);
|
||||
expect(requestBody.auth.scope.project.domain.name).toEqual(projectDomainName);
|
||||
done();
|
||||
})
|
||||
.catch((error) => done.fail(error));
|
||||
});
|
||||
|
||||
it("Should not cache its results", (done) => {
|
||||
let mockOptions = mockData.tokenIssue();
|
||||
fetchMock.mock(mockData.root());
|
||||
|
|
Loading…
Reference in New Issue