Upgrade to keystone v3

Implements: blueprint keystone-v3
Closes-Bug: #1671479
Change-Id: Ic168c652dceb93e820f053e406e992c7dc161573
This commit is contained in:
Honza Pokorny 2017-03-28 15:20:35 -03:00
parent 76e5d143df
commit 89e063f932
12 changed files with 111 additions and 57 deletions

View File

@ -4,12 +4,12 @@ window.tripleOUiConfig = {
// A valid Keystone service URL is required. The other endpoints
// will then be obtained automatically from the Keystone catalog.
//
// 'keystone': 'http://192.0.2.1:5000/v2.0',
// 'keystone': 'http://192.0.2.1:5000/v3',
//
// 'heat': 'http://192.0.2.1:8004/v1/%(tenant_id)s',
// 'heat': 'http://192.0.2.1:8004/v1/%(project_id)s',
// 'ironic': 'http://192.0.2.1:6385',
// 'mistral': 'http://192.0.2.1:8989/v2',
// 'swift': 'http://192.0.2.1:8080/v1/AUTH_%(tenant_id)s',
// 'swift': 'http://192.0.2.1:8080/v1/AUTH_%(project_id)s',
// 'zaqar-websocket': 'ws://192.0.2.1:9000',
// Default websocket queue name

View File

@ -0,0 +1,5 @@
---
fixes:
- |
Fixes `bug 1671479 <https://launchpad.net/bugs/1671479>`__
Upgrade the UI to use keystone v3

View File

@ -6,20 +6,19 @@ import store from '../../js/store';
describe('utility functions', () => {
const appState = {
login: new InitialLoginState({
keystoneAccess: Map({
token: Map({
id: 123456,
tenant: Map({
id: 778899
})
token: Map({
id: 123456,
project: Map({
id: 778899
}),
serviceCatalog: List([
catalog: List([
Map({
name: 'nova',
endpoints: List([
Map({
adminURL: 'http://someNovaAdminUrl',
publicURL: 'http://someNovaPublicUrl'
id: '1',
interface: 'public',
url: 'http://someNovaPublicUrl'
})
])
}),
@ -27,7 +26,9 @@ describe('utility functions', () => {
name: 'fooservice',
endpoints: List([
Map({
publicURL: 'http://IGNOREDFooPublicUrl'
id: '1',
interface: 'public',
url: 'http://someNovaPublicUrl'
})
])
}),
@ -35,7 +36,9 @@ describe('utility functions', () => {
name: 'macroservice',
endpoints: List([
Map({
publicURL: 'http://MacroPublicUrl/v1/Foo_%(tenant_id)s'
id: '1',
interface: 'public',
url: 'http://MacroPublicUrl/v1/Foo_%(project_id)s'
})
])
})
@ -68,7 +71,7 @@ describe('utility functions', () => {
).toEqual('http://FooPublicURL');
});
it('expands urls containing the keystone tenant macro', () => {
it('expands urls containing the keystone project macro', () => {
expect(getServiceUrl('macroservice')).toEqual('http://MacroPublicUrl/v1/Foo_778899');
});
});

View File

@ -12,11 +12,12 @@ export default {
authenticateUserViaToken(keystoneAuthTokenId, nextPath) {
return (dispatch, getState) => {
dispatch(this.userAuthStarted());
KeystoneApiService.authenticateUserViaToken(keystoneAuthTokenId).then((response) => {
cookie.save('keystoneAuthTokenId',
response.access.token.id,
{ path: '/' });
dispatch(this.userAuthSuccess(response.access));
KeystoneApiService.authenticateUserViaToken(keystoneAuthTokenId).then((result) => {
const tokenId = result.request.getResponseHeader('X-Subject-Token');
let response = result.response;
response.token.id = tokenId;
cookie.save('keystoneAuthTokenId', tokenId, { path: '/' });
dispatch(this.userAuthSuccess(response.token));
ZaqarWebSocketService.init(getState, dispatch);
browserHistory.push(nextPath);
}).catch((error) => {
@ -32,11 +33,12 @@ export default {
authenticateUser(formData, formFields, nextPath) {
return (dispatch, getState) => {
dispatch(this.userAuthStarted());
KeystoneApiService.authenticateUser(formData.username, formData.password).then((response) => {
cookie.save('keystoneAuthTokenId',
response.access.token.id,
{ path: '/' });
dispatch(this.userAuthSuccess(response.access));
KeystoneApiService.authenticateUser(formData.username, formData.password).then((result) => {
const tokenId = result.request.getResponseHeader('X-Subject-Token');
let response = result.response;
response.token.id = tokenId;
cookie.save('keystoneAuthTokenId', tokenId, { path: '/' });
dispatch(this.userAuthSuccess(response.token));
ZaqarWebSocketService.init(getState, dispatch);
browserHistory.push(nextPath);
}).catch((error) => {
@ -64,10 +66,10 @@ export default {
};
},
userAuthSuccess(keystoneAccess) {
userAuthSuccess(token) {
return {
type: LoginConstants.USER_AUTH_SUCCESS,
payload: fromJS(keystoneAccess)
payload: fromJS(token)
};
},

View File

@ -69,7 +69,7 @@ const mapStateToProps = state => {
currentPlanName: state.currentPlan.currentPlanName,
noPlans: state.plans.get('all').isEmpty(),
plansLoaded: state.plans.get('plansLoaded'),
user: state.login.getIn(['keystoneAccess', 'user'])
user: state.login.getIn(['token', 'user'])
};
};

View File

@ -70,7 +70,7 @@ export default class NavBar extends React.Component {
<li>
<a id="NavBar__username">
<span className="pficon pficon-user"></span>
{this.props.user.get('username')}
{this.props.user.get('name')}
</a>
</li>
{this._renderLanguageDropdown()}

View File

@ -1,6 +1,6 @@
import { getAppConfig } from '../services/utils';
let HOST = location.protocol + '//' + location.hostname;
let KEYSTONE_URL = getAppConfig().keystone || HOST + ':5000/v2.0';
let KEYSTONE_URL = getAppConfig().keystone || HOST + ':5000/v3';
export const AUTH_URL = KEYSTONE_URL + '/tokens';
export const AUTH_URL = KEYSTONE_URL + '/auth/tokens';

View File

@ -1,7 +1,7 @@
import { List, Map, Record } from 'immutable';
export const InitialLoginState = Record({
keystoneAccess: Map(),
token: Map(),
loginForm: Map({
formErrors: List(),
formFieldErrors: Map()

View File

@ -10,7 +10,7 @@ export default function loginReducer(state = initialState, action) {
return state.set('isAuthenticating', true);
case LoginConstants.USER_AUTH_SUCCESS:
return state.set('keystoneAccess', action.payload)
return state.set('token', action.payload)
.set('isAuthenticating', false)
.set('isAuthenticated', true);

View File

@ -16,30 +16,74 @@ class KeystoneApiService {
}
authenticateUser(username, password) {
return when(request(this.defaultRequest({
let req = request(this.defaultRequest({
data: JSON.stringify({
auth: {
tenantName: 'admin',
passwordCredentials: {
username: username,
password: password
identity: {
methods: ['password'],
password: {
user: {
name: username,
domain: {
name: 'Default'
},
password: password
}
}
},
scope: {
project: {
name: 'admin',
domain: {
name: 'Default'
}
}
}
}
})
})));
}));
// We're passing the req object to the next handler in the chain so that we
// can inspect response headers later.
return when(req, (response) => {
return {
request: req.request,
response
};
});
}
authenticateUserViaToken(keystoneAuthTokenId) {
return when(request(this.defaultRequest({
let req = request(this.defaultRequest({
data: JSON.stringify({
auth: {
tenantName: 'admin',
token: {
id: keystoneAuthTokenId
identity: {
methods: ['token'],
token: {
id: keystoneAuthTokenId
}
},
scope: {
project: {
name: 'admin',
domain: {
name: 'Default'
}
}
}
}
})
})));
}));
// We're passing the req object to the next handler in the chain so that we
// can inspect response headers later.
return when(req, (response) => {
return {
request: req.request,
response
};
});
}
}

View File

@ -1,7 +1,7 @@
import uuid from 'node-uuid';
import when from 'when';
import { getAuthTokenId, getTenantId, getServiceUrl } from './utils';
import { getAuthTokenId, getProjectId, getServiceUrl } from './utils';
import { ZAQAR_DEFAULT_QUEUE } from '../constants/ZaqarConstants';
import ZaqarActions from '../actions/ZaqarActions';
import NotificationActions from '../actions/NotificationActions';
@ -42,7 +42,7 @@ export default {
headers: {
'X-Auth-Token': getAuthTokenId(),
'Client-ID': this.clientID,
'X-Project-ID': getTenantId()
'X-Project-ID': getProjectId()
}
};
this.socket.send(JSON.stringify(message));
@ -53,7 +53,7 @@ export default {
action: action,
headers: {
'Client-ID': this.clientID,
'X-Project-ID': getTenantId()
'X-Project-ID': getProjectId()
},
body: body
};

View File

@ -8,32 +8,32 @@ import store from '../store';
* It gives precedence to urls stored in the app.conf file over
* the ones exposed through the serviceCatalog.
*/
export function getServiceUrl(serviceName, urlType='publicURL', appConfig=getAppConfig()) {
export function getServiceUrl(serviceName, urlType='public', appConfig=getAppConfig()) {
let serviceUrl = appConfig[serviceName] || getFromServiceCatalog(serviceName, urlType);
if(!serviceUrl) {
throw Error(`URL for service ${serviceName} can not be found`);
}
let tenantId = getTenantId();
return serviceUrl.replace('%(tenant_id)s', tenantId);
return serviceUrl.replace('%(project_id)s', getProjectId());
}
function getFromServiceCatalog(serviceName, urlType) {
let endpoint = store.getState().login
.getIn(['keystoneAccess', 'serviceCatalog'], List())
return store.getState().login
.getIn(['token', 'catalog'], List())
.find(service => service.get('name') === serviceName, null, Map())
.get('endpoints', List()).first();
return endpoint ? endpoint.get(urlType) : undefined;
.get('endpoints', List())
.find(endpoint => endpoint.get('interface') === urlType, null, Map())
.get('url');
}
/**
* Returns Keystone Auth Token ID
*/
export function getAuthTokenId() {
return store.getState().login.getIn(['keystoneAccess', 'token', 'id']);
return store.getState().login.getIn(['token', 'id']);
}
export function getTenantId() {
return store.getState().login.getIn(['keystoneAccess', 'token', 'tenant', 'id']);
export function getProjectId() {
return store.getState().login.getIn(['token', 'project', 'id']);
}
export function getAppConfig() {