package com.stacksync.syncservice.storage; import java.io.IOException; import org.apache.http.Header; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpHead; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.Period; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import com.google.gson.Gson; import com.stacksync.commons.models.User; import com.stacksync.commons.models.Workspace; import com.stacksync.syncservice.exceptions.storage.EndpointNotFoundException; import com.stacksync.syncservice.exceptions.storage.ObjectNotFoundException; import com.stacksync.syncservice.exceptions.storage.UnauthorizedException; import com.stacksync.syncservice.exceptions.storage.UnexpectedStatusCodeException; import com.stacksync.syncservice.storage.swift.LoginResponseObject; import com.stacksync.syncservice.storage.swift.ServiceObject; import com.stacksync.syncservice.util.Config; public class SwiftManager extends StorageManager { private static StorageManager instance = null; private String authUrl; private String user; private String tenant; private String password; private String storageUrl; private String authToken; private DateTime expirationDate; private SwiftManager() { this.authUrl = Config.getSwiftAuthUrl(); this.user = Config.getSwiftUser(); this.tenant = Config.getSwiftTenant(); this.password = Config.getSwiftPassword(); this.expirationDate = DateTime.now(); } public static synchronized StorageManager getInstance() { if (instance == null) { instance = new SwiftManager(); } return instance; } @Override public void login() throws EndpointNotFoundException, UnauthorizedException, UnexpectedStatusCodeException, IOException { HttpClient httpClient = new DefaultHttpClient(); try { HttpPost request = new HttpPost(authUrl); String body = String .format("{\"auth\": {\"passwordCredentials\": {\"username\": \"%s\", \"password\": \"%s\"}, \"tenantName\":\"%s\"}}", user, password, tenant); StringEntity entity = new StringEntity(body); entity.setContentType("application/json"); request.setEntity(entity); HttpResponse response = httpClient.execute(request); SwiftResponse swiftResponse = new SwiftResponse(response); if (swiftResponse.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { throw new UnauthorizedException("404 User unauthorized"); } if (swiftResponse.getStatusCode() < 200 || swiftResponse.getStatusCode() >= 300) { throw new UnexpectedStatusCodeException("Unexpected status code: " + swiftResponse.getStatusCode()); } String responseBody = swiftResponse.getResponseBodyAsString(); Gson gson = new Gson(); LoginResponseObject loginResponse = gson.fromJson(responseBody, LoginResponseObject.class); this.authToken = loginResponse.getAccess().getToken().getId(); Boolean endpointFound = false; for (ServiceObject service : loginResponse.getAccess().getServiceCatalog()) { if (service.getType().equals("object-store")) { this.storageUrl = service.getEndpoints().get(0).getPublicURL(); endpointFound = true; break; } } // get the token issue swift date DateTimeZone.setDefault(DateTimeZone.UTC); DateTimeFormatter dateStringFormat = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS"); DateTime issuedAt = dateStringFormat.parseDateTime(loginResponse.getAccess().getToken().getIssuedAt()); // get the token expiration swift date dateStringFormat = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssZ"); DateTime expiresAt = dateStringFormat.parseDateTime(loginResponse.getAccess().getToken().getExpires()); // calculate the period between these two dates and add it to our // current time because datetime can differ from Swift and this // device Period period = new Period(issuedAt, expiresAt); expirationDate = DateTime.now().plus(period); if (!endpointFound) { throw new EndpointNotFoundException(); } } finally { httpClient.getConnectionManager().shutdown(); } } @Override public void createNewWorkspace(Workspace workspace) throws Exception { if (!isTokenActive()) { login(); } HttpClient httpClient = new DefaultHttpClient(); String url = this.storageUrl + "/" + workspace.getSwiftContainer(); try { HttpPut request = new HttpPut(url); request.setHeader(SwiftResponse.X_AUTH_TOKEN, authToken); HttpResponse response = httpClient.execute(request); SwiftResponse swiftResponse = new SwiftResponse(response); if (swiftResponse.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { throw new UnauthorizedException("401 User unauthorized"); } if (swiftResponse.getStatusCode() < 200 || swiftResponse.getStatusCode() >= 300) { throw new UnexpectedStatusCodeException("Unexpected status code: " + swiftResponse.getStatusCode()); } } finally { httpClient.getConnectionManager().shutdown(); } } @Override public void removeUserToWorkspace(User owner, User user, Workspace workspace) throws Exception { if (!isTokenActive()) { login(); } String permissions = getWorkspacePermissions(owner, workspace); String tenantUser = Config.getSwiftTenant() + ":" + user.getSwiftUser(); if (permissions.contains("," + tenantUser)) { permissions.replace("," + tenantUser, ""); } else if (permissions.contains(tenantUser)) { permissions.replace(tenantUser, ""); } else { return; } HttpClient httpClient = new DefaultHttpClient(); String url = this.storageUrl + "/" + workspace.getSwiftContainer(); try { HttpPut request = new HttpPut(url); request.setHeader(SwiftResponse.X_AUTH_TOKEN, authToken); request.setHeader(SwiftResponse.X_CONTAINER_READ, permissions); request.setHeader(SwiftResponse.X_CONTAINER_WRITE, permissions); HttpResponse response = httpClient.execute(request); SwiftResponse swiftResponse = new SwiftResponse(response); if (swiftResponse.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { throw new UnauthorizedException("404 User unauthorized"); } if (swiftResponse.getStatusCode() < 200 || swiftResponse.getStatusCode() >= 300) { throw new UnexpectedStatusCodeException("Unexpected status code: " + swiftResponse.getStatusCode()); } } finally { httpClient.getConnectionManager().shutdown(); } } @Override public void grantUserToWorkspace(User owner, User user, Workspace workspace) throws Exception { if (!isTokenActive()) { login(); } String permissions = getWorkspacePermissions(owner, workspace); String tenantUser = Config.getSwiftTenant() + ":" + user.getSwiftUser(); if (permissions.contains(tenantUser)) { return; } permissions += "," + tenantUser; HttpClient httpClient = new DefaultHttpClient(); String url = this.storageUrl + "/" + workspace.getSwiftContainer(); try { HttpPut request = new HttpPut(url); request.setHeader(SwiftResponse.X_AUTH_TOKEN, authToken); request.setHeader(SwiftResponse.X_CONTAINER_READ, permissions); request.setHeader(SwiftResponse.X_CONTAINER_WRITE, permissions); HttpResponse response = httpClient.execute(request); SwiftResponse swiftResponse = new SwiftResponse(response); if (swiftResponse.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { throw new UnauthorizedException("404 User unauthorized"); } if (swiftResponse.getStatusCode() < 200 || swiftResponse.getStatusCode() >= 300) { throw new UnexpectedStatusCodeException("Unexpected status code: " + swiftResponse.getStatusCode()); } } finally { httpClient.getConnectionManager().shutdown(); } } @Override public void copyChunk(Workspace sourceWorkspace, Workspace destinationWorkspace, String chunkName) throws Exception { if (!isTokenActive()) { login(); } HttpClient httpClient = new DefaultHttpClient(); String url = this.storageUrl + "/" + destinationWorkspace.getSwiftContainer() + "/" + chunkName; String copyFrom = "/" + sourceWorkspace.getSwiftContainer() + "/" + chunkName; try { HttpPut request = new HttpPut(url); request.setHeader(SwiftResponse.X_AUTH_TOKEN, authToken); request.setHeader(SwiftResponse.X_COPY_FROM, copyFrom); //request.setHeader("Content-Length", "0"); HttpResponse response = httpClient.execute(request); SwiftResponse swiftResponse = new SwiftResponse(response); if (swiftResponse.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { throw new UnauthorizedException("401 User unauthorized"); } if (swiftResponse.getStatusCode() == HttpStatus.SC_NOT_FOUND) { throw new ObjectNotFoundException("404 Not Found"); } if (swiftResponse.getStatusCode() < 200 || swiftResponse.getStatusCode() >= 300) { throw new UnexpectedStatusCodeException("Unexpected status code: " + swiftResponse.getStatusCode()); } } finally { httpClient.getConnectionManager().shutdown(); } } @Override public void deleteChunk(Workspace workspace, String chunkName) throws Exception { if (!isTokenActive()) { login(); } HttpClient httpClient = new DefaultHttpClient(); String url = this.storageUrl + "/" + workspace.getSwiftContainer() + "/" + chunkName; try { HttpDelete request = new HttpDelete(url); request.setHeader(SwiftResponse.X_AUTH_TOKEN, authToken); HttpResponse response = httpClient.execute(request); SwiftResponse swiftResponse = new SwiftResponse(response); if (swiftResponse.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { throw new UnauthorizedException("401 User unauthorized"); } if (swiftResponse.getStatusCode() == HttpStatus.SC_NOT_FOUND) { throw new ObjectNotFoundException("404 Not Found"); } if (swiftResponse.getStatusCode() < 200 || swiftResponse.getStatusCode() >= 300) { throw new UnexpectedStatusCodeException("Unexpected status code: " + swiftResponse.getStatusCode()); } } finally { httpClient.getConnectionManager().shutdown(); } } @Override public void deleteWorkspace(Workspace workspace) throws Exception { if (!isTokenActive()) { login(); } HttpClient httpClient = new DefaultHttpClient(); String url = this.storageUrl + "/" + workspace.getSwiftContainer(); try { HttpDelete request = new HttpDelete(url); request.setHeader(SwiftResponse.X_AUTH_TOKEN, authToken); HttpResponse response = httpClient.execute(request); SwiftResponse swiftResponse = new SwiftResponse(response); if (swiftResponse.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { throw new UnauthorizedException("401 User unauthorized"); } if (swiftResponse.getStatusCode() < 200 || swiftResponse.getStatusCode() >= 300) { throw new UnexpectedStatusCodeException("Unexpected status code: " + swiftResponse.getStatusCode()); } } finally { httpClient.getConnectionManager().shutdown(); } } private String getWorkspacePermissions(User user, Workspace workspace) throws Exception { if (!isTokenActive()) { login(); } HttpClient httpClient = new DefaultHttpClient(); String url = this.storageUrl + "/" + workspace.getSwiftContainer(); try { HttpHead request = new HttpHead(url); request.setHeader(SwiftResponse.X_AUTH_TOKEN, authToken); HttpResponse response = httpClient.execute(request); SwiftResponse swiftResponse = new SwiftResponse(response); if (swiftResponse.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { throw new UnauthorizedException("404 User unauthorized"); } if (swiftResponse.getStatusCode() < 200 || swiftResponse.getStatusCode() >= 300) { throw new UnexpectedStatusCodeException("Unexpected status code: " + swiftResponse.getStatusCode()); } // We suppose there are the same permissions for read and write Header containerWriteHeader = swiftResponse.getResponseHeader(SwiftResponse.X_CONTAINER_WRITE); if (containerWriteHeader == null) { return ""; } return containerWriteHeader.getValue(); } finally { httpClient.getConnectionManager().shutdown(); } } private boolean isTokenActive() { return DateTime.now().isBefore(expirationDate); } }