Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,19 @@ $ mvn jetty:run
Navigate to:

http://localhost:8080/

API demos
=========

The home page links to a set of demos exercising the Castle SDK directly:

* **Lists & list items API** (`/lists-demo`) — creates a list, adds an item,
queries the items, lists all lists and deletes the list.
* **Events API** (`/events-demo`) — fetches the event schema and queries events.
* **Privacy data request** (`/privacy-demo?userId=...`) — requests the data
Castle holds for a user.
* **Received webhooks** (`/webhooks`) — `POST` a Castle webhook with a valid
`X-Castle-Signature` header to have it verified against the raw request body
and listed on the page.

These demos require `castle-java` 2.2.0.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<groupId>io.castle.example</groupId>
<artifactId>castle-example</artifactId>
<packaging>war</packaging>
<version>1.4.0</version>
<version>1.5.0</version>
<name>Castle Java JDK Example</name>
<url>https://github.com/castle/castle-java-example</url>
<dependencies>
Expand All @@ -27,7 +27,7 @@
<dependency>
<groupId>io.castle</groupId>
<artifactId>castle-java</artifactId>
<version>1.4.0</version>
<version>2.2.0</version>
</dependency>
</dependencies>

Expand Down
45 changes: 45 additions & 0 deletions src/main/java/io/castle/example/EventsDemoServlet.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package io.castle.example;

import com.google.common.collect.ImmutableMap;
import io.castle.client.Castle;
import io.castle.client.api.CastleApi;
import io.castle.client.model.CastleResponse;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
* Demonstrates the Events API (schema and query).
*/
@WebServlet("/events-demo")
public class EventsDemoServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf-8");
PrintWriter out = resp.getWriter();
out.println("<html><head><title>Events API demo</title></head><body>");
out.println("<h2>Events API demo</h2><p><a href=\"/\">&larr; Home</a></p>");

CastleApi client = Castle.instance().client();

try {
CastleResponse schema = client.eventsSchema();
ListsDemoServlet.render(out, "eventsSchema", schema);

CastleResponse query = client.queryEvents(ImmutableMap.builder()
.put("filters", ImmutableMap.of())
.build());
ListsDemoServlet.render(out, "queryEvents", query);
} catch (Exception e) {
out.println("<pre style=\"color:red\">Error: " + e.getMessage() + "</pre>");
}

out.println("</body></html>");
}
}
70 changes: 70 additions & 0 deletions src/main/java/io/castle/example/ListsDemoServlet.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package io.castle.example;

import com.google.common.collect.ImmutableMap;
import io.castle.client.Castle;
import io.castle.client.api.CastleApi;
import io.castle.client.model.CastleResponse;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
* Demonstrates the Lists and List items API end to end:
* create a list, add an item, query the items, list all lists and delete the list.
*/
@WebServlet("/lists-demo")
public class ListsDemoServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf-8");
PrintWriter out = resp.getWriter();
out.println("<html><head><title>Lists API demo</title></head><body>");
out.println("<h2>Lists API demo</h2><p><a href=\"/\">&larr; Home</a></p>");

CastleApi client = Castle.instance().client();

try {
CastleResponse created = client.createList(ImmutableMap.builder()
.put("name", "Example trusted IPs")
.put("description", "Created by the castle-java example app")
.put("color", "$green")
.put("primary_field", "context.ip")
.build());
render(out, "createList", created);

String listId = created.json().getAsJsonObject().get("id").getAsString();

CastleResponse item = client.createListItem(listId, ImmutableMap.builder()
.put("primary_value", "1.2.3.4")
.put("comment", "added from the example app")
.build());
render(out, "createListItem", item);

CastleResponse items = client.queryListItems(listId, ImmutableMap.builder()
.put("filters", ImmutableMap.of())
.build());
render(out, "queryListItems", items);

CastleResponse all = client.getAllLists();
render(out, "getAllLists", all);

CastleResponse deleted = client.deleteList(listId);
render(out, "deleteList", deleted);
} catch (Exception e) {
out.println("<pre style=\"color:red\">Error: " + e.getMessage() + "</pre>");
}

out.println("</body></html>");
}

static void render(PrintWriter out, String label, CastleResponse response) {
out.println("<h3>" + label + " (HTTP " + (response.isSuccessful() ? "2xx" : "error") + ")</h3>");
out.println("<pre>" + response.json() + "</pre>");
}
}
47 changes: 47 additions & 0 deletions src/main/java/io/castle/example/PrivacyDemoServlet.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package io.castle.example;

import com.google.common.collect.ImmutableMap;
import io.castle.client.Castle;
import io.castle.client.api.CastleApi;
import io.castle.client.model.CastleResponse;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
* Demonstrates the privacy data-request API.
*/
@WebServlet("/privacy-demo")
public class PrivacyDemoServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf-8");
PrintWriter out = resp.getWriter();
out.println("<html><head><title>Privacy API demo</title></head><body>");
out.println("<h2>Privacy API demo</h2><p><a href=\"/\">&larr; Home</a></p>");

String userId = req.getParameter("userId");
if (userId == null || userId.isEmpty()) {
userId = "1";
}

CastleApi client = Castle.instance().client();

try {
CastleResponse response = client.requestUserData(ImmutableMap.builder()
.put("user_id", userId)
.build());
ListsDemoServlet.render(out, "requestUserData(" + userId + ")", response);
} catch (Exception e) {
out.println("<pre style=\"color:red\">Error: " + e.getMessage() + "</pre>");
}

out.println("</body></html>");
}
}
70 changes: 70 additions & 0 deletions src/main/java/io/castle/example/WebhookServlet.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package io.castle.example;

import io.castle.client.Castle;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
* Receives Castle webhooks and verifies the {@code X-Castle-Signature} header
* against the raw request body before trusting the payload.
*/
@WebServlet("/webhooks")
public class WebhookServlet extends HttpServlet {

private static final List<String> RECEIVED = new CopyOnWriteArrayList<String>();

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
byte[] body = readBody(req);

boolean valid = Castle.instance().verifyWebhookSignature(req, body);

if (!valid) {
resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
resp.getWriter().println("invalid signature");
return;
}

RECEIVED.add(0, new String(body, "UTF-8"));
resp.setStatus(HttpServletResponse.SC_OK);
resp.getWriter().println("ok");
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf-8");
PrintWriter out = resp.getWriter();
out.println("<html><head><title>Webhooks</title></head><body>");
out.println("<h2>Received webhooks</h2><p><a href=\"/\">&larr; Home</a></p>");
out.println("<p>POST a Castle webhook to <code>/webhooks</code> with a valid "
+ "<code>X-Castle-Signature</code> header to see it verified and listed here.</p>");
if (RECEIVED.isEmpty()) {
out.println("<p><em>No verified webhooks received yet.</em></p>");
}
for (String payload : RECEIVED) {
out.println("<pre>" + payload + "</pre>");
}
out.println("</body></html>");
}

private byte[] readBody(HttpServletRequest req) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
ServletInputStream in = req.getInputStream();
byte[] chunk = new byte[4096];
int read;
while ((read = in.read(chunk)) != -1) {
buffer.write(chunk, 0, read);
}
return buffer.toByteArray();
}
}
18 changes: 18 additions & 0 deletions src/main/webapp/index.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@
<div>
<a href="password_change_form.jsp">Change your password</a>
</div>
<div>
<h3>API demos</h3>
<ul>
<li><a href="lists-demo">Lists &amp; list items API</a></li>
<li><a href="events-demo">Events API</a></li>
<li><a href="privacy-demo?userId=<c:out value='${currentUser}'/>">Privacy data request</a></li>
<li><a href="webhooks">Received webhooks</a></li>
</ul>
</div>
<div>
<form action="logout" method="post">
<input type="submit" value="Logout"/>
Expand Down Expand Up @@ -72,6 +81,15 @@
<div>
<a href="forgot_password.jsp">Forgot your password?</a>
</div>
<div>
<h3>API demos</h3>
<ul>
<li><a href="lists-demo">Lists &amp; list items API</a></li>
<li><a href="events-demo">Events API</a></li>
<li><a href="privacy-demo">Privacy data request</a></li>
<li><a href="webhooks">Received webhooks</a></li>
</ul>
</div>
<div>
<h2>Test data</h2>
<p>The example application contains a built-in list of users with the following logins:</p>
Expand Down