Projects




Version 2 (modified by Lawrence Cabac, 7 years ago) (diff)

--

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	id="WebApp_ID" version="3.0">
	<display-name>xProject</display-name>

	<!-- DB and resource references for required services (Tenant, Connectivity, 
		Password Storage) -->
	<resource-ref>
		<res-ref-name>jdbc/xProjectDB</res-ref-name>
		<res-type>javax.sql.DataSource</res-type>
	</resource-ref>

	<resource-ref>
		<res-ref-name>tenantContext</res-ref-name>
		<res-type>com.sap.cloud.account.TenantContext</res-type>
	</resource-ref>

	<resource-ref>
		<res-ref-name>connectivityConfiguration</res-ref-name>
		<res-type>com.sap.core.connectivity.api.configuration.ConnectivityConfiguration</res-type>
	</resource-ref>

	<!-- TODO: Place cursor below this line and insert Snippet 3 -->
	<resource-ref>
		<res-ref-name>PasswordStorage</res-ref-name>
		<res-type>com.sap.cloud.security.password.PasswordStorage</res-type>
	</resource-ref>

	<!-- Authentication and authorization settings -->
	<login-config>
		<auth-method>FORM</auth-method>
	</login-config>

	<security-constraint>
		<web-resource-collection>
			<web-resource-name>Protected APIs</web-resource-name>
			<url-pattern>/api/v1/*</url-pattern>
		</web-resource-collection>
		<auth-constraint>
			<role-name>ProjectManager</role-name>
			<role-name>ProjectMember</role-name>
		</auth-constraint>
	</security-constraint>

	<security-role>
		<role-name>ProjectManager</role-name>
	</security-role>
	<security-role>
		<role-name>ProjectMember</role-name>
	</security-role>

	<filter>
		<display-name>OAuth scope definition for joining a project</display-name>
		<filter-name>ListProjectsScopeFilter</filter-name>
		<filter-class>com.sap.cloud.security.oauth2.OAuthAuthorizationFilter</filter-class>
		<init-param>
			<param-name>scope</param-name>
			<param-value>list-projects</param-value>
		</init-param>
		<init-param>
			<param-name>http-method</param-name>
			<param-value>GET</param-value>
		</init-param>
		<init-param>
			<param-name>no-session</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>ListProjectsScopeFilter</filter-name>
		<url-pattern>/api/v1/mobile/projects</url-pattern>
	</filter-mapping>

	<filter>
		<display-name>OAuth scope definition for joining a project</display-name>
		<filter-name>JoinProjectScopeFilter</filter-name>
		<filter-class>com.sap.cloud.security.oauth2.OAuthAuthorizationFilter</filter-class>
		<init-param>
			<param-name>scope</param-name>
			<param-value>join-project</param-value>
		</init-param>
		<init-param>
			<param-name>http-method</param-name>
			<param-value>POST</param-value>
		</init-param>
		<init-param>
			<param-name>no-session</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>JoinProjectScopeFilter</filter-name>
		<url-pattern>/api/v1/mobile/projects</url-pattern>
	</filter-mapping>

	<filter>
		<display-name>OAuth scope definition for managing a project member's
			timesheets
		</display-name>
		<filter-name>ManageTimesheetsScopeFilter</filter-name>
		<filter-class>com.sap.cloud.security.oauth2.OAuthAuthorizationFilter</filter-class>
		<init-param>
			<param-name>scope</param-name>
			<param-value>manage-timesheets</param-value>
		</init-param>
		<init-param>
			<param-name>http-method</param-name>
			<param-value>GET POST</param-value>
		</init-param>
		<init-param>
			<param-name>no-session</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>ManageTimesheetsScopeFilter</filter-name>
		<url-pattern>/api/v1/mobile/timesheets</url-pattern>
	</filter-mapping>

	<servlet>
		<servlet-name>TestDataServlet</servlet-name>
		<servlet-class>com.sap.cloud.sample.xproject.servlet.TestDataServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>TestDataServlet</servlet-name>
		<url-pattern>/TestDataServlet</url-pattern>
	</servlet-mapping>

	<filter>
		<filter-name>CsrfFilter</filter-name>
		<filter-class>org.apache.catalina.filters.CsrfPreventionFilter</filter-class>
		<init-param>
      		<param-name>entryPoints</param-name>
      		<param-value>/index.jsp</param-value>
    	</init-param>
	</filter>
	<filter-mapping>
		<filter-name>CsrfFilter</filter-name>
		<url-pattern>/api/v1/web</url-pattern>
	</filter-mapping>

</web-app>
package com.sap.cloud.sample.xproject.web;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sap.cloud.account.TenantContext;
import com.sap.cloud.security.password.PasswordStorage;
import com.sap.cloud.security.password.PasswordStorageException;
import com.sap.core.connectivity.api.configuration.ConnectivityConfiguration;
import com.sap.core.connectivity.api.configuration.DestinationConfiguration;

public class Util {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(Util.class);
	
	private static final String JNDI_KEY_DATA_SOURCE = "java:comp/env/jdbc/xProjectDB";
	private static final String JNDI_KEY_CONNECTIVITY_CONFIG = "java:comp/env/connectivityConfiguration";
	private static final String JNDI_KEY_PASSWORD_STORAGE = "java:comp/env/PasswordStorage";
	private static final String DESTINATION_OAUTHAS_TOKEN = "oauthasTokenEndpoint";
	private static final String DESTINATION_AUTHZ_MGMT = "authzMgmtService";
	private static final String PROPERTY_CLIENTID = "User";
	private static final String PROPERTY_SECRET = "Password";
	private static final String ON_PREMISE_PROXY = "OnPremise";	
	
	// used for user to role assignments
	private static class Role {
		public String applicationName;                        
		public String name;
		public String providerAccount;
    }
	
	private static class Roles {
		public Roles() {
			this.role = new ArrayList<Util.Role>();
		}
		@JsonProperty("roles")
	    public List<Role> role;
		

	}
	
	public static DataSource getDataSource() {
		Object dataSource;
		try {
			InitialContext ctx = new InitialContext();
			dataSource = ctx.lookup(JNDI_KEY_DATA_SOURCE);
		} catch (NamingException e) {
			throw new IllegalStateException("JNDI lookup failure", e);
		}
		if (dataSource instanceof DataSource) {
			return (DataSource) dataSource;
		}
		throw new IllegalStateException(
				"No data source available in JNDI context");
	}
	
	public static String getAccountName() {
		TenantContext context = null;
		try {
			context = (TenantContext) (new InitialContext()).lookup("java:comp/env/tenantContext");
		}
		catch (NamingException ne) {
			LOGGER.error("Failed to get tenant context", ne);
		}
		return context.getTenant().getAccount().getId();
	}
	
    public static Proxy getProxy(String proxyType) {
        String proxyHost = null;
        int proxyPort;
        
        if (ON_PREMISE_PROXY.equals(proxyType)) {
            // Get proxy for on-premise destinations
            proxyHost = System.getenv("HC_OP_HTTP_PROXY_HOST");
            proxyPort = Integer.parseInt(System.getenv("HC_OP_HTTP_PROXY_PORT"));
        } else {
            // Get proxy for internet destinations
            proxyHost = System.getProperty("http.proxyHost");
            proxyPort = Integer.parseInt(System.getProperty("http.proxyPort"));
        }
        return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
    }
    
    public static int assignUserToRole(String userid, String rolename, String accessToken) {	
    	// https://api.hana.ondemand.com/authorization/v1/documentation#accounts__accountName__users_roles_put    	
    	Role newRole = new Role();
    	newRole.applicationName = "xproject";
    	newRole.name = rolename;
    	newRole.providerAccount = getAccountName();
    	
    	Roles roles = new Roles();
    	roles.role.add(newRole);
    	
    	int responseCode = 0;
    	
    	ObjectMapper mapper = new ObjectMapper();
        try {
			String json = mapper.writeValueAsString(roles);
			
			// look up the connectivity configuration API "connectivityConfiguration"
	    	Context ctx = new InitialContext();
	    	ConnectivityConfiguration configuration = (ConnectivityConfiguration) ctx.lookup(JNDI_KEY_CONNECTIVITY_CONFIG);

	    	// get destination configuration for "authzapi"
	    	DestinationConfiguration destConfiguration = configuration.getConfiguration(DESTINATION_AUTHZ_MGMT);

	    	// get destination URL
	    	String url = destConfiguration.getProperty("URL").replace("accountName", Util.getAccountName()).
	    			concat("?userId=" + URLEncoder.encode(userid, StandardCharsets.UTF_8.name()));
	    	LOGGER.debug("Usign URL {}", url);
	    	
	    	// create HTTP Client and PUT request from Apache libs
	    	CloseableHttpClient httpClient = HttpClients.createDefault();
	    	HttpPut httpPut = new HttpPut(url);
	    	
	    	// add API request body
	    	StringEntity requestEntity = new StringEntity(json, ContentType.APPLICATION_JSON);
	    	httpPut.setEntity(requestEntity);
	    	LOGGER.debug("Sending API request body: {}", requestEntity.toString());
	    	
	    	// add the OAuth Access Token to the header
	    	httpPut.addHeader("Authorization", "Bearer " + accessToken);
	    	
	    	try {
		    	// call API
	            LOGGER.debug("Executing request {}", httpPut.getRequestLine());
	            CloseableHttpResponse response = httpClient.execute(httpPut);
	            responseCode = response.getStatusLine().getStatusCode();
	            HttpEntity responseBody = response.getEntity();
	            try {
	            	LOGGER.debug("Response code: {}", responseCode);
	                if (responseCode == HttpURLConnection.HTTP_CREATED) {	               
		                // process response from api 
	                	String apiResponse = EntityUtils.toString(responseBody);
		                LOGGER.debug("Response is: {}", apiResponse);
	                }
	            } finally {
	                // complete response processing
	                EntityUtils.consume(responseBody);
	                response.close();
	            }
	        } catch (ClientProtocolException cpe) {
				LOGGER.error("ClientProtocol Exception: ", cpe);				
	        } 
        	finally {
	            httpClient.close();
	        }			
		} catch (JsonProcessingException jpe) {
			LOGGER.error("Error assigning the role: ", jpe);
		} catch (IOException ioe) {
			LOGGER.error("IO Exception: ", ioe);    		
    	} catch (NamingException ne) {
    		LOGGER.error("JNDI lookup failure", ne);
    	} 
        return responseCode;
    }
    
    public static int unassignUserFromRole(String userid, String rolename, String accessToken) { 	
    	// https://api.hana.ondemand.com/authorization/v1/documentation#accounts__accountName__users_roles_delete
    	int responseCode = 0;
        try {
			// look up the connectivity configuration API "connectivityConfiguration"
	    	Context ctx = new InitialContext();
	    	ConnectivityConfiguration configuration = (ConnectivityConfiguration) ctx.lookup(JNDI_KEY_CONNECTIVITY_CONFIG);

	    	// get destination configuration for "authzapi"
	    	DestinationConfiguration destConfiguration = configuration.getConfiguration(DESTINATION_AUTHZ_MGMT);

	    	// get destination URL
	    	String url = destConfiguration.getProperty("URL").replace("accountName", Util.getAccountName()).
	    			concat("?userId=" + URLEncoder.encode(userid, StandardCharsets.UTF_8.name())).
	    			concat("&roles=" + URLEncoder.encode(rolename + "@" + Util.getAccountName() + ":" + "xproject",StandardCharsets.UTF_8.name()));
	    	LOGGER.debug("Usign URL {}", url);
	    	
	    	// create HTTP Client and DELETE request from Apache libs
	    	CloseableHttpClient httpClient = HttpClients.createDefault();
	    	HttpDelete httpDelete = new HttpDelete(url);
	    	
	    	// add the OAuth Access Token to the header
	    	httpDelete.addHeader("Authorization", "Bearer " + accessToken);
	    	
	    	try {
		    	// call API
	            LOGGER.debug("Executing request {}", httpDelete.getRequestLine());
	            CloseableHttpResponse response = httpClient.execute(httpDelete);
	            responseCode = response.getStatusLine().getStatusCode();
	            try {
	            	HttpEntity responseBody = response.getEntity();
	            	LOGGER.debug("Response code: {}", responseCode);
	                if (responseCode == HttpURLConnection.HTTP_OK) {	               
		                // process response from api 
	                	String apiResponse = EntityUtils.toString(responseBody);
		                LOGGER.debug("Response is: {}", apiResponse);
	                }
	                // complete response processing
	                EntityUtils.consume(responseBody);
	            } finally {
	                response.close();
	            }
	        } catch (ClientProtocolException cpe) {
				LOGGER.error("ClientProtocol Exception: ", cpe);				
	        } 
        	finally {
	            httpClient.close();
	        }			
		} catch (JsonProcessingException jpe) {
			LOGGER.error("Error assigning the role: ", jpe);
		} catch (IOException ioe) {
			LOGGER.error("IO Exception: ", ioe);
    	} catch (NamingException ne) {
    		LOGGER.error("JNDI lookup failure", ne);
    	} 
        return responseCode;
    }
    
    public static String requestNewAccessTokenForPlatformAPI() {
    	String accessToken = null;
    	try {    		    		
	    	// look up the connectivity configuration API "connectivityConfiguration"
	    	Context ctx = new InitialContext();
	    	ConnectivityConfiguration configuration = (ConnectivityConfiguration) ctx.lookup(JNDI_KEY_CONNECTIVITY_CONFIG);

	    	// get destination configuration for "oauthasTokenEndpoint"
	    	DestinationConfiguration destConfiguration = configuration.getConfiguration(DESTINATION_OAUTHAS_TOKEN);
	    		
	    	// get all destination properties
	    	Map<String, String> allDestinationPropeties = destConfiguration.getAllProperties();	    
	    	
	    	// get clientid and secret from destination user and password properties
	    	String clientid = allDestinationPropeties.get(Util.PROPERTY_CLIENTID);
	    	String secret = allDestinationPropeties.get(Util.PROPERTY_SECRET);
	    	
	    	LOGGER.debug("Usign client id {} and secret {}", clientid ,secret);
	    	
	    	// get destination URL
	    	URL url = new URL(allDestinationPropeties.get("URL"));	    	
	    	LOGGER.debug("Usign URL {}", url.toString());
	    	
	    	// create HTTP Client and POST request from Apache libs
	    	CloseableHttpClient httpClient = HttpClients.createDefault();
	    	HttpPost httpPost = new HttpPost(url.toString());
	    	
        	// add OAuth access token request parameters as per https://tools.ietf.org/html/rfc6749#section-4.4.1
        	List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        	nvps.add(new BasicNameValuePair("grant_type", "client_credentials"));
        	
        	httpPost.setEntity(new UrlEncodedFormEntity(nvps));
	    	
	        try {
		    	// create Basic Authn header
		    	UsernamePasswordCredentials creds = 
		    		      new UsernamePasswordCredentials(clientid, secret);
		    	httpPost.addHeader(new BasicScheme().authenticate(creds, httpPost, null));
	        	
	        	// send access token request to SAP CP Neo OAuth 2.0 Authorization Server
	            LOGGER.debug("Executing request {}", httpPost.getRequestLine());
	            CloseableHttpResponse response = httpClient.execute(httpPost);
	            try {
	            	HttpEntity responseBody = response.getEntity();
	            	int responseCode = response.getStatusLine().getStatusCode();
	                LOGGER.debug("Response code: {}", responseCode);
	                if (responseCode == HttpURLConnection.HTTP_OK) {	               
		                // process response from api token endpoint
		                String apiTokenResponse = EntityUtils.toString(responseBody);
		                LOGGER.debug("Response is: {}", apiTokenResponse);
	
		                // read api token response
		                ObjectMapper mapper = new ObjectMapper();
		                JsonNode apiToken = mapper.readTree(apiTokenResponse);
		                
		                // get access token from response
		                accessToken = apiToken.findValue("access_token").textValue();
		                LOGGER.debug("Access token: {}", accessToken);
		                
		                // store access token in SAP CP password storage using the account- and API name as alias
		                setAccessToken(getAccountName(), accessToken.toCharArray());
	                }
	                // complete response processing
	                EntityUtils.consume(responseBody);
	            } finally {
	                response.close();
	            }
	        } catch (ClientProtocolException cpe) {
				LOGGER.error("ClientProtocol Exception: ", cpe);				
	        } catch (AuthenticationException ae) {
	        	LOGGER.error("Authentication Exception: ", ae);
			}
			finally {
	            httpClient.close();
	        }
    	} catch (Exception e) {
			LOGGER.error("Exception: ", e); 
	 	} 
    	return accessToken;
    }
    
    public static String getAccessTokenForPlatformAPI() {
    	String accessToken = null;
    	try {
    		char[] accessTokenChar = getAccessToken(getAccountName());
    		if (accessTokenChar != null) {
    			accessToken = String.valueOf(accessTokenChar);
    		} else {
    			// request a new access token from SAP CP Neo OAuth 2.0 Authorization Server
    			accessToken = requestNewAccessTokenForPlatformAPI();    			
    		}
    	} catch (PasswordStorageException pse) {
    		LOGGER.error("Error retrieving access token from password storage: ", pse); 
    	} catch (NamingException ne) {
    		LOGGER.error("JNDI lookup failure", ne);
    	}
    	return accessToken;
    }
    
    private static PasswordStorage getPasswordStorage() throws NamingException {
        InitialContext ctx = new InitialContext();
        PasswordStorage passwordStorage = (PasswordStorage) ctx.lookup(JNDI_KEY_PASSWORD_STORAGE);
        return passwordStorage;
    }
     
    private static void setAccessToken(String alias, char[] accessToken) throws PasswordStorageException, NamingException {
    	// TODO: Place cursor below this line and insert Snippet 1
		PasswordStorage passwordStorage = getPasswordStorage();
		passwordStorage.setPassword(alias, accessToken);
		LOGGER.debug("Successfully stored access token with account id {} as alias", alias);
    }
     
    private static char[] getAccessToken(String alias) throws PasswordStorageException, NamingException {
    	char[] accessToken = null;
    	// TODO: Place cursor below this line and insert Snippet 2
		PasswordStorage passwordStorage = getPasswordStorage();
		accessToken = passwordStorage.getPassword(alias);
        if (accessToken != null) {
        	LOGGER.debug("Retrieved access token {} for account id {} as alias", String.valueOf(accessToken), alias);
        }
    	return accessToken;
    }
}

DX360

  • Sorter does not use the function
  • use expander parameters after binding (bind)

Attachments (1)

Download all attachments as: .zip