com.amazonaws.sample.entitlement.services.EntitlementService.java Source code

Java tutorial

Introduction

Here is the source code for com.amazonaws.sample.entitlement.services.EntitlementService.java

Source

/*
 * Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Amazon Software License (the "License"). You may not use
 * this file except in compliance with the License. A copy of the License is
 * located at:
 *
 *       http://aws.amazon.com/asl/
 *
 * This Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

package com.amazonaws.sample.entitlement.services;

import com.amazonaws.AmazonServiceException;
import com.amazonaws.AmazonServiceException.ErrorType;
import com.amazonaws.sample.entitlement.authorization.AuthorizationHandler;
import com.amazonaws.sample.entitlement.authorization.Identity;
import com.amazonaws.sample.entitlement.exceptions.ApplicationBadStateException;
import com.amazonaws.sample.entitlement.exceptions.ApplicationNotFoundException;
import com.amazonaws.sample.entitlement.exceptions.AuthorizationException;
import com.amazonaws.sample.entitlement.exceptions.MaxSessionsLimitExceededException;
import com.amazonaws.sample.entitlement.exceptions.SessionActiveException;
import com.amazonaws.sample.entitlement.exceptions.UserNotEntitledException;
import com.amazonaws.sample.entitlement.exceptions.UserNotFoundException;
import com.amazonaws.services.appstream.AppStream;
import com.amazonaws.services.appstream.Application;
import com.amazonaws.services.appstream.EntitleSessionInput;
import com.amazonaws.services.appstream.Session;
import com.amazonaws.services.dynamodbv2.document.*;
import com.amazonaws.services.dynamodbv2.document.spec.GetItemSpec;
import com.amazonaws.services.dynamodbv2.document.spec.QuerySpec;
import com.amazonaws.util.json.JSONArray;
import org.apache.http.HttpStatus;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.aws.core.env.ResourceIdResolver;
import org.springframework.stereotype.Component;

import javax.inject.Inject;
import java.time.Instant;
import java.util.Iterator;

/**
 * Provides methods related to authorization, retrieval of AppStream {@link Application} objects, user entitlement and
 * {@link Session} management.
 */
@Component
public class EntitlementService {

    // @see http://tools.ietf.org/html/rfc6585#section-4
    private static final int HTTP_STATUS_TOO_MANY_REQUESTS = 429;

    @Inject
    public EntitlementService(ResourceIdResolver resourceIdResolver, DynamoDB dynamoDBDocument) {
        this.entitlementServiceUserTable = dynamoDBDocument
                .getTable(resourceIdResolver.resolveToPhysicalResourceId("EntitlementServiceUser"));
        this.entitlementServiceUserSessionTable = dynamoDBDocument
                .getTable(resourceIdResolver.resolveToPhysicalResourceId("EntitlementServiceUserSession"));
        this.entitlementServiceUserSubscriptionTable = dynamoDBDocument
                .getTable(resourceIdResolver.resolveToPhysicalResourceId("EntitlementServiceUserSubscription"));
    }

    //-------------------------------------------------------------
    // Variables - Private
    //-------------------------------------------------------------

    private Table entitlementServiceUserTable;
    private Table entitlementServiceUserSessionTable;
    private Table entitlementServiceUserSubscriptionTable;

    @Inject
    private AppStream appstream;
    @Inject
    private AuthorizationHandler authorizationHandler;

    @Value("${createUserWhenNew:false}")
    private boolean createUserWhenNew;
    @Value("${entitleAllWhenNew:false}")
    private boolean entitleAllWhenNew;

    private Logger log = Logger.getLogger(EntitlementService.class.getName());

    //-------------------------------------------------------------
    // Methods - Public
    //-------------------------------------------------------------

    /**
     * Given an authorization string (tied to a users identity), return a User object.
     * @param authorization value from authorization header
     * @return a User object
     * @throws AuthorizationException
     */
    public Item getUserFromAuthorization(String authorization) throws AuthorizationException {
        if (authorization == null) {
            throw new AuthorizationException("Missing Authorization header.");
        }

        Identity thirdPartyIdentity = authorizationHandler.processAuthorization(authorization);

        Item user = entitlementServiceUserTable.getItem("id", thirdPartyIdentity.getId());
        log.info(thirdPartyIdentity.getId());

        if (user == null) {
            if (!createUserWhenNew) {
                log.warn("No such user: " + thirdPartyIdentity.getId());
                throw new UserNotFoundException();
            }
            user.withString("id", thirdPartyIdentity.getId());
            user.withString("email", thirdPartyIdentity.getEmail());
            user = entitlementServiceUserTable.putItem(user).getItem();
        }

        return user;
    }

    public Boolean shouldAlwaysEntitle() {
        return authorizationHandler.shouldAlwaysEntitle();
    }

    /**
     * Retrieve an Application object from the AppStream service.
     * @param applicationId AppStream application id
     * @return an AppStream Application
     * @throws ApplicationNotFoundException
     */
    public Application getApplication(String applicationId) throws ApplicationNotFoundException {
        try {
            return appstream.getApplications().getById(applicationId);
        } catch (Exception e) {
            log.error(e);

            throw new ApplicationNotFoundException(
                    "The application identified by " + applicationId + " was not found.");
        }
    }

    /**
     * Return applications user is entitled to run
     * @param user the user the current request is for
     * @throws ApplicationBadStateException if the application the current request is for is in an error state
     * @return prettified string of JSON
     */

    public String getSubscriptions(Item user) throws ApplicationBadStateException {
        try {
            QuerySpec querySpec = new QuerySpec().withHashKey("UserId", user.getString("id"));
            ItemCollection<QueryOutcome> items = entitlementServiceUserTable.query(querySpec);
            JSONArray allItems = new JSONArray();
            // Process each page of results
            for (Page<Item, QueryOutcome> page : items.pages()) {
                // Process each item on the current page
                Iterator<Item> item = page.iterator();
                while (item.hasNext()) {
                    allItems.put(item.next().toJSON());
                }
            }
            // String jsonString= allItems.toString();
            return allItems.toString();
        } catch (AmazonServiceException e) {
            // Make the exception just a bit more obvious with an informational message.
            // It may not be obvious where the error occurred with just a stack trace.
            if (e.getErrorType().equals(AmazonServiceException.ErrorType.Service)) {
                log.error("An error occurred while getting data from DynamoDB: " + e);
            }
            throw e;
        }
    }

    /**
     * Return applications user is running (connected or not)
     * @param user the user the current request is for
     * @throws ApplicationBadStateException if the application the current request is for is in an error state
     * @return prettified string of JSON
     */

    public String getUserSessions(Item user) throws ApplicationBadStateException {
        try {
            QuerySpec querySpec = new QuerySpec().withHashKey("UserId", user.getString("id"));
            ItemCollection<QueryOutcome> items = entitlementServiceUserTable.query(querySpec);
            JSONArray allItems = new JSONArray();
            // Process each page of results
            for (Page<Item, QueryOutcome> page : items.pages()) {
                // Process each item on the current page
                Iterator<Item> item = page.iterator();
                while (item.hasNext()) {
                    // allItems.put(item.next());
                    allItems.put(item.next().toJSON());
                }
            }
            String jsonString = allItems.toString();
            return jsonString;
        } catch (AmazonServiceException e) {
            // Make the exception just a bit more obvious with an informational message.
            // It may not be obvious where the error occurred with just a stack trace.
            if (e.getErrorType().equals(AmazonServiceException.ErrorType.Service)) {
                log.error("An error occurred while getting data from DynamoDB: " + e);
            }
            throw e;
        }
    }

    /**
     * Return new UserSession
     * @param user the user the current request is for
     * @param userSubscription the current request is for
     * @throws ApplicationBadStateException if the application the current request is for is in an error state
     * @return prettified string of JSON
     */

    public String startUserSession(String userSubscription, Item user)
            throws UserNotEntitledException, ApplicationBadStateException, MaxSessionsLimitExceededException {
        try {
            Session session;
            Application application;
            Item retrievedSubscription;
            try {
                // user input subscription
                Item inputSubscription = Item.fromJSON(userSubscription);
                // substitute verified user id into query
                retrievedSubscription = entitlementServiceUserSubscriptionTable
                        .getItem(new GetItemSpec().withPrimaryKey("UserId", user.getString("id"),
                                "CreationTimeMilli", inputSubscription.getLong("CreationTimeMilli")));
                if (retrievedSubscription.getLong("TotalCombinedSessionTimeLimitMilli") < 0) {
                    throw new UserNotEntitledException("You do not have remaining time for this subscription.");
                }
            } catch (Error e) {
                log.error("Couldn't retrieve subscription provided. Error: " + e);
                throw new UserNotEntitledException(
                        "You are not currently allowed to use this application.  Please ask for access.");
            }
            try {
                application = getApplication(retrievedSubscription.getString("AppStreamApplicationId"));
                session = entitleSessionV2(application);
            } catch (ApplicationNotFoundException e) {
                log.error(e);
                throw new ApplicationBadStateException("Could not connect subscription to AppStream Application");
            } catch (MaxSessionsLimitExceededException e) {
                log.error(e);
                throw e;
            }
            // save session
            Item userSession = new Item()
                    .withPrimaryKey("UserId", user.getString("id"), "CreationTimeMilli",
                            Instant.now().toEpochMilli())
                    .withLong("UserSubscriptionCreationTimeMilli",
                            retrievedSubscription.getLong("CreationTimeMilli"))
                    .withString("Email", user.getString("email"))
                    .withString("UserApplicationId", retrievedSubscription.getString("UserApplicationId"))
                    .withString("AppStreamSessionId", session.getId())
                    .withString("AppStreamApplicationId", retrievedSubscription.getString("AppStreamApplicationId"))
                    .withString("UserApplicationName", retrievedSubscription.getString("UserApplicationName"))
                    .withString("UserApplicationDescription",
                            retrievedSubscription.getString("UserApplicationDescription"))
                    .withLong("PerSessionTimeLimitMilli", retrievedSubscription.getLong("PerSessionTimeLimitMilli"))
                    .withString("AppStreamEntitlementUrl", session.getEntitlementUrl())
                    .withLong("AppStreamEntitlementUrlValidTimeMilli", 350000);
            entitlementServiceUserSessionTable.putItem(userSession).getItem();
            // send back session
            return userSession.toJSONPretty();
        } catch (AmazonServiceException e) {
            if (e.getErrorType().equals(AmazonServiceException.ErrorType.Service)) {
                log.error("An error occurred while getting data from DynamoDB: " + e);
            }
            throw e;
        }
    }

    /**
     * Return a Session from the AppStream service for a User and Application.
     * @param application the application the current request is for
     * @throws ApplicationBadStateException if the application the current request is for is in an error state
     * @return an AppStream entitled Session
     */
    public Session entitleSessionV2(Application application)
            throws ApplicationBadStateException, MaxSessionsLimitExceededException {
        Session session;

        try {
            EntitleSessionInput entitleSessionInput = new EntitleSessionInput();

            // Could optionally pass opaque data to the application that will service the session.
            // entitleSessionInput.setOpaqueData("some application-specific data");

            session = application.entitleSession(entitleSessionInput);
        } catch (AmazonServiceException e) {
            if (e.getErrorType().equals(AmazonServiceException.ErrorType.Service)) {
                log.error("An error occurred in the AppStream service while entitling a session for app: "
                        + application.getId(), e);
            } else if (ErrorType.Client == e.getErrorType()) {
                if (e.getStatusCode() == HttpStatus.SC_CONFLICT) {
                    throw new ApplicationBadStateException(e.getMessage());
                } else if (e.getStatusCode() == HTTP_STATUS_TOO_MANY_REQUESTS) { // maximum sessions limit reached
                    throw new MaxSessionsLimitExceededException();
                }
            }

            throw e;
        }

        return session;
    }

    /**
     * Return a Session from the AppStream service for a User and Application. Sample DES V1
     * @param application the application the current request is for
     * @throws ApplicationBadStateException if the application the current request is for is in an error state
     * @return an AppStream entitled Session
     */
    public Session entitleSessionV1(Application application)
            throws ApplicationBadStateException, MaxSessionsLimitExceededException {
        Session session;
        try {
            EntitleSessionInput entitleSessionInput = new EntitleSessionInput();
            session = application.entitleSession(entitleSessionInput);
        } catch (AmazonServiceException e) {
            if (e.getErrorType().equals(AmazonServiceException.ErrorType.Service)) {
                log.error("An error occurred in the AppStream service while entitling a session for app: "
                        + application.getId(), e);
            } else if (ErrorType.Client == e.getErrorType()) {
                if (e.getStatusCode() == HttpStatus.SC_CONFLICT) {
                    throw new ApplicationBadStateException(e.getMessage());
                } else if (e.getStatusCode() == HTTP_STATUS_TOO_MANY_REQUESTS) { // maximum sessions limit reached
                    throw new MaxSessionsLimitExceededException();
                }
            }

            throw e;
        }

        return session;
    }

}