Skip to content

Commit

Permalink
Usage logging
Browse files Browse the repository at this point in the history
  • Loading branch information
Ole Friis Østergaard committed Apr 26, 2011
1 parent 1428965 commit f22e33b
Show file tree
Hide file tree
Showing 15 changed files with 259 additions and 40 deletions.
9 changes: 9 additions & 0 deletions db/migrations/3.0.0-3.0.0kombit.sql
Expand Up @@ -146,3 +146,12 @@ CREATE TABLE MorOgFaroplysninger (
INDEX (ValidFrom, ValidTo),
CONSTRAINT MorOgFaroplysninger_Person_1 UNIQUE (CPR, Foraelderkode, ValidFrom)
) ENGINE=InnoDB COLLATE=utf8_danish_ci;

CREATE TABLE UsageLogEntry (
UsageLogEntryPID BIGINT(15) AUTO_INCREMENT NOT NULL PRIMARY KEY,
ClientId VARCHAR(100) NOT NULL,
Date DATETIME NOT NULL,
Type VARCHAR(200) NOT NULL,
Amount INTEGER NOT NULL,
INDEX (ClientId, UsageLogEntryPID)
) ENGINE=InnoDB COLLATE=utf8_danish_ci;
9 changes: 9 additions & 0 deletions db/schema.sql
Expand Up @@ -1159,3 +1159,12 @@ CREATE TABLE MorOgFaroplysninger (
INDEX (ValidFrom, ValidTo),
CONSTRAINT MorOgFaroplysninger_Person_1 UNIQUE (CPR, Foraelderkode, ValidFrom)
) ENGINE=InnoDB COLLATE=utf8_danish_ci;

CREATE TABLE UsageLogEntry (
UsageLogEntryPID BIGINT(15) AUTO_INCREMENT NOT NULL PRIMARY KEY,
ClientId VARCHAR(100) NOT NULL,
Date DATETIME NOT NULL,
Type VARCHAR(200) NOT NULL,
Amount INTEGER NOT NULL,
INDEX (ClientId, UsageLogEntryPID)
) ENGINE=InnoDB COLLATE=utf8_danish_ci;
Expand Up @@ -60,14 +60,15 @@ public class AtomFeedWriter {
this.viewXmlHelper = checkNotNull(viewXmlHelper);
}

public void write(Class<? extends View> viewClass, ScrollableResults records, OutputStream outputStream, boolean useFastInfoset) throws IOException {
public int write(Class<? extends View> viewClass, ScrollableResults records, OutputStream outputStream, boolean useFastInfoset) throws IOException {

checkNotNull(viewClass);
checkNotNull(records);
checkNotNull(outputStream);
records.beforeFirst();

String entityName = Views.getViewPath(viewClass);
int writtenRecords = 0;

try {
XMLStreamWriter writer;
Expand Down Expand Up @@ -96,11 +97,13 @@ public void write(Class<? extends View> viewClass, ScrollableResults records, Ou
while (records.next()) {
View view = (View)records.get(0);
writeEntry(writer, entityName, view, marshaller);
writtenRecords++;
}

// End the feed.

writer.writeEndDocument();
return writtenRecords;
}
catch (Exception e) {
throw new IOException(e);
Expand Down
Expand Up @@ -38,6 +38,7 @@
import com.trifork.stamdata.replication.replication.annotations.Registry;
import com.trifork.stamdata.replication.replication.views.View;
import com.trifork.stamdata.replication.security.SecurityManager;
import com.trifork.stamdata.replication.usagelog.UsageLogger;


@Singleton
Expand All @@ -52,9 +53,15 @@ public class RegistryServlet extends HttpServlet {
private final Provider<RecordDao> recordDao;

private final Provider<AtomFeedWriter> writers;
private final Provider<UsageLogger> usageLogger;

@Inject
RegistryServlet(@Registry Map<String, Class<? extends View>> registry, Provider<SecurityManager> securityManager, Provider<RecordDao> recordDao, Provider<AtomFeedWriter> writers) {
RegistryServlet(@Registry Map<String, Class<? extends View>> registry,
Provider<UsageLogger> usageLogger,
Provider<SecurityManager> securityManager,
Provider<RecordDao> recordDao,
Provider<AtomFeedWriter> writers) {
this.usageLogger = usageLogger;
this.registry = checkNotNull(registry);
this.recordDao = checkNotNull(recordDao);
this.writers = checkNotNull(writers);
Expand All @@ -71,7 +78,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t
//
// TODO: Log any unauthorized attempts.

if (!securityManager.get().authorize(request)) {
if (!securityManager.get().isAuthorized()) {

response.setStatus(HTTP_UNAUTHORIZED);
response.setHeader("WWW-Authenticate", "STAMDATA");
Expand Down Expand Up @@ -138,7 +145,8 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t
response.setContentType(contentType);
response.flushBuffer();

writers.get().write(entityType, records, response.getOutputStream(), useFastInfoSet);
int writtenRecords = writers.get().write(entityType, records, response.getOutputStream(), useFastInfoSet);
usageLogger.get().log(securityManager.get().getClientId(), viewName, writtenRecords);

records.close();
}
Expand Down
Expand Up @@ -17,9 +17,6 @@

package com.trifork.stamdata.replication.security;

import javax.servlet.http.HttpServletRequest;


/**
* A unit that authorizes client HTTP request access to the system in some way.
*
Expand All @@ -35,11 +32,12 @@
public interface SecurityManager {

/**
* Authenticates the request and authorizes access to the requested resource
* if the user has the right access level.
*
* @param request The HTTP request to authorize.
* @return true if the request is authorized.
*/
boolean authorize(HttpServletRequest request);
boolean isAuthorized();

/**
* @return a unique identification of the client.
*/
String getClientId();
}
Expand Up @@ -17,8 +17,6 @@

package com.trifork.stamdata.replication.security;

import javax.servlet.http.HttpServletRequest;

/**
* This security manager does not restrict access in any way.
*
Expand All @@ -29,8 +27,12 @@
public class UnrestrictedSecurityManager implements SecurityManager {

@Override
public boolean authorize(HttpServletRequest request) {

public boolean isAuthorized() {
return true;
}

@Override
public String getClientId() {
return "Unkown";
}
}
Expand Up @@ -58,4 +58,12 @@ public void save(Authorization authorization) {

em.persist(authorization);
}

public String findCvr(byte[] authorizationToken) {
checkNotNull(authorizationToken);

Query q = em.createQuery("SELECT cvr FROM Authorization a WHERE token = :token AND expiresAt > NOW()");
q.setParameter("token", authorizationToken);
return q.uniqueResult().toString();
}
}
Expand Up @@ -48,10 +48,11 @@ public class DGWSSecurityManager implements SecurityManager {
private static final SecureRandom random = new SecureRandom();

private final AuthorizationDao authorizationDao;
private final HttpServletRequest request;

@Inject
DGWSSecurityManager(AuthorizationDao authorizationDao) {

DGWSSecurityManager(AuthorizationDao authorizationDao, HttpServletRequest request) {
this.request = request;
this.authorizationDao = checkNotNull(authorizationDao);
}

Expand Down Expand Up @@ -96,14 +97,14 @@ public String issueAuthenticationToken(String cvr, Class<? extends View> viewCla
}

@Override
public boolean authorize(HttpServletRequest request) {
public boolean isAuthorized() {

checkNotNull(request);

// TODO: Cache the ~20 most recent tokens. We can use a priority queue
// for efficiency.

byte[] token = parseAuthenticationToken(request.getHeader("Authentication"));
byte[] token = authenticationToken();

if (token == null) return false;

Expand All @@ -116,12 +117,16 @@ public boolean authorize(HttpServletRequest request) {
return authorizationDao.isTokenValid(token, viewName);
}

protected byte[] parseAuthenticationToken(String header) {
@Override
public String getClientId() {
return "CVR:" + authorizationDao.findCvr(authenticationToken());
}

protected byte[] authenticationToken() {
String header = request.getHeader("Authentication");
if (header == null) return null;

Matcher matcher = authenticationRegex.matcher(header);

return matcher.find() ? Base64.decode(matcher.group(1)) : null;
}
}
@@ -0,0 +1,21 @@
package com.trifork.stamdata.replication.usagelog;

import java.util.Date;

import org.hibernate.Session;

import com.trifork.stamdata.replication.replication.views.usagelog.UsageLogEntry;

public class UsageLogger {

private final Session session;

public UsageLogger(Session session) {
this.session = session;
}

public void log(String clientId, String type, int amount) {
session.save(new UsageLogEntry(clientId, new Date(), type, amount));
}

}
Expand Up @@ -38,29 +38,35 @@
import org.hibernate.ScrollableResults;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;

import com.google.inject.Provider;
import com.trifork.stamdata.replication.mocks.MockEntity;
import com.trifork.stamdata.replication.replication.views.View;
import com.trifork.stamdata.replication.security.SecurityManager;
import com.trifork.stamdata.replication.usagelog.UsageLogger;


@RunWith(MockitoJUnitRunner.class)
public class RegistryServletTest {

private RegistryServlet servlet;

private HttpServletRequest request;
private HttpServletResponse response;

private SecurityManager securityManager;
private @Mock SecurityManager securityManager;
private Map<String, Class<? extends View>> registry;
private Map<String, Class<? extends View>> mappedClasses;
private RecordDao recordDao;
private AtomFeedWriter writer;
private @Mock RecordDao recordDao;
private @Mock AtomFeedWriter writer;
private @Mock UsageLogger usageLogger;
private String requestPath;
private String countParam;
private String clientId = "CVR:12345678";
private ScrollableResults records;
private String acceptHeader;
private boolean authorized;
Expand All @@ -74,18 +80,18 @@ public void setUp() throws Exception {
registry = new HashMap<String, Class<? extends View>>();

Provider securityManagerProvider = mock(Provider.class);
securityManager = mock(SecurityManager.class);
when(securityManagerProvider.get()).thenReturn(securityManager);

Provider recordDaoProvider = mock(Provider.class);
recordDao = mock(RecordDao.class);
when(recordDaoProvider.get()).thenReturn(recordDao);

Provider writerProvider = mock(Provider.class);
writer = mock(AtomFeedWriter.class);
when(writerProvider.get()).thenReturn(writer);

Provider usageLoggerProvider = mock(Provider.class);
when(usageLoggerProvider.get()).thenReturn(usageLogger);

servlet = new RegistryServlet(registry, securityManagerProvider, recordDaoProvider, writerProvider);
servlet = new RegistryServlet(registry, usageLoggerProvider, securityManagerProvider, recordDaoProvider, writerProvider);

setUpValidRequest();
}
Expand Down Expand Up @@ -127,6 +133,16 @@ public void Should_return_a_web_link_for_the_next_page_if_there_are_more_records
get();
verify(response).addHeader("Link", String.format("<stamdata://foo/bar/v1?offset=%s>; rel=\"next\"", nextOffset));
}

@Test
public void Should_perform_usage_logging() throws Exception {
when(writer.write(Matchers.<Class<? extends View>>anyObject(), Matchers.<ScrollableResults>anyObject(), Matchers.<OutputStream>anyObject(), Matchers.anyBoolean()))
.thenReturn(2);

get();

verify(usageLogger).log(clientId, "foo/bar/v1", 2);
}

@Test
public void Should_not_return_a_web_link_if_there_are_no_more_records() throws Exception {
Expand Down Expand Up @@ -176,7 +192,8 @@ public void Should_return_records_from_the_correct_offset() throws Exception {

public void get() throws Exception {

when(securityManager.authorize(request)).thenReturn(authorized);
when(securityManager.isAuthorized()).thenReturn(authorized);
when(securityManager.getClientId()).thenReturn(clientId);
when(recordDao.findPage(MockEntity.class, "2222222222", new Date(1111111111000L), parseInt(countParam))).thenReturn(records);
when(request.getPathInfo()).thenReturn(requestPath);
when(request.getHeader("Accept")).thenReturn(acceptHeader);
Expand Down
Expand Up @@ -2,20 +2,19 @@

import static com.trifork.stamdata.replication.TimeHelper.tomorrow;
import static com.trifork.stamdata.replication.TimeHelper.yesterday;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import org.hibernate.Session;
import org.hibernate.type.YesNoType;
import org.junit.Assert;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;

import com.trifork.stamdata.replication.DatabaseHelper;
import com.trifork.stamdata.replication.TimeHelper;
import com.trifork.stamdata.replication.TokenHelper;
import com.trifork.stamdata.replication.mocks.MockEntity;

Expand All @@ -29,20 +28,18 @@ public class AuthorizationDaoTest {

@BeforeClass
public static void init() throws Exception {

DatabaseHelper db = new DatabaseHelper(Authorization.class);
session = db.openSession();
}

@Before
public void setUp() {

dao = new AuthorizationDao(session);
session.beginTransaction();
}

@After
public void tearDown() {

session.getTransaction().rollback();
}

Expand Down Expand Up @@ -81,4 +78,13 @@ public void should_return_false_if_the_authorization_is_not_valid_from_the_reque

assertFalse(dao.isTokenValid(token, "bas/baz/v2"));
}

@Test
public void can_find_cvr_from_authentication_token() {
Authorization authorization = new Authorization(MockEntity.class, "12345678", tomorrow(), token);

dao.save(authorization);

assertEquals("12345678", dao.findCvr(token));
}
}

0 comments on commit f22e33b

Please sign in to comment.