08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
Restaurant Server - Transcript.pdf
1. Restaurant Server
I skipped the server creation tutorial before as I felt it left the domain of Codename One too much and ventured into server programming. In this level we don’t skip such
things so I’m going to review the server code related to the restaurant app and why it was built like that.
5. @Controller
@RequestMapping("/braintree")
public class BraintreePaymentService {
@Autowired
private RestaurantRepository restaurantRepository;
@Autowired
private OrderRepository orderRepository;
private WeakHashMap<String, BraintreeGateway> gatewayCache = new WeakHashMap<>();
private BraintreeGateway getGateway(String auth) {
BraintreeGateway bg = gatewayCache.get(auth);
if(bg != null) {
return bg;
}
RestaurantEntity r = restaurantRepository.findOne(auth);
if(r != null) {
bg = new BraintreeGateway(
Environment.SANDBOX,
r.getMerchantId(),
r.getPublicKey(),
r.getPrivateKey()
);
gatewayCache.put(auth, bg);
return bg;
}
return null;
}
BraintreePaymentService
The braintree service is pretty simple, it maps to the braintree URL. The first thing we do is provide access to the braintree gateway.
6. @Controller
@RequestMapping("/braintree")
public class BraintreePaymentService {
@Autowired
private RestaurantRepository restaurantRepository;
@Autowired
private OrderRepository orderRepository;
private WeakHashMap<String, BraintreeGateway> gatewayCache = new WeakHashMap<>();
private BraintreeGateway getGateway(String auth) {
BraintreeGateway bg = gatewayCache.get(auth);
if(bg != null) {
return bg;
}
RestaurantEntity r = restaurantRepository.findOne(auth);
if(r != null) {
bg = new BraintreeGateway(
Environment.SANDBOX,
r.getMerchantId(),
r.getPublicKey(),
r.getPrivateKey()
);
gatewayCache.put(auth, bg);
return bg;
}
return null;
}
BraintreePaymentService
Notice that the normal braintree API hardcodes all of these values but we load them dynamically and then place them in a cache for reuse. The reason for this is that the
braintree credentials are specific to restaurant and we will have multiple restaurants running on a single server
7. @RequestMapping(method=RequestMethod.GET)
public @ResponseBody String getToken(@RequestParam(name = "auth") String auth) {
return getGateway(auth).clientToken().generate();
}
@RequestMapping(method=RequestMethod.POST)
public @ResponseBody PurchaseResult purchase(@RequestBody(required=true) Order i) {
TransactionRequest request = new TransactionRequest()
.amount(i.calculateAmount())
.paymentMethodNonce(i.getNounce())
.options()
.submitForSettlement(true)
.done();
Result<Transaction> result = getGateway(i.getAuth()).transaction().sale(request);
if(result.isSuccess()) {
// Store entry and email restaurant
}
return new PurchaseResult(result.isSuccess(), result.getMessage());
}
}
BraintreePaymentService
The service methods themselves are pretty trivial
8. @RequestMapping(method=RequestMethod.GET)
public @ResponseBody String getToken(@RequestParam(name = "auth") String auth) {
return getGateway(auth).clientToken().generate();
}
@RequestMapping(method=RequestMethod.POST)
public @ResponseBody PurchaseResult purchase(@RequestBody(required=true) Order i) {
TransactionRequest request = new TransactionRequest()
.amount(i.calculateAmount())
.paymentMethodNonce(i.getNounce())
.options()
.submitForSettlement(true)
.done();
Result<Transaction> result = getGateway(i.getAuth()).transaction().sale(request);
if(result.isSuccess()) {
// Store entry and email restaurant
}
return new PurchaseResult(result.isSuccess(), result.getMessage());
}
}
BraintreePaymentService
The token is generated using the standard braintree API, there isn’t much here it just returns the string which we use in the client side code
9. @RequestMapping(method=RequestMethod.GET)
public @ResponseBody String getToken(@RequestParam(name = "auth") String auth) {
return getGateway(auth).clientToken().generate();
}
@RequestMapping(method=RequestMethod.POST)
public @ResponseBody PurchaseResult purchase(@RequestBody(required=true) Order i) {
TransactionRequest request = new TransactionRequest()
.amount(i.calculateAmount())
.paymentMethodNonce(i.getNounce())
.options()
.submitForSettlement(true)
.done();
Result<Transaction> result = getGateway(i.getAuth()).transaction().sale(request);
if(result.isSuccess()) {
// Store entry and email restaurant
}
return new PurchaseResult(result.isSuccess(), result.getMessage());
}
}
BraintreePaymentService
The interesting method is the purchase method where we create a purchase request and finally fill in the amount we want to pay.
This isn’t obvious from the code here but we have a small security issue in this code…
10. @RequestMapping(method=RequestMethod.GET)
public @ResponseBody String getToken(@RequestParam(name = "auth") String auth) {
return getGateway(auth).clientToken().generate();
}
@RequestMapping(method=RequestMethod.POST)
public @ResponseBody PurchaseResult purchase(@RequestBody(required=true) Order i) {
TransactionRequest request = new TransactionRequest()
.amount(i.calculateAmount())
.paymentMethodNonce(i.getNounce())
.options()
.submitForSettlement(true)
.done();
Result<Transaction> result = getGateway(i.getAuth()).transaction().sale(request);
if(result.isSuccess()) {
// Store entry and email restaurant
}
return new PurchaseResult(result.isSuccess(), result.getMessage());
}
}
BraintreePaymentService
Right here… Calculate amount runs on the order which we received from the client. This means that the prices of the elements in the order were also received from the
client.
Our client is theoretically trustworthy but if someone reverse engineers the communication protocol or the client they could just change the prices of the dishes to give
themselves a discount. However, if we just take the dish ID and calculate the price on the server this will make no difference…
This doesn’t even have to be malicious, if a price was updated and a local copy of the app still has the old price because a user hasn’t launched the app in a year. It’s
important to make these calculations in the server always. In the final version of the server this is fixed but I kept it for now in the interest of simplicity and also to
demonstrate how nuanced security issues can be.
11. @Entity
@Table(name="Restaurant")
public class RestaurantEntity {
@Id
private String id;
private String name;
private String merchantId;
private String publicKey;
private String privateKey;
private double currentVersionIOS = 1.0;
private double lastSupportedVersionIOS = 0;
private double currentVersionAnd = 1.0;
private double lastSupportedVersionAnd = 0;
private double currentVersionUWP = 1.0;
private double lastSupportedVersionUWP = 0;
private long dishListUpdateTimestamp = 1;
private String restaurantEmail;
@OneToMany
private Set<DishEntity> dishes;
@OneToMany
private Set<CategoryEntity> categories;
public RestaurantEntity() {
id = UUID.randomUUID().toString();
}
RestaurantEntity
This is the basic restaurant entity. It allows us to save the details we need of the restaurant. We’ll add a lot more into this class but for now all we need are these things.
Notice that instead of using a numeric auto generated id I chose to use a string which I’ve set to a universal unique identifier in the constructor.
Numeric id’s are sometimes problematic as one can potentially guess the id’s and scan your database. E.g. if someone notices that we have an API called listDishes that
accepts the restaurant id this might be a problem. He could scrape the API for all the dishes of all the restaurants by scanning integer values.
Scanning UUID values will take centuries or even eons.
12. @Entity
@Table(name="Purchases")
public class OrderEntity {
@Id
private String id;
@OneToMany
private Set<OrderLineEntity> dishQuanity;
private Date date;
private String notes;
public OrderEntity() {
id = UUID.randomUUID().toString();
}
OrderEntity vs. dao Order
public class Order {
private String id;
private Date date;
private boolean purchased;
private Map<Dish, Integer> dishQuantity = new
HashMap<>();
private String notes;
private String nounce;
private String auth;
public BigDecimal calculateAmount() {
BigDecimal b = BigDecimal.ZERO;
for(Map.Entry<Dish, Integer> d :
dishQuantity.entrySet()) {
b = b.add(new BigDecimal(
d.getValue() *
d.getKey().
getPrice()));
}
return b;
}
When looking at the code you will see some duplication. In this example we can see the order entity next to the order DAO.
You will notice that these values are very similar to one another, a DAO transfers data from the client or within the server and an entity stores the data. They are often
mirror images of one another but not always. In this case we can see that the DAO contains things that aren’t a part of the order.
Also the structure is different since the entity needs to map to storage structure and the DAO is designed for our convenience. This is a common practice that saves the
need to pass entities around and break the separation of the tiers within the application. It allows us to have some flexibility with our data.
13. @Controller
@RequestMapping("/ver")
public class VersionService {
@Autowired
private RestaurantRepository restaurantRepository;
@RequestMapping(method=RequestMethod.GET)
public @ResponseBody AppVersion getKey(@RequestParam(value="os", required=true) String os,
@RequestParam(value="appId", required=true) String appId) {
RestaurantEntity r = restaurantRepository.findOne(appId);
switch(os) {
case "ios":
return new AppVersion(r.getCurrentVersionIOS(), r.getLastSupportedVersionIOS());
case "and":
return new AppVersion(r.getCurrentVersionAnd(), r.getLastSupportedVersionAnd());
case "win":
return new AppVersion(r.getCurrentVersionUWP(), r.getLastSupportedVersionUWP());
}
return null;
}
}
VersionService
The versioning service allows us to detect if a new version of the app is available and effectively allows us to disable outdated versions. This is important as we might be
stuck with an out of date protocol that we wish to retire or we might need to change the server address.