Projects



Changes between Initial Version and Version 1 of TechEd


Ignore:
Timestamp:
Nov 16, 2017, 3:56:21 PM (7 years ago)
Author:
Lawrence Cabac
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • TechEd

    v1 v1  
     1
     2{{{
     3<?xml version="1.0" encoding="UTF-8"?>
     4<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     5        xmlns="http://java.sun.com/xml/ns/javaee"
     6        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
     7        id="WebApp_ID" version="3.0">
     8        <display-name>xProject</display-name>
     9
     10        <!-- DB and resource references for required services (Tenant, Connectivity,
     11                Password Storage) -->
     12        <resource-ref>
     13                <res-ref-name>jdbc/xProjectDB</res-ref-name>
     14                <res-type>javax.sql.DataSource</res-type>
     15        </resource-ref>
     16
     17        <resource-ref>
     18                <res-ref-name>tenantContext</res-ref-name>
     19                <res-type>com.sap.cloud.account.TenantContext</res-type>
     20        </resource-ref>
     21
     22        <resource-ref>
     23                <res-ref-name>connectivityConfiguration</res-ref-name>
     24                <res-type>com.sap.core.connectivity.api.configuration.ConnectivityConfiguration</res-type>
     25        </resource-ref>
     26
     27        <!-- TODO: Place cursor below this line and insert Snippet 3 -->
     28        <resource-ref>
     29                <res-ref-name>PasswordStorage</res-ref-name>
     30                <res-type>com.sap.cloud.security.password.PasswordStorage</res-type>
     31        </resource-ref>
     32
     33        <!-- Authentication and authorization settings -->
     34        <login-config>
     35                <auth-method>FORM</auth-method>
     36        </login-config>
     37
     38        <security-constraint>
     39                <web-resource-collection>
     40                        <web-resource-name>Protected APIs</web-resource-name>
     41                        <url-pattern>/api/v1/*</url-pattern>
     42                </web-resource-collection>
     43                <auth-constraint>
     44                        <role-name>ProjectManager</role-name>
     45                        <role-name>ProjectMember</role-name>
     46                </auth-constraint>
     47        </security-constraint>
     48
     49        <security-role>
     50                <role-name>ProjectManager</role-name>
     51        </security-role>
     52        <security-role>
     53                <role-name>ProjectMember</role-name>
     54        </security-role>
     55
     56        <filter>
     57                <display-name>OAuth scope definition for joining a project</display-name>
     58                <filter-name>ListProjectsScopeFilter</filter-name>
     59                <filter-class>com.sap.cloud.security.oauth2.OAuthAuthorizationFilter</filter-class>
     60                <init-param>
     61                        <param-name>scope</param-name>
     62                        <param-value>list-projects</param-value>
     63                </init-param>
     64                <init-param>
     65                        <param-name>http-method</param-name>
     66                        <param-value>GET</param-value>
     67                </init-param>
     68                <init-param>
     69                        <param-name>no-session</param-name>
     70                        <param-value>true</param-value>
     71                </init-param>
     72        </filter>
     73        <filter-mapping>
     74                <filter-name>ListProjectsScopeFilter</filter-name>
     75                <url-pattern>/api/v1/mobile/projects</url-pattern>
     76        </filter-mapping>
     77
     78        <filter>
     79                <display-name>OAuth scope definition for joining a project</display-name>
     80                <filter-name>JoinProjectScopeFilter</filter-name>
     81                <filter-class>com.sap.cloud.security.oauth2.OAuthAuthorizationFilter</filter-class>
     82                <init-param>
     83                        <param-name>scope</param-name>
     84                        <param-value>join-project</param-value>
     85                </init-param>
     86                <init-param>
     87                        <param-name>http-method</param-name>
     88                        <param-value>POST</param-value>
     89                </init-param>
     90                <init-param>
     91                        <param-name>no-session</param-name>
     92                        <param-value>true</param-value>
     93                </init-param>
     94        </filter>
     95        <filter-mapping>
     96                <filter-name>JoinProjectScopeFilter</filter-name>
     97                <url-pattern>/api/v1/mobile/projects</url-pattern>
     98        </filter-mapping>
     99
     100        <filter>
     101                <display-name>OAuth scope definition for managing a project member's
     102                        timesheets
     103                </display-name>
     104                <filter-name>ManageTimesheetsScopeFilter</filter-name>
     105                <filter-class>com.sap.cloud.security.oauth2.OAuthAuthorizationFilter</filter-class>
     106                <init-param>
     107                        <param-name>scope</param-name>
     108                        <param-value>manage-timesheets</param-value>
     109                </init-param>
     110                <init-param>
     111                        <param-name>http-method</param-name>
     112                        <param-value>GET POST</param-value>
     113                </init-param>
     114                <init-param>
     115                        <param-name>no-session</param-name>
     116                        <param-value>true</param-value>
     117                </init-param>
     118        </filter>
     119        <filter-mapping>
     120                <filter-name>ManageTimesheetsScopeFilter</filter-name>
     121                <url-pattern>/api/v1/mobile/timesheets</url-pattern>
     122        </filter-mapping>
     123
     124        <servlet>
     125                <servlet-name>TestDataServlet</servlet-name>
     126                <servlet-class>com.sap.cloud.sample.xproject.servlet.TestDataServlet</servlet-class>
     127        </servlet>
     128        <servlet-mapping>
     129                <servlet-name>TestDataServlet</servlet-name>
     130                <url-pattern>/TestDataServlet</url-pattern>
     131        </servlet-mapping>
     132
     133        <filter>
     134                <filter-name>CsrfFilter</filter-name>
     135                <filter-class>org.apache.catalina.filters.CsrfPreventionFilter</filter-class>
     136                <init-param>
     137                <param-name>entryPoints</param-name>
     138                <param-value>/index.jsp</param-value>
     139        </init-param>
     140        </filter>
     141        <filter-mapping>
     142                <filter-name>CsrfFilter</filter-name>
     143                <url-pattern>/api/v1/web</url-pattern>
     144        </filter-mapping>
     145
     146</web-app>
     147}}}
     148
     149{{{
     150
     151package com.sap.cloud.sample.xproject.web;
     152
     153import java.io.IOException;
     154import java.net.HttpURLConnection;
     155import java.net.InetSocketAddress;
     156import java.net.Proxy;
     157import java.net.URL;
     158import java.net.URLEncoder;
     159import java.nio.charset.StandardCharsets;
     160import java.util.ArrayList;
     161import java.util.List;
     162import java.util.Map;
     163
     164import javax.naming.Context;
     165import javax.naming.InitialContext;
     166import javax.naming.NamingException;
     167import javax.sql.DataSource;
     168
     169import org.apache.http.HttpEntity;
     170import org.apache.http.NameValuePair;
     171import org.apache.http.auth.AuthenticationException;
     172import org.apache.http.auth.UsernamePasswordCredentials;
     173import org.apache.http.client.ClientProtocolException;
     174import org.apache.http.client.entity.UrlEncodedFormEntity;
     175import org.apache.http.client.methods.CloseableHttpResponse;
     176import org.apache.http.client.methods.HttpDelete;
     177import org.apache.http.client.methods.HttpPost;
     178import org.apache.http.client.methods.HttpPut;
     179import org.apache.http.entity.ContentType;
     180import org.apache.http.entity.StringEntity;
     181import org.apache.http.impl.auth.BasicScheme;
     182import org.apache.http.impl.client.CloseableHttpClient;
     183import org.apache.http.impl.client.HttpClients;
     184import org.apache.http.message.BasicNameValuePair;
     185import org.apache.http.util.EntityUtils;
     186import org.slf4j.Logger;
     187import org.slf4j.LoggerFactory;
     188
     189import com.fasterxml.jackson.annotation.JsonProperty;
     190import com.fasterxml.jackson.core.JsonProcessingException;
     191import com.fasterxml.jackson.databind.JsonNode;
     192import com.fasterxml.jackson.databind.ObjectMapper;
     193import com.sap.cloud.account.TenantContext;
     194import com.sap.cloud.security.password.PasswordStorage;
     195import com.sap.cloud.security.password.PasswordStorageException;
     196import com.sap.core.connectivity.api.configuration.ConnectivityConfiguration;
     197import com.sap.core.connectivity.api.configuration.DestinationConfiguration;
     198
     199public class Util {
     200       
     201        private static final Logger LOGGER = LoggerFactory.getLogger(Util.class);
     202       
     203        private static final String JNDI_KEY_DATA_SOURCE = "java:comp/env/jdbc/xProjectDB";
     204        private static final String JNDI_KEY_CONNECTIVITY_CONFIG = "java:comp/env/connectivityConfiguration";
     205        private static final String JNDI_KEY_PASSWORD_STORAGE = "java:comp/env/PasswordStorage";
     206        private static final String DESTINATION_OAUTHAS_TOKEN = "oauthasTokenEndpoint";
     207        private static final String DESTINATION_AUTHZ_MGMT = "authzMgmtService";
     208        private static final String PROPERTY_CLIENTID = "User";
     209        private static final String PROPERTY_SECRET = "Password";
     210        private static final String ON_PREMISE_PROXY = "OnPremise";     
     211       
     212        // used for user to role assignments
     213        private static class Role {
     214                public String applicationName;                       
     215                public String name;
     216                public String providerAccount;
     217    }
     218       
     219        private static class Roles {
     220                public Roles() {
     221                        this.role = new ArrayList<Util.Role>();
     222                }
     223                @JsonProperty("roles")
     224            public List<Role> role;
     225               
     226
     227        }
     228       
     229        public static DataSource getDataSource() {
     230                Object dataSource;
     231                try {
     232                        InitialContext ctx = new InitialContext();
     233                        dataSource = ctx.lookup(JNDI_KEY_DATA_SOURCE);
     234                } catch (NamingException e) {
     235                        throw new IllegalStateException("JNDI lookup failure", e);
     236                }
     237                if (dataSource instanceof DataSource) {
     238                        return (DataSource) dataSource;
     239                }
     240                throw new IllegalStateException(
     241                                "No data source available in JNDI context");
     242        }
     243       
     244        public static String getAccountName() {
     245                TenantContext context = null;
     246                try {
     247                        context = (TenantContext) (new InitialContext()).lookup("java:comp/env/tenantContext");
     248                }
     249                catch (NamingException ne) {
     250                        LOGGER.error("Failed to get tenant context", ne);
     251                }
     252                return context.getTenant().getAccount().getId();
     253        }
     254       
     255    public static Proxy getProxy(String proxyType) {
     256        String proxyHost = null;
     257        int proxyPort;
     258       
     259        if (ON_PREMISE_PROXY.equals(proxyType)) {
     260            // Get proxy for on-premise destinations
     261            proxyHost = System.getenv("HC_OP_HTTP_PROXY_HOST");
     262            proxyPort = Integer.parseInt(System.getenv("HC_OP_HTTP_PROXY_PORT"));
     263        } else {
     264            // Get proxy for internet destinations
     265            proxyHost = System.getProperty("http.proxyHost");
     266            proxyPort = Integer.parseInt(System.getProperty("http.proxyPort"));
     267        }
     268        return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
     269    }
     270   
     271    public static int assignUserToRole(String userid, String rolename, String accessToken) {   
     272        // https://api.hana.ondemand.com/authorization/v1/documentation#accounts__accountName__users_roles_put         
     273        Role newRole = new Role();
     274        newRole.applicationName = "xproject";
     275        newRole.name = rolename;
     276        newRole.providerAccount = getAccountName();
     277       
     278        Roles roles = new Roles();
     279        roles.role.add(newRole);
     280       
     281        int responseCode = 0;
     282       
     283        ObjectMapper mapper = new ObjectMapper();
     284        try {
     285                        String json = mapper.writeValueAsString(roles);
     286                       
     287                        // look up the connectivity configuration API "connectivityConfiguration"
     288                Context ctx = new InitialContext();
     289                ConnectivityConfiguration configuration = (ConnectivityConfiguration) ctx.lookup(JNDI_KEY_CONNECTIVITY_CONFIG);
     290
     291                // get destination configuration for "authzapi"
     292                DestinationConfiguration destConfiguration = configuration.getConfiguration(DESTINATION_AUTHZ_MGMT);
     293
     294                // get destination URL
     295                String url = destConfiguration.getProperty("URL").replace("accountName", Util.getAccountName()).
     296                                concat("?userId=" + URLEncoder.encode(userid, StandardCharsets.UTF_8.name()));
     297                LOGGER.debug("Usign URL {}", url);
     298               
     299                // create HTTP Client and PUT request from Apache libs
     300                CloseableHttpClient httpClient = HttpClients.createDefault();
     301                HttpPut httpPut = new HttpPut(url);
     302               
     303                // add API request body
     304                StringEntity requestEntity = new StringEntity(json, ContentType.APPLICATION_JSON);
     305                httpPut.setEntity(requestEntity);
     306                LOGGER.debug("Sending API request body: {}", requestEntity.toString());
     307               
     308                // add the OAuth Access Token to the header
     309                httpPut.addHeader("Authorization", "Bearer " + accessToken);
     310               
     311                try {
     312                        // call API
     313                    LOGGER.debug("Executing request {}", httpPut.getRequestLine());
     314                    CloseableHttpResponse response = httpClient.execute(httpPut);
     315                    responseCode = response.getStatusLine().getStatusCode();
     316                    HttpEntity responseBody = response.getEntity();
     317                    try {
     318                        LOGGER.debug("Response code: {}", responseCode);
     319                        if (responseCode == HttpURLConnection.HTTP_CREATED) {                 
     320                                // process response from api
     321                                String apiResponse = EntityUtils.toString(responseBody);
     322                                LOGGER.debug("Response is: {}", apiResponse);
     323                        }
     324                    } finally {
     325                        // complete response processing
     326                        EntityUtils.consume(responseBody);
     327                        response.close();
     328                    }
     329                } catch (ClientProtocolException cpe) {
     330                                LOGGER.error("ClientProtocol Exception: ", cpe);                               
     331                }
     332                finally {
     333                    httpClient.close();
     334                }                       
     335                } catch (JsonProcessingException jpe) {
     336                        LOGGER.error("Error assigning the role: ", jpe);
     337                } catch (IOException ioe) {
     338                        LOGGER.error("IO Exception: ", ioe);                   
     339        } catch (NamingException ne) {
     340                LOGGER.error("JNDI lookup failure", ne);
     341        }
     342        return responseCode;
     343    }
     344   
     345    public static int unassignUserFromRole(String userid, String rolename, String accessToken) {       
     346        // https://api.hana.ondemand.com/authorization/v1/documentation#accounts__accountName__users_roles_delete
     347        int responseCode = 0;
     348        try {
     349                        // look up the connectivity configuration API "connectivityConfiguration"
     350                Context ctx = new InitialContext();
     351                ConnectivityConfiguration configuration = (ConnectivityConfiguration) ctx.lookup(JNDI_KEY_CONNECTIVITY_CONFIG);
     352
     353                // get destination configuration for "authzapi"
     354                DestinationConfiguration destConfiguration = configuration.getConfiguration(DESTINATION_AUTHZ_MGMT);
     355
     356                // get destination URL
     357                String url = destConfiguration.getProperty("URL").replace("accountName", Util.getAccountName()).
     358                                concat("?userId=" + URLEncoder.encode(userid, StandardCharsets.UTF_8.name())).
     359                                concat("&roles=" + URLEncoder.encode(rolename + "@" + Util.getAccountName() + ":" + "xproject",StandardCharsets.UTF_8.name()));
     360                LOGGER.debug("Usign URL {}", url);
     361               
     362                // create HTTP Client and DELETE request from Apache libs
     363                CloseableHttpClient httpClient = HttpClients.createDefault();
     364                HttpDelete httpDelete = new HttpDelete(url);
     365               
     366                // add the OAuth Access Token to the header
     367                httpDelete.addHeader("Authorization", "Bearer " + accessToken);
     368               
     369                try {
     370                        // call API
     371                    LOGGER.debug("Executing request {}", httpDelete.getRequestLine());
     372                    CloseableHttpResponse response = httpClient.execute(httpDelete);
     373                    responseCode = response.getStatusLine().getStatusCode();
     374                    try {
     375                        HttpEntity responseBody = response.getEntity();
     376                        LOGGER.debug("Response code: {}", responseCode);
     377                        if (responseCode == HttpURLConnection.HTTP_OK) {                       
     378                                // process response from api
     379                                String apiResponse = EntityUtils.toString(responseBody);
     380                                LOGGER.debug("Response is: {}", apiResponse);
     381                        }
     382                        // complete response processing
     383                        EntityUtils.consume(responseBody);
     384                    } finally {
     385                        response.close();
     386                    }
     387                } catch (ClientProtocolException cpe) {
     388                                LOGGER.error("ClientProtocol Exception: ", cpe);                               
     389                }
     390                finally {
     391                    httpClient.close();
     392                }                       
     393                } catch (JsonProcessingException jpe) {
     394                        LOGGER.error("Error assigning the role: ", jpe);
     395                } catch (IOException ioe) {
     396                        LOGGER.error("IO Exception: ", ioe);
     397        } catch (NamingException ne) {
     398                LOGGER.error("JNDI lookup failure", ne);
     399        }
     400        return responseCode;
     401    }
     402   
     403    public static String requestNewAccessTokenForPlatformAPI() {
     404        String accessToken = null;
     405        try {                                   
     406                // look up the connectivity configuration API "connectivityConfiguration"
     407                Context ctx = new InitialContext();
     408                ConnectivityConfiguration configuration = (ConnectivityConfiguration) ctx.lookup(JNDI_KEY_CONNECTIVITY_CONFIG);
     409
     410                // get destination configuration for "oauthasTokenEndpoint"
     411                DestinationConfiguration destConfiguration = configuration.getConfiguration(DESTINATION_OAUTHAS_TOKEN);
     412                       
     413                // get all destination properties
     414                Map<String, String> allDestinationPropeties = destConfiguration.getAllProperties();         
     415               
     416                // get clientid and secret from destination user and password properties
     417                String clientid = allDestinationPropeties.get(Util.PROPERTY_CLIENTID);
     418                String secret = allDestinationPropeties.get(Util.PROPERTY_SECRET);
     419               
     420                LOGGER.debug("Usign client id {} and secret {}", clientid ,secret);
     421               
     422                // get destination URL
     423                URL url = new URL(allDestinationPropeties.get("URL"));         
     424                LOGGER.debug("Usign URL {}", url.toString());
     425               
     426                // create HTTP Client and POST request from Apache libs
     427                CloseableHttpClient httpClient = HttpClients.createDefault();
     428                HttpPost httpPost = new HttpPost(url.toString());
     429               
     430                // add OAuth access token request parameters as per https://tools.ietf.org/html/rfc6749#section-4.4.1
     431                List<NameValuePair> nvps = new ArrayList<NameValuePair>();
     432                nvps.add(new BasicNameValuePair("grant_type", "client_credentials"));
     433               
     434                httpPost.setEntity(new UrlEncodedFormEntity(nvps));
     435               
     436                try {
     437                        // create Basic Authn header
     438                        UsernamePasswordCredentials creds =
     439                                      new UsernamePasswordCredentials(clientid, secret);
     440                        httpPost.addHeader(new BasicScheme().authenticate(creds, httpPost, null));
     441                       
     442                        // send access token request to SAP CP Neo OAuth 2.0 Authorization Server
     443                    LOGGER.debug("Executing request {}", httpPost.getRequestLine());
     444                    CloseableHttpResponse response = httpClient.execute(httpPost);
     445                    try {
     446                        HttpEntity responseBody = response.getEntity();
     447                        int responseCode = response.getStatusLine().getStatusCode();
     448                        LOGGER.debug("Response code: {}", responseCode);
     449                        if (responseCode == HttpURLConnection.HTTP_OK) {                       
     450                                // process response from api token endpoint
     451                                String apiTokenResponse = EntityUtils.toString(responseBody);
     452                                LOGGER.debug("Response is: {}", apiTokenResponse);
     453       
     454                                // read api token response
     455                                ObjectMapper mapper = new ObjectMapper();
     456                                JsonNode apiToken = mapper.readTree(apiTokenResponse);
     457                               
     458                                // get access token from response
     459                                accessToken = apiToken.findValue("access_token").textValue();
     460                                LOGGER.debug("Access token: {}", accessToken);
     461                               
     462                                // store access token in SAP CP password storage using the account- and API name as alias
     463                                setAccessToken(getAccountName(), accessToken.toCharArray());
     464                        }
     465                        // complete response processing
     466                        EntityUtils.consume(responseBody);
     467                    } finally {
     468                        response.close();
     469                    }
     470                } catch (ClientProtocolException cpe) {
     471                                LOGGER.error("ClientProtocol Exception: ", cpe);                               
     472                } catch (AuthenticationException ae) {
     473                        LOGGER.error("Authentication Exception: ", ae);
     474                        }
     475                        finally {
     476                    httpClient.close();
     477                }
     478        } catch (Exception e) {
     479                        LOGGER.error("Exception: ", e);
     480                }
     481        return accessToken;
     482    }
     483   
     484    public static String getAccessTokenForPlatformAPI() {
     485        String accessToken = null;
     486        try {
     487                char[] accessTokenChar = getAccessToken(getAccountName());
     488                if (accessTokenChar != null) {
     489                        accessToken = String.valueOf(accessTokenChar);
     490                } else {
     491                        // request a new access token from SAP CP Neo OAuth 2.0 Authorization Server
     492                        accessToken = requestNewAccessTokenForPlatformAPI();                           
     493                }
     494        } catch (PasswordStorageException pse) {
     495                LOGGER.error("Error retrieving access token from password storage: ", pse);
     496        } catch (NamingException ne) {
     497                LOGGER.error("JNDI lookup failure", ne);
     498        }
     499        return accessToken;
     500    }
     501   
     502    private static PasswordStorage getPasswordStorage() throws NamingException {
     503        InitialContext ctx = new InitialContext();
     504        PasswordStorage passwordStorage = (PasswordStorage) ctx.lookup(JNDI_KEY_PASSWORD_STORAGE);
     505        return passwordStorage;
     506    }
     507     
     508    private static void setAccessToken(String alias, char[] accessToken) throws PasswordStorageException, NamingException {
     509        // TODO: Place cursor below this line and insert Snippet 1
     510                PasswordStorage passwordStorage = getPasswordStorage();
     511                passwordStorage.setPassword(alias, accessToken);
     512                LOGGER.debug("Successfully stored access token with account id {} as alias", alias);
     513    }
     514     
     515    private static char[] getAccessToken(String alias) throws PasswordStorageException, NamingException {
     516        char[] accessToken = null;
     517        // TODO: Place cursor below this line and insert Snippet 2
     518                PasswordStorage passwordStorage = getPasswordStorage();
     519                accessToken = passwordStorage.getPassword(alias);
     520        if (accessToken != null) {
     521                LOGGER.debug("Retrieved access token {} for account id {} as alias", String.valueOf(accessToken), alias);
     522        }
     523        return accessToken;
     524    }
     525}
     526
     527}}}