Version 2 (modified by 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)
- Solution_Session DX360.zip (231.0 KB) - added by 7 years ago.
Download all attachments as: .zip