Skip to content
This repository has been archived by the owner on May 9, 2019. It is now read-only.

Commit

Permalink
Added security and stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
jroper committed Sep 21, 2016
1 parent f8253b6 commit e554d6a
Show file tree
Hide file tree
Showing 13 changed files with 382 additions and 47 deletions.
88 changes: 58 additions & 30 deletions build.sbt
@@ -1,106 +1,134 @@
lazy val root = (project in file("."))
.aggregate(itemApi, itemImpl, biddingApi, biddingImpl, userApi, userImpl, webGateway)

organization in ThisBuild := "com.example"

// the Scala version that will be used for cross-compiled libraries
scalaVersion in ThisBuild := "2.11.7"

lazy val itemApi = project("item-api")
lazy val security = (project in file("security"))
.settings(commonSettings: _*)
.settings(
version := "1.0-SNAPSHOT",
libraryDependencies ++= Seq(
lagomJavadslApi,
lagomJavadslServer % Optional
)
)

lazy val itemApi = (project in file("item-api"))
.settings(commonSettings: _*)
.settings(
version := "1.0-SNAPSHOT",
libraryDependencies += lagomJavadslApi
)
.dependsOn(security)

lazy val itemImpl = project("item-impl")
lazy val itemImpl = (project in file("item-impl"))
.settings(commonSettings: _*)
.enablePlugins(LagomJava)
.settings(
version := "1.0-SNAPSHOT",
libraryDependencies ++= Seq(
lagomJavadslPersistence,
lagomJavadslPersistenceCassandra,
lagomJavadslTestKit
)
)
.settings(lagomForkedTestSettings: _*)
.dependsOn(itemApi)

lazy val biddingApi = project("bidding-api")
.settings(version := "1.0-SNAPSHOT")
lazy val biddingApi = (project in file("bidding-api"))
.settings(commonSettings: _*)
.settings(
version := "1.0-SNAPSHOT",
libraryDependencies += lagomJavadslApi
)
.dependsOn(security)

lazy val biddingImpl = project("bidding-impl")
.settings(version := "1.0-SNAPSHOT")
lazy val biddingImpl = (project in file("bidding-impl"))
.settings(commonSettings: _*)
// .enablePlugins(LagomJava)
.dependsOn(biddingApi, itemApi)
.settings(
version := "1.0-SNAPSHOT",
libraryDependencies ++= Seq(
lagomJavadslPersistence,
lagomJavadslPersistenceCassandra,
lagomJavadslTestKit
),
javacOptions += "-Xlint",
maxErrors := 10000

)

lazy val searchApi = project("search-api")
.settings(version := "1.0-SNAPSHOT")
lazy val searchApi = (project in file("search-api"))
.settings(commonSettings: _*)
.settings(
version := "1.0-SNAPSHOT",
libraryDependencies += lagomJavadslApi
)
.dependsOn(security)

lazy val searchImpl = project("search-impl")
.settings(version := "1.0-SNAPSHOT")
lazy val searchImpl = (project in file("search-impl"))
.settings(commonSettings: _*)
// .enablePlugins(LagomJava)
.dependsOn(searchApi, itemApi, biddingApi)
.settings(
version := "1.0-SNAPSHOT",
libraryDependencies ++= Seq(
lagomJavadslPersistence,
lagomJavadslPersistenceCassandra,
lagomJavadslTestKit
)
)

lazy val transactionApi = project("transaction-api")
.settings(version := "1.0-SNAPSHOT")
lazy val transactionApi = (project in file("transaction-api"))
.settings(commonSettings: _*)
.dependsOn(itemApi)
.settings(
version := "1.0-SNAPSHOT",
libraryDependencies += lagomJavadslApi
)
.dependsOn(security)

lazy val transactionImpl = project("transaction-impl")
.settings(version := "1.0-SNAPSHOT")
lazy val transactionImpl = (project in file("transaction-impl"))
.settings(commonSettings: _*)
// .enablePlugins(LagomJava)
.dependsOn(transactionApi, biddingApi)
.settings(
version := "1.0-SNAPSHOT",
libraryDependencies ++= Seq(
lagomJavadslPersistence,
lagomJavadslPersistenceCassandra,
lagomJavadslTestKit
)
)

lazy val userApi = project("user-api")
.settings(version := "1.0-SNAPSHOT")
lazy val userApi = (project in file("user-api"))
.settings(commonSettings: _*)
.settings(
version := "1.0-SNAPSHOT",
libraryDependencies += lagomJavadslApi
)
.dependsOn(security)

lazy val userImpl = project("user-impl")
.settings(version := "1.0-SNAPSHOT")
lazy val userImpl = (project in file("user-impl"))
.settings(commonSettings: _*)
.enablePlugins(LagomJava)
.dependsOn(userApi)
.settings(
libraryDependencies += lagomJavadslPersistence
version := "1.0-SNAPSHOT",
libraryDependencies += lagomJavadslPersistenceCassandra
)

lazy val webGateway = project("web-gateway")
.settings(version := "1.0-SNAPSHOT")
lazy val webGateway = (project in file("web-gateway"))
.settings(commonSettings: _*)
.enablePlugins(PlayJava && LagomPlay)
.dependsOn(transactionApi, biddingApi, itemApi, searchApi, userApi)
.settings(
version := "1.0-SNAPSHOT",
libraryDependencies += lagomJavadslClient
)

def project(id: String) = Project(id, base = file(id))
.settings(eclipseSettings: _*)
.settings(javacOptions ++= Seq("-encoding", "UTF-8", "-source", "1.8", "-target", "1.8", "-Xlint:unchecked", "-Xlint:deprecation", "-parameters"))
def commonSettings: Seq[Setting[_]] = eclipseSettings ++ Seq(
javacOptions ++= Seq("-encoding", "UTF-8", "-source", "1.8", "-target", "1.8", "-Xlint:unchecked", "-Xlint:deprecation", "-parameters")
)

// Configuration of sbteclipse
// Needed for importing the project into Eclipse
Expand All @@ -116,4 +144,4 @@ lazy val eclipseSettings = Seq(
unmanagedSourceDirectories in Test := Seq((javaSource in Test).value)
)

lagomCassandraCleanOnStart := false
lagomCassandraCleanOnStart in ThisBuild := false
Expand Up @@ -9,6 +9,7 @@

import akka.Done;
import akka.NotUsed;
import com.example.auction.security.SecurityHeaderFilter;
import com.lightbend.lagom.javadsl.api.Descriptor;
import com.lightbend.lagom.javadsl.api.Service;
import com.lightbend.lagom.javadsl.api.ServiceCall;
Expand Down Expand Up @@ -40,6 +41,7 @@ default Descriptor descriptor() {
restCall(Method.POST, "/api/item/:id/start", this::startAuction),
pathCall("/api/item/:id", this::getItem),
pathCall("/api/item?userId&pageNo&pageSize", this::getItemsForUser)
).withPathParamSerializer(UUID.class, PathParamSerializers.required("UUID", UUID::fromString, UUID::toString));
).withPathParamSerializer(UUID.class, PathParamSerializers.required("UUID", UUID::fromString, UUID::toString))
.withHeaderFilter(SecurityHeaderFilter.INSTANCE);
}
}
Expand Up @@ -6,6 +6,7 @@
import com.example.auction.item.api.ItemService;
import com.example.auction.item.api.ItemStatus;
import com.lightbend.lagom.javadsl.api.ServiceCall;
import com.lightbend.lagom.javadsl.api.transport.Forbidden;
import com.lightbend.lagom.javadsl.api.transport.NotFound;
import com.lightbend.lagom.javadsl.persistence.PersistentEntityRef;
import com.lightbend.lagom.javadsl.persistence.PersistentEntityRegistry;
Expand All @@ -16,6 +17,8 @@
import java.util.Optional;
import java.util.UUID;

import static com.example.auction.security.ServerSecurity.*;

@Singleton
public class ItemServiceImpl implements ItemService {

Expand All @@ -30,25 +33,30 @@ public ItemServiceImpl(PersistentEntityRegistry registry) {

@Override
public ServiceCall<Item, Item> createItem() {
return item -> {
return authenticated(userId -> item -> {
if (!userId.equals(item.getCreator())) {
throw new Forbidden("User " + userId + " can't created an item on behalf of " + item.getCreator());
}
UUID itemId = UUID.randomUUID();
PItem pItem = new PItem(itemId, item.getCreator(), item.getTitle(), item.getDescription(),
item.getCurrencyId(), item.getIncrement(), item.getReservePrice(), item.getAuctionDuration());
return entityRef(itemId).ask(new PItemCommand.CreateItem(pItem)).thenApply(done -> convertItem(pItem));
};
});
}

@Override
public ServiceCall<Item, Done> updateItem(UUID id) {
return item -> {
return authenticated(userId -> item -> {
// todo implement
return null;
};
});
}

@Override
public ServiceCall<NotUsed, Done> startAuction(UUID id) {
return req -> entityRef(id).ask(PItemCommand.StartAuction.INSTANCE);
return authenticated(userId -> req ->
entityRef(id).ask(new PItemCommand.StartAuction(userId))
);
}

@Override
Expand Down
Expand Up @@ -6,6 +6,7 @@
import com.lightbend.lagom.serialization.Jsonable;

import java.util.Optional;
import java.util.UUID;

public interface PItemCommand extends Jsonable {

Expand Down Expand Up @@ -42,7 +43,32 @@ public int hashCode() {
}
}

enum StartAuction implements PItemCommand, PersistentEntity.ReplyType<Done> {
INSTANCE
final class StartAuction implements PItemCommand, PersistentEntity.ReplyType<Done> {
private final UUID userId;

@JsonCreator
public StartAuction(UUID userId) {
this.userId = userId;
}

public UUID getUserId() {
return userId;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

StartAuction that = (StartAuction) o;

return userId.equals(that.userId);

}

@Override
public int hashCode() {
return userId.hashCode();
}
}
}
@@ -1,6 +1,7 @@
package com.example.auction.item.impl;

import akka.Done;
import com.lightbend.lagom.javadsl.api.transport.Forbidden;
import com.lightbend.lagom.javadsl.persistence.PersistentEntity;

import com.example.auction.item.impl.PItemCommand.*;
Expand Down Expand Up @@ -49,9 +50,14 @@ private Behavior created(PItemState state) {

builder.setReadOnlyCommandHandler(GetItem.class, this::getItem);

builder.setCommandHandler(StartAuction.class, (start, ctx) ->
ctx.thenPersist(new AuctionStarted(entityUuid()), evt -> ctx.reply(Done.getInstance()))
);
builder.setCommandHandler(StartAuction.class, (start, ctx) -> {
if (start.getUserId().equals(state().getItem().get().getCreator())) {
return ctx.thenPersist(new AuctionStarted(entityUuid()), evt -> ctx.reply(Done.getInstance()));
} else {
ctx.invalidCommand("User " + start.getUserId() + " is not allowed to start this auction");
return ctx.done();
}
});
builder.setEventHandlerChangingBehavior(AuctionStarted.class, evt -> auction(state().start(Instant.now())));

return builder.build();
Expand Down
@@ -0,0 +1,25 @@
package com.example.auction.security;

import com.lightbend.lagom.javadsl.api.security.ServicePrincipal;
import com.lightbend.lagom.javadsl.api.transport.RequestHeader;

import java.util.Optional;
import java.util.UUID;
import java.util.function.Function;

public class ClientSecurity {

/**
* Authenticate a client request.
*/
public static final Function<RequestHeader, RequestHeader> authenticate(UUID userId) {
return request -> {
Optional<ServicePrincipal> service = request.principal()
.filter(p -> p instanceof ServicePrincipal)
.map(p -> (ServicePrincipal) p);

return request.withPrincipal(UserPrincipal.of(userId, service));
};
}

}
@@ -0,0 +1,54 @@
package com.example.auction.security;

import com.lightbend.lagom.javadsl.api.security.ServicePrincipal;
import com.lightbend.lagom.javadsl.api.security.UserAgentHeaderFilter;
import com.lightbend.lagom.javadsl.api.transport.HeaderFilter;
import com.lightbend.lagom.javadsl.api.transport.RequestHeader;
import com.lightbend.lagom.javadsl.api.transport.ResponseHeader;

import java.security.Principal;
import java.util.Optional;
import java.util.UUID;

public final class SecurityHeaderFilter implements HeaderFilter {

private SecurityHeaderFilter() {}

public static final HeaderFilter INSTANCE = HeaderFilter.composite(new SecurityHeaderFilter(), new UserAgentHeaderFilter());

@Override
public RequestHeader transformClientRequest(RequestHeader request) {
if (request.principal().isPresent()) {
Principal principal = request.principal().get();
if (principal instanceof UserPrincipal) {
return request.withHeader("User-Id", ((UserPrincipal) principal).getUserId().toString());
}
}
return request;
}

@Override
public RequestHeader transformServerRequest(RequestHeader request) {
Optional<String> userId = request.getHeader("User-Id");
if (userId.isPresent()) {
Optional<ServicePrincipal> service = request.principal()
.filter(p -> p instanceof ServicePrincipal)
.map(p -> (ServicePrincipal) p);

UUID userUuid = UUID.fromString(userId.get());

return request.withPrincipal(UserPrincipal.of(userUuid, service));
}
return request;
}

@Override
public ResponseHeader transformServerResponse(ResponseHeader response, RequestHeader request) {
return response;
}

@Override
public ResponseHeader transformClientResponse(ResponseHeader response, RequestHeader request) {
return response;
}
}

0 comments on commit e554d6a

Please sign in to comment.