
org.xlcloud.openstack.client.OpenStackKeystoneClient Maven / Gradle / Ivy
/*
* Copyright 2012 AMG.lab, a Bull Group Company
*
* 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 org.xlcloud.openstack.client;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.List;
import javax.ws.rs.core.MediaType;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.xlcloud.logging.LoggingUtils;
import org.xlcloud.openstack.api.IdentityManagementClient;
import org.xlcloud.openstack.api.identity.IdentityAdministrationEndpoint;
import org.xlcloud.openstack.api.identity.UserResource;
import org.xlcloud.openstack.api.identity.UsersResource;
import org.xlcloud.openstack.model.exceptions.OpenStackAuthenticationException;
import org.xlcloud.openstack.model.exceptions.OpenStackNotFoundException;
import org.xlcloud.openstack.model.identity.Access;
import org.xlcloud.openstack.model.identity.Role;
import org.xlcloud.openstack.model.identity.RoleList;
import org.xlcloud.openstack.model.identity.Tenant;
import org.xlcloud.openstack.model.identity.TenantList;
import org.xlcloud.openstack.model.identity.Token;
import org.xlcloud.openstack.model.identity.User;
import org.xlcloud.openstack.model.identity.UserForCreate;
import org.xlcloud.openstack.model.identity.UserList;
import org.xlcloud.openstack.model.identity.keystone.KeystoneAccess;
import org.xlcloud.openstack.model.identity.keystone.KeystoneAuthentication;
import org.xlcloud.openstack.rest.PerformanceMonitorFilter;
import org.xlcloud.rest.client.config.ClientLoggingFilter;
import com.sun.jersey.api.client.Client;
/**
* This class is the main implementation of the {@link IdentityManagementClient}
* . As there is no public constructor of this class, the only way to retrieve
* the instance of this class is by invoking one of static methods getInstance.
* To see the difference between them, compare
* {@link OpenStackKeystoneClient#getInstance(String,String,String,String)} and
* {@link OpenStackKeystoneClient#getInstance(Token, String)}.
*
* @author Andrzej Stasiak, AMG.net
*/
public class OpenStackKeystoneClient implements IdentityManagementClient {
private static final Logger LOG = Logger.getLogger(OpenStackKeystoneClient.class);
private final String resourceUri;
private final Client client;
private String tokenId;
private OpenStackKeystoneClient( String endpoint ) {
this.client = KeystoneRestClientProducer.INSTANCE.produceClient();
this.resourceUri = endpoint;
LOG.debug("Using Jersey client " + client.hashCode());
}
/**
* This method creates and returns new object of OpenStackClient class. It
* takes as parameters following items: user credentials (username and
* password), name of the tenant the user should be logged on (one user may
* belong to many tenants, and their privileges may differ between tenants)
* and endpoint URI indicating the location of Keystone server (including
* port number).
*
* The return value is not instance of OpenStackClient, but instead
* {@link java.lang.reflect.Proxy} with {@link RefreshAuthenticationHandler}
* . Note that, therefore, authentication on openstack is not performed at
* the time of creating the client, but during the first request. This
* behaviour is different than behavior of
* {@link OpenStackKeystoneClient#getInstance(String, String)}.
*
*
* @param username
* name of user to be logged as
* @param password
* password of user to be logged as
* @param tenantName
* name of tenant to be logged in
* @param keystoneEndpointUri
* an absolute URI to Keystone server
* @return instance of OpenStackClient
*/
public static IdentityManagementClient getKeystoneClientInstance(String username, String password, String tenantName, String endpointUri) {
if (LOG.isDebugEnabled()) {
LOG.debug("Creating new OpenStackClient for user " + username + " on tenant " + tenantName + ". Endpoint URI: " + endpointUri);
}
OpenStackKeystoneClient osClient = new OpenStackKeystoneClient(endpointUri);
osClient.setFilters();
InvocationHandler handler = new RefreshAuthenticationHandler(osClient, username, password, tenantName);
IdentityManagementClient imClient = (IdentityManagementClient) Proxy.newProxyInstance(
OpenStackKeystoneClient.class.getClassLoader(), new Class[] { IdentityManagementClient.class }, handler);
return imClient;
}
/**
* This method creates and returns new object of OpenStackClient class. It
* takes as parameters keystone token and endpoint URI indicating the
* location of Keystone server (including port number). In contrary to
* {@link OpenStackKeystoneClient#getInstance(String,String,String,String)}
* this method returns real OpenStackClient object, without proxy.
*
* This is not default way of obtaining OpenStackClient. You should use it
* only when you want to connect with existing Keystone session (e.g. super
* as Keystone superuser, who has fixed token and no user credentials).
*
* When given endpointUri does not indicate Keystone server,
* {@link OpenStackNotFoundException} is thrown. If given token id is not
* valid, {@link OpenStackAuthenticationException} is thrown.
*
* As an implementation of {@link IdentityManagementClient} it uses set of
* implementations of identity interfaces (package
* org.xlcloud.openstack.model.identity) from package
* org.xlcloud.openstack.model.identity.keystone.
*
* @param token
* Keystone token id
* @param endpointUri
* an absolute URI to Keystone server
* @return
*/
public static IdentityManagementClient getKeystoneClientInstance(String token, String endpointUri) {
if (LOG.isDebugEnabled()) {
LOG.debug("Creating new OpenStackClient with token: " + LoggingUtils.maskPartially(token) + ". Endpoint URI: " + endpointUri);
}
OpenStackKeystoneClient osClient = new OpenStackKeystoneClient(endpointUri);
osClient.setToken(token);
return osClient;
}
/** {@inheritDoc} */
public void reauthenticate(String username, String password, String tenantName) {
if (isTokenValid(tokenId)) {
// throw new OpenStackAuthenticationException("Current token is valid");
LOG.warn("Current token is valid, but we'll reauthenticate the user anyway.");
}
KeystoneAuthentication authentication = KeystoneAuthentication.withPasswordCredentials(username, password);
if (StringUtils.isNotBlank(tenantName)) {
authentication.setTenantName(tenantName);
}
Access access = client.resource(resourceUri + "/tokens").type(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON)
.post(KeystoneAccess.class, authentication);
tokenId = access.getToken().getId();
setFilters();
}
/**
* This method should not be invoked directly, it is invoked by
* RefreshAuthenticationHandler.
*
* @param id
* token id to be used
*/
void setToken(String id) {
this.tokenId = id;
setFilters();
}
/**
* This method is made for testing purposes. It should not be invoked when
* using OpenStackClient.
*
* @return token of current Keystone session.
*/
@Override
public String getToken() {
return this.tokenId;
}
private void setFilters() {
client.removeAllFilters();
if (tokenId != null) {
client.addFilter(new AuthFilter(tokenId));
}
client.addFilter(new ClientLoggingFilter());
client.addFilter(new ExceptionHandlingFilter());
client.addFilter(new PerformanceMonitorFilter());
}
private IdentityAdministrationEndpoint getIdentityAdministationEndpoint() {
return new IdentityAdministrationEndpoint(client, resourceUri);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isTokenValid(String token) {
if (StringUtils.isBlank(token)) {
return false;
}
try {
String digest = DigestUtils.md5Hex(token);
LOG.debug("Transformed token [" + LoggingUtils.maskPartially(token) + "] to digest [" + digest + "]");
getIdentityAdministationEndpoint().tokens().token(digest).get();
} catch (OpenStackAuthenticationException | OpenStackNotFoundException ex) {
return false;
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public UserList getUserResources() {
return getIdentityAdministationEndpoint().users().get();
}
/**
* {@inheritDoc}
*/
@Override
public User getUserById(String id) {
return getIdentityAdministationEndpoint().users().user(id).get();
}
/**
* {@inheritDoc}
*/
@Override
public User getUserByName(String name) throws OpenStackNotFoundException {
return getIdentityAdministationEndpoint().users().getUserByName(name);
}
/**
* {@inheritDoc}
*/
@Override
public void deleteUserById(String id) {
getIdentityAdministationEndpoint().users().user(id).delete();
}
/**
* {@inheritDoc}
*/
@Override
public User createUser(UserForCreate user) {
IdentityAdministrationEndpoint identityAdministationEndpoint = getIdentityAdministationEndpoint();
UsersResource users = identityAdministationEndpoint.users();
return users.post(user);
}
/**
* {@inheritDoc}
*/
@Override
public User updateUser(UserForCreate user) {
IdentityAdministrationEndpoint identityAdministationEndpoint = getIdentityAdministationEndpoint();
UserResource userRespource = identityAdministationEndpoint.users().user(user.getId());
return userRespource.put(user);
}
/**
* {@inheritDoc}
*/
@Override
public Tenant createTentant(Tenant tenant) {
return getIdentityAdministationEndpoint().tenants().post(tenant);
}
/**
* {@inheritDoc}
*/
@Override
public Role createRole(Role role) {
return getIdentityAdministationEndpoint().roles().post(role);
}
/**
* {@inheritDoc}
*/
@Override
public TenantList getTenantResources() {
return getIdentityAdministationEndpoint().tenants().get();
}
/**
* {@inheritDoc}
*/
@Override
public Role assignRole(String userId, String roleId, String tenantId) {
return getIdentityAdministationEndpoint().tenants().tenant(tenantId).users().user(userId).roles().role(roleId).put();
}
/**
* {@inheritDoc}
*/
@Override
public void removeRole(String userId, String roleId, String tenantId) {
getIdentityAdministationEndpoint().tenants().tenant(tenantId).users().user(userId).roles().role(roleId).delete();
}
/**
* {@inheritDoc}
*/
@Override
public RoleList getRoleResources() {
return getIdentityAdministationEndpoint().roles().get();
}
/**
* {@inheritDoc}
*/
@Override
public Tenant getTenantByName(String name) throws OpenStackNotFoundException {
return getIdentityAdministationEndpoint().tenants().getTenantByName(name);
}
/**
* {@inheritDoc}
*/
@Override
public Role getRoleByName(String name) throws OpenStackNotFoundException {
/*
* According to the documentation
* (http://docs.openstack.org/api/openstack
* -identity-service/2.0/content/
* Admin_API_Service_Developer_Operations-d1e1357.html), it should be
* possible to get role by name in the same manner as in getUserByName()
* and getTentantByName(). However for some reason the "name" parameter
* is ignored and the whole list is returned.
*/
RoleList roleList = getRoleResources();
if (roleList != null) {
List roles = roleList.getList();
for (Role role : roles) {
if (name.equals(role.getName())) {
return role;
}
}
}
throw new OpenStackNotFoundException("Could not find role: " + name);
}
/**
* {@inheritDoc}
*/
@Override
public Tenant getTenantById(String id) {
return getIdentityAdministationEndpoint().tenants().tenant(id).get();
}
@Override
public void removeTenant(String id) {
getIdentityAdministationEndpoint().tenants().tenant(id).delete();
}
/** {@inheritDoc} */
@Override
public RoleList getUserRolesInTenant(String userId, String tenantId) {
return getIdentityAdministationEndpoint().tenants().tenant(tenantId).users().user(userId).roles().get();
}
/** {@inheritDoc} */
@Override
public void removeAllUserRolesInTenant(String userId, String tenantId) {
RoleList roleList = getUserRolesInTenant(userId, tenantId);
if (roleList == null) {
return;
}
for (Role role : roleList.getList()) {
removeRole(userId, role.getId(), tenantId);
}
}
}