Browse Source

feat(server): Allow guest Customers to view completed order

Completed orders may be viewed by code only for a limited time after completion
Michael Bromley 7 years ago
parent
commit
6d95443e87

+ 21 - 9
server/src/api/resolvers/order.resolver.ts

@@ -1,4 +1,5 @@
 import { Args, Mutation, Parent, Query, ResolveProperty, Resolver } from '@nestjs/graphql';
+import * as ms from 'ms';
 import {
     AddItemToOrderMutationArgs,
     AddPaymentToOrderMutationArgs,
@@ -98,16 +99,27 @@ export class OrderResolver {
     ): Promise<Order | undefined> {
         if (ctx.authorizedAsOwnerOnly) {
             const order = await this.orderService.findOneByCode(ctx, args.code);
-            if (
-                order &&
-                order.customer &&
-                order.customer.user &&
-                order.customer.user.id === ctx.activeUserId
-            ) {
-                return this.orderService.findOne(ctx, order.id);
-            } else {
-                throw new I18nError(`error.forbidden`);
+
+            if (order) {
+                // For guest Customers, allow access to the Order for the following
+                // time period
+                const anonymousAccessLimit = ms('2h');
+                const orderPlaced = order.orderPlacedAt ? +order.orderPlacedAt : 0;
+                const activeUserMatches = !!(
+                    order &&
+                    order.customer &&
+                    order.customer.user &&
+                    order.customer.user.id === ctx.activeUserId
+                );
+                const now = +new Date();
+                const isWithinAnonymousAccessLimit = now - orderPlaced < anonymousAccessLimit;
+                if (activeUserMatches || isWithinAnonymousAccessLimit) {
+                    return this.orderService.findOne(ctx, order.id);
+                }
             }
+            // We throw even if the order does not exist, since giving a different response
+            // opens the door to an enumeration attack to find valid order codes.
+            throw new I18nError(`error.forbidden`);
         }
     }
 

+ 3 - 0
server/src/entity/order/order.entity.ts

@@ -25,6 +25,9 @@ export class Order extends VendureEntity {
     @Column({ default: true })
     active: boolean;
 
+    @Column({ nullable: true })
+    orderPlacedAt?: Date;
+
     @ManyToOne(type => Customer)
     customer?: Customer;
 

+ 1 - 0
server/src/service/helpers/order-state-machine/order-state-machine.ts

@@ -51,6 +51,7 @@ export class OrderStateMachine {
     private onTransitionEnd(fromState: OrderState, toState: OrderState, data: OrderTransitionData) {
         if (toState === 'PaymentAuthorized' || toState === 'PaymentSettled') {
             data.order.active = false;
+            data.order.orderPlacedAt = new Date();
         }
     }