diff options
author | Jesse Morgan <jesse@jesterpm.net> | 2014-03-15 10:19:35 -0700 |
---|---|---|
committer | Jesse Morgan <jesse@jesterpm.net> | 2014-03-15 10:19:35 -0700 |
commit | 1494010751e89a6ed748ed90d702d5574ffbdd9b (patch) | |
tree | 626f0229e4309df4b7fc23b21aab3b38824faa95 /src/com/p4square/grow/backend | |
parent | 21f1ddafde8a552e4671d2bc574aa97fa86f95a3 (diff) |
Adding authenticated outside access to backend.
Diffstat (limited to 'src/com/p4square/grow/backend')
-rw-r--r-- | src/com/p4square/grow/backend/BackendVerifier.java | 92 | ||||
-rw-r--r-- | src/com/p4square/grow/backend/GrowBackend.java | 32 | ||||
-rw-r--r-- | src/com/p4square/grow/backend/resources/AccountResource.java | 40 |
3 files changed, 146 insertions, 18 deletions
diff --git a/src/com/p4square/grow/backend/BackendVerifier.java b/src/com/p4square/grow/backend/BackendVerifier.java new file mode 100644 index 0000000..83160a9 --- /dev/null +++ b/src/com/p4square/grow/backend/BackendVerifier.java @@ -0,0 +1,92 @@ +/* + * Copyright 2014 Jesse Morgan + */ + +package com.p4square.grow.backend; + +import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import org.apache.commons.codec.binary.Hex; + +import org.restlet.security.SecretVerifier; + +import com.p4square.grow.model.UserRecord; +import com.p4square.grow.provider.Provider; + +/** + * Verify the given credentials against the users with backend access. + */ +public class BackendVerifier extends SecretVerifier { + + private final Provider<String, UserRecord> mUserProvider; + + public BackendVerifier(Provider<String, UserRecord> userProvider) { + mUserProvider = userProvider; + } + + @Override + public int verify(String identifier, char[] secret) { + if (identifier == null) { + throw new IllegalArgumentException("Null identifier"); + } + + if (secret == null) { + throw new IllegalArgumentException("Null secret"); + } + + // Does the user exist? + UserRecord user; + try { + user = mUserProvider.get(identifier); + if (user == null) { + return RESULT_UNKNOWN; + } + + } catch (IOException e) { + return RESULT_UNKNOWN; + } + + // Does the user have a backend password? + String storedHash = user.getBackendPasswordHash(); + if (storedHash == null) { + // This user doesn't have access + return RESULT_INVALID; + } + + // Validate the password. + try { + String hashedInput = hashPassword(secret); + if (hashedInput.equals(storedHash)) { + return RESULT_VALID; + } + + } catch (NoSuchAlgorithmException e) { + return RESULT_UNSUPPORTED; + } + + // If all else fails, fail. + return RESULT_INVALID; + } + + /** + * Hash the given secret. + */ + public static String hashPassword(char[] secret) throws NoSuchAlgorithmException { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + + // Convert the char[] to byte[] + // FIXME This approach is incorrectly truncating multibyte + // characters. + byte[] b = new byte[secret.length]; + for (int i = 0; i < secret.length; i++) { + b[i] = (byte) secret[i]; + } + + md.update(b); + + byte[] hash = md.digest(); + return new String(Hex.encodeHex(hash)); + } +} diff --git a/src/com/p4square/grow/backend/GrowBackend.java b/src/com/p4square/grow/backend/GrowBackend.java index f844feb..683c99b 100644 --- a/src/com/p4square/grow/backend/GrowBackend.java +++ b/src/com/p4square/grow/backend/GrowBackend.java @@ -22,17 +22,19 @@ import com.p4square.grow.backend.db.CassandraProviderImpl; import com.p4square.grow.backend.db.CassandraCollectionProvider; import com.p4square.grow.backend.db.CassandraTrainingRecordProvider; +import com.p4square.grow.model.Message; +import com.p4square.grow.model.MessageThread; +import com.p4square.grow.model.Playlist; import com.p4square.grow.model.Question; import com.p4square.grow.model.TrainingRecord; -import com.p4square.grow.model.Playlist; -import com.p4square.grow.model.MessageThread; -import com.p4square.grow.model.Message; +import com.p4square.grow.model.UserRecord; import com.p4square.grow.provider.CollectionProvider; +import com.p4square.grow.provider.DelegateProvider; import com.p4square.grow.provider.Provider; import com.p4square.grow.provider.ProvidesQuestions; import com.p4square.grow.provider.ProvidesTrainingRecords; -import com.p4square.grow.provider.QuestionProvider; +import com.p4square.grow.provider.ProvidesUserRecords; import com.p4square.grow.backend.resources.AccountResource; import com.p4square.grow.backend.resources.BannerResource; @@ -51,7 +53,8 @@ import com.p4square.grow.backend.feed.TopicResource; * @author Jesse Morgan <jesse@jesterpm.net> */ public class GrowBackend extends Application - implements ProvidesQuestions, ProvidesTrainingRecords, FeedDataProvider { + implements ProvidesQuestions, ProvidesTrainingRecords, FeedDataProvider, + ProvidesUserRecords { private static final String DEFAULT_COLUMN = "value"; private final static Logger LOG = Logger.getLogger(GrowBackend.class); @@ -59,6 +62,8 @@ public class GrowBackend extends Application private final Config mConfig; private final CassandraDatabase mDatabase; + private final Provider<String, UserRecord> mUserRecordProvider; + private final Provider<String, Question> mQuestionProvider; private final CassandraTrainingRecordProvider mTrainingRecordProvider; @@ -73,7 +78,16 @@ public class GrowBackend extends Application mConfig = config; mDatabase = new CassandraDatabase(); - mQuestionProvider = new QuestionProvider<CassandraKey>(new CassandraProviderImpl<Question>(mDatabase, Question.class)) { + mUserRecordProvider = new DelegateProvider<String, CassandraKey, UserRecord>( + new CassandraProviderImpl<UserRecord>(mDatabase, UserRecord.class)) { + @Override + public CassandraKey makeKey(String userid) { + return new CassandraKey("accounts", userid, DEFAULT_COLUMN); + } + }; + + mQuestionProvider = new DelegateProvider<String, CassandraKey, Question>( + new CassandraProviderImpl<Question>(mDatabase, Question.class)) { @Override public CassandraKey makeKey(String questionId) { return new CassandraKey("strings", "/questions/" + questionId, DEFAULT_COLUMN); @@ -93,6 +107,7 @@ public class GrowBackend extends Application Router router = new Router(getContext()); // Account API + router.attach("/accounts", AccountResource.class); router.attach("/accounts/{userId}", AccountResource.class); // Survey API @@ -153,6 +168,11 @@ public class GrowBackend extends Application } @Override + public Provider<String, UserRecord> getUserRecordProvider() { + return mUserRecordProvider; + } + + @Override public Provider<String, Question> getQuestionProvider() { return mQuestionProvider; } diff --git a/src/com/p4square/grow/backend/resources/AccountResource.java b/src/com/p4square/grow/backend/resources/AccountResource.java index f3404c0..2ac7061 100644 --- a/src/com/p4square/grow/backend/resources/AccountResource.java +++ b/src/com/p4square/grow/backend/resources/AccountResource.java @@ -9,12 +9,15 @@ import java.io.IOException; import org.restlet.data.Status; import org.restlet.resource.ServerResource; import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; + +import org.restlet.ext.jackson.JacksonRepresentation; import org.apache.log4j.Logger; -import com.p4square.grow.backend.GrowBackend; -import com.p4square.grow.backend.db.CassandraDatabase; +import com.p4square.grow.model.UserRecord; +import com.p4square.grow.provider.Provider; +import com.p4square.grow.provider.ProvidesUserRecords; +import com.p4square.grow.provider.JsonEncodedProvider; /** * Stores a document about a user. @@ -24,7 +27,7 @@ import com.p4square.grow.backend.db.CassandraDatabase; public class AccountResource extends ServerResource { private static final Logger LOG = Logger.getLogger(AccountResource.class); - private CassandraDatabase mDb; + private Provider<String, UserRecord> mUserRecordProvider; private String mUserId; @@ -32,8 +35,8 @@ public class AccountResource extends ServerResource { public void doInit() { super.doInit(); - final GrowBackend backend = (GrowBackend) getApplication(); - mDb = backend.getDatabase(); + final ProvidesUserRecords backend = (ProvidesUserRecords) getApplication(); + mUserRecordProvider = backend.getUserRecordProvider(); mUserId = getAttribute("userId"); } @@ -43,14 +46,22 @@ public class AccountResource extends ServerResource { */ @Override protected Representation get() { - String result = mDb.getKey("accounts", mUserId); + try { + UserRecord result = mUserRecordProvider.get(mUserId); + + if (result == null) { + setStatus(Status.CLIENT_ERROR_NOT_FOUND); + return null; + } + + JacksonRepresentation<UserRecord> rep = new JacksonRepresentation<UserRecord>(result); + rep.setObjectMapper(JsonEncodedProvider.MAPPER); + return rep; - if (result == null) { - setStatus(Status.CLIENT_ERROR_NOT_FOUND); + } catch (IOException e) { + setStatus(Status.SERVER_ERROR_INTERNAL); return null; } - - return new StringRepresentation(result); } /** @@ -59,7 +70,12 @@ public class AccountResource extends ServerResource { @Override protected Representation put(Representation entity) { try { - mDb.putKey("accounts", mUserId, entity.getText()); + JacksonRepresentation<UserRecord> representation = + new JacksonRepresentation<>(entity, UserRecord.class); + representation.setObjectMapper(JsonEncodedProvider.MAPPER); + UserRecord record = representation.getObject(); + + mUserRecordProvider.put(mUserId, record); setStatus(Status.SUCCESS_NO_CONTENT); } catch (IOException e) { |