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

Java tutorial

Introduction

Here is the source code for com.amazonaws.sample.entitlement.services.AdministrationService.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.sample.entitlement.authorization.AuthorizationHandler;
import com.amazonaws.sample.entitlement.authorization.Identity;
import com.amazonaws.sample.entitlement.exceptions.*;
import com.amazonaws.services.appstream.*;
import com.amazonaws.services.cognitoidentity.AmazonCognitoIdentityClient;
import com.amazonaws.services.cognitoidentity.model.*;
import com.amazonaws.services.dynamodbv2.document.*;
import com.amazonaws.services.dynamodbv2.document.utils.NameMap;
import com.amazonaws.util.json.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.io.UnsupportedEncodingException;
import java.time.Instant;
import java.util.*;

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

    private Table entitlementServiceUserTable;
    private Table entitlementServiceUserApplicationTable;
    private Table entitlementServiceUserSubscriptionTable;
    private Table entitlementServiceUserSessionTable;
    private Table entitlementServiceConfigurationTable;

    private String awsCognitoIdentityPool;
    private String awsCognitoDeveloperProviderName;

    @Inject
    public AdministrationService(ResourceIdResolver resourceIdResolver, Properties cognitoProperties,
            DynamoDB dynamoDBDocument) {
        this.entitlementServiceUserTable = dynamoDBDocument
                .getTable(resourceIdResolver.resolveToPhysicalResourceId("EntitlementServiceUser"));
        this.entitlementServiceUserApplicationTable = dynamoDBDocument
                .getTable(resourceIdResolver.resolveToPhysicalResourceId("EntitlementServiceUserApplication"));
        this.entitlementServiceUserSubscriptionTable = dynamoDBDocument
                .getTable(resourceIdResolver.resolveToPhysicalResourceId("EntitlementServiceUserSubscription"));
        this.entitlementServiceUserSessionTable = dynamoDBDocument
                .getTable(resourceIdResolver.resolveToPhysicalResourceId("EntitlementServiceUserSession"));
        this.entitlementServiceConfigurationTable = dynamoDBDocument
                .getTable(resourceIdResolver.resolveToPhysicalResourceId("EntitlementServiceConfiguration"));
        this.awsCognitoDeveloperProviderName = cognitoProperties.getProperty("awsCognitoDeveloperProviderName");
        this.awsCognitoIdentityPool = cognitoProperties.getProperty("awsCognitoIdentityPool");
    }

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

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

    @Autowired
    @Qualifier("configuration")
    private Properties cognitoProperties;

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

    // Since we have a PolicyBasedAuthorizationHandler, it is safe to default createUserWhenNew to true.
    // If the policy is Deny then the AuthorizationHandler will always check to see if the user is valid and when a
    // new user is created via the UI you still have to specify that the user is entitled to all applications.
    // If the policy is Allow then these both need to be true or else so a user can be created if necessary and
    // a new user will always be entitled to all applications.
    @Value("${createUserWhenNew:false}")
    private boolean createUserWhenNew;
    @Value("${entitleAllWhenNew:false}")
    private boolean entitleAllWhenNew;

    private Logger log = Logger.getLogger(AdministrationService.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.");
        }
        // TODO have to verify the token only
        Identity thirdPartyIdentity = authorizationHandler.processAuthorization(authorization);

        //TODO  then we have to lookup by email
        Item user = entitlementServiceUserTable.getItem("id", 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());
            entitlementServiceUserTable.putItem(user);
        }

        String role = user.getString("role");
        if (role == null || !role.equals("Administrator")) {
            log.info("An authorization exception occurred: User not assigned Administrator role.");
            throw new AuthorizationException("Not Authorized.");
        }

        return user;
    }

    /**
     * Get users
     * @throws ApplicationBadStateException if the application the current request is for is in an error state
     * @return prettified string of JSON
     */

    public String getUsers() throws ApplicationBadStateException {
        try {
            ItemCollection<ScanOutcome> items = entitlementServiceUserTable.scan(
                    // filter expression
                    null,
                    // projection expression
                    "id, email, #n, #r",
                    // attribute name substitution
                    new NameMap().with("#n", "name").with("#r", "role"),
                    // attribute value substitution
                    null);
            List<String> allItems = new ArrayList<>();
            // Process each page of results
            for (Page<Item, ScanOutcome> page : items.pages()) {
                // Process each item on the current page
                Iterator<Item> item = page.iterator();
                while (item.hasNext()) {
                    allItems.add(item.next().toJSON());
                }
            }
            return "[" + StringUtils.join(allItems, ",") + "]";
        } catch (AmazonServiceException e) {
            if (e.getErrorType().equals(AmazonServiceException.ErrorType.Service)) {
                log.error("An error occurred while getting data from DynamoDB: " + e);
            }
            throw e;
        }
    }

    /**
     * Add user
     * @throws ApplicationBadStateException if the application the current request is for is in an error state
     * @return prettified string of JSON
     */
    public String addUser(String user) throws ApplicationBadStateException, UserBadIdentifierException {
        try {
            Item userItem = Item.fromJSON(user);
            String base64EncEmail = Base64.getEncoder().withoutPadding()
                    .encodeToString(userItem.getString("email").getBytes("utf-8"));
            GetOpenIdTokenForDeveloperIdentityRequest req = new GetOpenIdTokenForDeveloperIdentityRequest();
            req.setIdentityPoolId(awsCognitoIdentityPool);
            req.addLoginsEntry(awsCognitoDeveloperProviderName, base64EncEmail);
            GetOpenIdTokenForDeveloperIdentityResult res = cognitoIdentityClient
                    .getOpenIdTokenForDeveloperIdentity(req);
            String cognitoIdentityId = res.getIdentityId();
            userItem.withString("id", cognitoIdentityId);
            entitlementServiceUserTable.putItem(userItem).getItem();
            return userItem.toJSONPretty();
        } catch (AmazonServiceException e) {
            if (e.getErrorType().equals(AmazonServiceException.ErrorType.Service)) {
                log.error("An error occurred while getting data from DynamoDB: " + e);
            }
            throw e;
        } catch (UnsupportedEncodingException e) {
            String m = "An error occurred while encoding provided user's email address.";
            log.error(m + e);
            throw new UserBadIdentifierException(m);
        }
    }

    /**
     * Delete user
     * @throws ApplicationBadStateException if the application the current request is for is in an error state
     */
    public void deleteUser(String email) throws ApplicationBadStateException {
        try {
            String cognitoIdentityId;
            String base64EncEmail;
            try {
                base64EncEmail = Base64.getEncoder().withoutPadding().encodeToString(email.getBytes("utf-8"));
            } catch (UnsupportedEncodingException e) {
                throw new ApplicationBadStateException("Don't know how to handle authorization.");
            }
            try {
                LookupDeveloperIdentityRequest req = new LookupDeveloperIdentityRequest().withMaxResults(1)
                        .withDeveloperUserIdentifier(base64EncEmail).withIdentityPoolId(awsCognitoIdentityPool);
                LookupDeveloperIdentityResult res = cognitoIdentityClient.lookupDeveloperIdentity(req);
                cognitoIdentityId = res.getIdentityId();
            } catch (NotAuthorizedException e) {
                log.error("No Cognito Developer Identity exists for " + email + " " + e);
                throw new ApplicationBadStateException("");
            }
            UnlinkDeveloperIdentityRequest unlinkRequest = new UnlinkDeveloperIdentityRequest()
                    .withIdentityId(cognitoIdentityId).withDeveloperUserIdentifier(base64EncEmail)
                    .withIdentityPoolId(awsCognitoIdentityPool)
                    .withDeveloperProviderName(awsCognitoDeveloperProviderName);
            cognitoIdentityClient.unlinkDeveloperIdentity(unlinkRequest);
            entitlementServiceUserTable.deleteItem("id", cognitoIdentityId);
        } catch (AmazonServiceException e) {
            if (e.getErrorType().equals(AmazonServiceException.ErrorType.Service)) {
                log.error("An error occurred while getting data from DynamoDB: " + e);
            }
            throw e;
        }
    }

    /**
     * Get applications
     * @throws ApplicationBadStateException if the application the current request is for is in an error state
     * @return prettified string of JSON
     */

    public String getUserApplications() throws ApplicationBadStateException {
        try {
            ItemCollection<ScanOutcome> items = entitlementServiceUserApplicationTable.scan(
                    // filter expression
                    null,
                    // projection expression
                    "id, UserApplicationName, UserApplicationDescription, AppStreamApplicationId",
                    // attribute name substitution
                    null,
                    // attribute value substitution
                    null);
            List<String> allItems = new ArrayList<>();
            // Process each page of results
            for (Page<Item, ScanOutcome> page : items.pages()) {
                // Process each item on the current page
                Iterator<Item> item = page.iterator();
                while (item.hasNext()) {
                    allItems.add(item.next().toJSON());
                }
            }
            return "[" + StringUtils.join(allItems, ",") + "]";
        } 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;
        }
    }

    /**
     * Add application
     * @throws ApplicationBadStateException if the application the current request is for is in an error state
     * @return prettified string of JSON
     */

    public String addUserApplication(String application) throws ApplicationBadStateException {
        try {
            // TODO WebRTCEnabled=true
            String id = UUID.randomUUID().toString();
            Item userItem = Item.fromJSON(application).withPrimaryKey("id", id);
            entitlementServiceUserApplicationTable.putItem(userItem).getItem();
            return userItem.toJSONPretty();
        } 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;
        }
    }

    /**
     * Delete application
     * @throws ApplicationBadStateException if the application the current request is for is in an error state
     */

    public void deleteUserApplication(String id) throws ApplicationBadStateException {
        try {
            entitlementServiceUserApplicationTable.deleteItem("id", id);
        } catch (AmazonServiceException e) {
            if (e.getErrorType().equals(AmazonServiceException.ErrorType.Service)) {
                log.error("An error occurred while getting data from DynamoDB: " + e);
            }
            throw e;
        }
    }

    /**
     * Get subscriptions
     * @throws ApplicationBadStateException if the application the current request is for is in an error state
     * @return prettified string of JSON
     */

    public String getUserSubscriptions() throws ApplicationBadStateException {
        try {
            ItemCollection<ScanOutcome> items = entitlementServiceUserSubscriptionTable.scan(
                    // filter expression
                    null,
                    // projection expression
                    "Email, UserId, CreationTimeMilli, UserApplicationId, AppStreamApplicationId, UserApplicationDescription, UserApplicationName, PerSessionTimeLimitMilli, TotalCombinedSessionTimeLimitMilli",
                    // attribute name substitution
                    null,
                    // attribute value substitution
                    null);
            List<String> allItems = new ArrayList<>();
            // Process each page of results
            for (Page<Item, ScanOutcome> page : items.pages()) {
                // Process each item on the current page
                Iterator<Item> item = page.iterator();
                while (item.hasNext()) {
                    allItems.add(item.next().toJSON());
                }
            }
            return "[" + StringUtils.join(allItems, ",") + "]";
        } catch (AmazonServiceException e) {
            if (e.getErrorType().equals(AmazonServiceException.ErrorType.Service)) {
                log.error("An error occurred while getting data from DynamoDB: " + e);
            }
            throw e;
        }
    }

    /**
     * Add subscription
     * @throws ApplicationBadStateException if the application the current request is for is in an error state
     * @return prettified string of JSON
     */

    public String addUserSubscription(String subscription) throws ApplicationBadStateException {
        try {
            Item subscriptionItem = Item.fromJSON(subscription);
            // lookup user item by GSI query on user table (table must only contain one result)
            ItemCollection<QueryOutcome> responses = entitlementServiceUserTable.getIndex("emailGSI").query("email",
                    subscriptionItem.getString("Email"));
            Iterator<Item> iter = responses.iterator();
            Item user = iter.next();
            // add and remove attributes to create valid subscription
            subscriptionItem.withString("UserApplicationId", subscriptionItem.getString("id")).removeAttribute("id")
                    .withPrimaryKey("UserId", user.getString("id"), "CreationTimeMilli",
                            Instant.now().toEpochMilli());
            // save new subscription
            entitlementServiceUserSubscriptionTable.putItem(subscriptionItem).getItem();
            return subscriptionItem.toJSONPretty();
        } catch (AmazonServiceException e) {
            if (e.getErrorType().equals(AmazonServiceException.ErrorType.Service)) {
                log.error("An error occurred while getting data from DynamoDB: " + e);
            }
            throw e;
        }
    }

    /**
     * Delete subscription
     * @throws ApplicationBadStateException if the application the current request is for is in an error state
     */

    public void deleteUserSubscription(String userId, Long creationTimeMilli) throws ApplicationBadStateException {
        try {

            entitlementServiceUserSubscriptionTable.deleteItem("UserId", userId, "CreationTimeMilli",
                    creationTimeMilli);
        } catch (AmazonServiceException e) {
            if (e.getErrorType().equals(AmazonServiceException.ErrorType.Service)) {
                log.error("An error occurred while getting data from DynamoDB: " + e);
            }
            throw e;
        }
    }

    /**
     * Get subscriptions
     * @throws ApplicationBadStateException if the application the current request is for is in an error state
     * @return prettified string of JSON
     */

    public String getUserSessions() throws ApplicationBadStateException {
        try {
            ItemCollection<ScanOutcome> items = entitlementServiceUserSessionTable.scan(
                    // filter expression
                    "attribute_not_exists(AppStreamEntitlementExpired)",
                    // projection expression
                    "Email, UserId, CreationTimeMilli, UserApplicationId, AppStreamApplicationId, UserApplicationDescription, UserApplicationName, PerSessionTimeLimitMilli, TotalCombinedSessionTimeLimitMilli",
                    // attribute name substitution
                    null,
                    // attribute value substitution
                    null);
            List<String> allItems = new ArrayList<>();
            // Process each page of results
            for (Page<Item, ScanOutcome> page : items.pages()) {
                // Process each item on the current page
                Iterator<Item> item = page.iterator();
                while (item.hasNext()) {
                    allItems.add(item.next().toJSON());
                }
            }
            return "[" + StringUtils.join(allItems, ",") + "]";
        } catch (AmazonServiceException e) {
            if (e.getErrorType().equals(AmazonServiceException.ErrorType.Service)) {
                log.error("An error occurred while getting data from DynamoDB: " + e);
            }
            throw e;
        }
    }

    /**
     * Get configuration
     * @throws ApplicationBadStateException if the application the current request is for is in an error state
     * @return prettified string of JSON
     */

    public String getConfiguration() throws ApplicationBadStateException {
        try {
            JSONObject response = new JSONObject(cognitoProperties);
            return response.toString();
        } catch (AmazonServiceException e) {
            if (e.getErrorType().equals(AmazonServiceException.ErrorType.Service)) {
                log.error("An error occurred while getting data from DynamoDB: " + e);
            }
            throw e;
        }
    }

    /**
     * Set configuration
     * @throws ApplicationBadStateException if the application the current request is for is in an error state
     * @return prettified string of JSON
     */

    public String setConfiguration(String config) throws ApplicationBadStateException {
        try {
            Item configItem = Item.fromJSON(config).removeAttribute("id").withPrimaryKey("StackId",
                    cognitoProperties.getProperty("StackId"));
            entitlementServiceConfigurationTable.putItem(configItem);
            return configItem.toJSONPretty();
        } catch (AmazonServiceException e) {
            if (e.getErrorType().equals(AmazonServiceException.ErrorType.Service)) {
                log.error("An error occurred while getting data from DynamoDB: " + e);
            }
            throw e;
        }
    }
}