use of uk.ac.cam.cl.dtg.isaac.dto.users.AnonymousUserDTO in project isaac-api by isaacphysics.
the class GameboardsFacadeTest method isaacEndPoint_checkEmptyGameboardCausesErrorNoUser_SegueErrorResponseShouldBeReturned.
/**
* Verify that when an empty gameboard is noticed a 204 is returned.
*
* @throws NoUserLoggedInException
* @throws ContentManagerException
*/
@Test
@PowerMockIgnore({ "javax.ws.*" })
public final void isaacEndPoint_checkEmptyGameboardCausesErrorNoUser_SegueErrorResponseShouldBeReturned() throws NoWildcardException, SegueDatabaseException, NoUserLoggedInException, ContentManagerException {
GameboardsFacade gameboardFacade = new GameboardsFacade(dummyPropertiesLoader, dummyLogManager, dummyGameManager, questionManager, userManager, userAssociationManager, userBadgeManager, fastTrackManager);
HttpServletRequest dummyRequest = createMock(HttpServletRequest.class);
String subjects = "physics";
String fields = "mechanics";
String topics = "dynamics";
String levels = "2,3,4";
String concepts = "newtoni";
String title = "Newton";
String questionCategory = "problem_solving";
String stages = "a_level";
String difficulties = "practice_1";
String examBoards = "wjec";
expect(dummyGameManager.generateRandomGameboard(EasyMock.<String>anyObject(), EasyMock.<List<String>>anyObject(), EasyMock.<List<String>>anyObject(), EasyMock.<List<String>>anyObject(), EasyMock.<List<Integer>>anyObject(), EasyMock.<List<String>>anyObject(), EasyMock.<List<String>>anyObject(), EasyMock.<List<String>>anyObject(), EasyMock.<List<String>>anyObject(), EasyMock.<List<String>>anyObject(), EasyMock.<AbstractSegueUserDTO>anyObject())).andReturn(null).atLeastOnce();
expect(userManager.getCurrentUser(dummyRequest)).andReturn(new AnonymousUserDTO("testID")).atLeastOnce();
replay(dummyGameManager);
Response r = gameboardFacade.generateTemporaryGameboard(dummyRequest, title, subjects, fields, topics, stages, difficulties, examBoards, levels, concepts, questionCategory);
assertTrue(r.getStatus() == Status.NO_CONTENT.getStatusCode());
verify(dummyGameManager);
}
use of uk.ac.cam.cl.dtg.isaac.dto.users.AnonymousUserDTO in project isaac-api by isaacphysics.
the class PagesFacade method getQuestion.
/**
* Rest end point that gets a single question page based on a given id.
*
* @param questionId
* to find as a string
* @param request
* - so that we can do etag and cache resolution.
* @param httpServletRequest
* - so that we can try and determine if the user is logged in. This will allow us to augment the
* question objects with any recorded state.
* @return A Response object containing a question page object or a SegueErrorResponse.
*/
@GET
@Path("/questions/{question_page_id}")
@Produces(MediaType.APPLICATION_JSON)
@GZIP
@ApiOperation(value = "Get a question page object by ID.")
public final Response getQuestion(@Context final Request request, @Context final HttpServletRequest httpServletRequest, @PathParam("question_page_id") final String questionId) {
Map<String, List<String>> fieldsToMatch = Maps.newHashMap();
fieldsToMatch.put("type", Arrays.asList(QUESTION_TYPE, FAST_TRACK_QUESTION_TYPE));
if (null == questionId || questionId.isEmpty()) {
return new SegueErrorResponse(Status.BAD_REQUEST, "You must provide a valid question id.").toResponse();
}
// options
fieldsToMatch.put(ID_FIELDNAME + "." + UNPROCESSED_SEARCH_FIELD_SUFFIX, Arrays.asList(questionId));
try {
AbstractSegueUserDTO user = userManager.getCurrentUser(httpServletRequest);
Map<String, Map<String, List<QuestionValidationResponse>>> userQuestionAttempts;
userQuestionAttempts = questionManager.getQuestionAttemptsByUser(user);
// Calculate the ETag
EntityTag etag = new EntityTag(questionId.hashCode() + userQuestionAttempts.toString().hashCode() + "");
Response cachedResponse = generateCachedResponse(request, etag, NEVER_CACHE_WITHOUT_ETAG_CHECK);
if (cachedResponse != null) {
return cachedResponse;
}
Response response = this.findSingleResult(fieldsToMatch, userQuestionAttempts);
if (response.getEntity() != null && response.getEntity() instanceof IsaacQuestionPageDTO) {
SeguePageDTO content = (SeguePageDTO) response.getEntity();
Map<String, String> logEntry = ImmutableMap.of(QUESTION_ID_LOG_FIELDNAME, content.getId(), CONTENT_VERSION_FIELDNAME, this.contentManager.getCurrentContentSHA());
String userIdForRandomisation;
if (user instanceof AnonymousUserDTO) {
userIdForRandomisation = ((AnonymousUserDTO) user).getSessionId();
} else {
userIdForRandomisation = ((RegisteredUserDTO) user).getId().toString();
}
content = this.questionManager.augmentQuestionObjects(content, userIdForRandomisation, userQuestionAttempts);
// the request log
getLogManager().logEvent(user, httpServletRequest, IsaacServerLogType.VIEW_QUESTION, logEntry);
// return augmented content.
return Response.ok(content).cacheControl(getCacheControl(NEVER_CACHE_WITHOUT_ETAG_CHECK, false)).tag(etag).build();
} else {
String error = "Unable to locate a question with the id specified: " + questionId;
log.warn(error);
return SegueErrorResponse.getResourceNotFoundResponse(error);
}
} catch (SegueDatabaseException e) {
String message = "SegueDatabaseException whilst trying to retrieve user data";
log.error(message, e);
return new SegueErrorResponse(Status.INTERNAL_SERVER_ERROR, message).toResponse();
}
}
use of uk.ac.cam.cl.dtg.isaac.dto.users.AnonymousUserDTO in project isaac-api by isaacphysics.
the class LogEventFacade method postLog.
/**
* Method to allow clients to log front-end specific behaviour in the database.
*
* @param httpRequest
* - to enable retrieval of session information.
* @param eventJSON
* - the event information to record as a json map <String, String>.
* @return 200 for success or 400 for failure.
*/
@POST
@Path("/")
@Consumes(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Record a new log event from the current user.", notes = "The 'type' field must be provided and must not be a reserved value.")
public Response postLog(@Context final HttpServletRequest httpRequest, final Map<String, Object> eventJSON) {
if (null == eventJSON || eventJSON.get(TYPE_FIELDNAME) == null) {
log.error("Error during log operation, no event type specified. Event: " + eventJSON);
return new SegueErrorResponse(Status.BAD_REQUEST, "Unable to record log message as the log has no " + TYPE_FIELDNAME + " property.").toResponse();
}
String eventType = (String) eventJSON.get(TYPE_FIELDNAME);
// To maintain data integrity - don't allow the client to report events reserved for the server
if (SEGUE_SERVER_LOG_TYPES.contains(eventType) || ISAAC_SERVER_LOG_TYPES.contains(eventType)) {
return new SegueErrorResponse(Status.FORBIDDEN, "Unable to record log message, restricted '" + TYPE_FIELDNAME + "' value.").toResponse();
}
// After a few weeks we should fail on the case where it is an unknown type.
if (!ISAAC_CLIENT_LOG_TYPES.contains(eventType)) {
log.error(String.format("Warning: Log Event '%s' is not included in ISAAC_CLIENT_LOG_TYPES", eventType));
}
try {
// implement arbitrary log size limit.
AbstractSegueUserDTO currentUser = userManager.getCurrentUser(httpRequest);
String uid;
if (currentUser instanceof AnonymousUserDTO) {
uid = ((AnonymousUserDTO) currentUser).getSessionId();
} else {
uid = ((RegisteredUserDTO) currentUser).getId().toString();
}
try {
misuseMonitor.notifyEvent(uid, LogEventMisuseHandler.class.getSimpleName(), httpRequest.getContentLength());
} catch (SegueResourceMisuseException e) {
log.error(String.format("Logging Event Failed - log event requested (%s bytes) " + "and would exceed daily limit size limit (%s bytes) ", httpRequest.getContentLength(), Constants.MAX_LOG_REQUEST_BODY_SIZE_IN_BYTES));
return SegueErrorResponse.getRateThrottledResponse(String.format("Log event request (%s bytes) would exceed limit for this endpoint.", httpRequest.getContentLength()));
}
// remove the type information as we don't need it.
eventJSON.remove(TYPE_FIELDNAME);
this.getLogManager().logExternalEvent(this.userManager.getCurrentUser(httpRequest), httpRequest, eventType, eventJSON);
return Response.ok().cacheControl(getCacheControl(NEVER_CACHE_WITHOUT_ETAG_CHECK, false)).build();
} catch (SegueDatabaseException e) {
SegueErrorResponse error = new SegueErrorResponse(Status.INTERNAL_SERVER_ERROR, "Database error while looking up user information.", e);
log.error(error.getErrorMessage(), e);
return error.toResponse();
}
}
use of uk.ac.cam.cl.dtg.isaac.dto.users.AnonymousUserDTO in project isaac-api by isaacphysics.
the class UserManagerTest method authenticateCallback_checkNewUserIsAuthenticated_createInternalUserAccount.
/**
* Check that a new (unseen) user is registered when seen with 3rd party authenticator.
*
* @throws Exception
* -
*/
@Test
public final void authenticateCallback_checkNewUserIsAuthenticated_createInternalUserAccount() throws Exception {
IOAuth2Authenticator dummyAuth = createMock(FacebookAuthenticator.class);
UserAccountManager userManager = buildTestUserManager(AuthenticationProvider.TEST, dummyAuth);
UserAuthenticationManager authManager = buildTestAuthenticationManager(AuthenticationProvider.TEST, dummyAuth);
// method param setup for method under test
HttpSession dummySession = createMock(HttpSession.class);
HttpServletRequest request = createMock(HttpServletRequest.class);
HttpServletResponse response = createMock(HttpServletResponse.class);
String someDomain = "http://www.somedomain.com/";
String someClientId = "someClientId";
String someAuthCode = "someAuthCode";
String someState = "someState";
StringBuffer sb = new StringBuffer(someDomain + "?state=" + someState + "&code=" + someAuthCode);
String validQueryStringFromProvider = "client_id=" + someClientId + "&redirect_uri=" + someDomain;
String fullResponseUrlFromProvider = someDomain + "?state=" + someState + "&code=" + someAuthCode + "?client_id=" + someClientId + "&redirect_uri=" + someDomain;
String someProviderGeneratedLookupValue = "MYPROVIDERREF";
String someProviderUniqueUserId = "USER-1";
Long someSegueUserId = 533L;
String someSegueAnonymousUserId = "9284723987anonymous83924923";
AnonymousUser au = new AnonymousUser();
au.setSessionId(someSegueAnonymousUserId);
expect(this.dummyUserCache.storeAnonymousUser(au)).andReturn(au).atLeastOnce();
expect(this.dummyUserCache.getById(au.getSessionId())).andReturn(au).atLeastOnce();
AnonymousUserDTO someAnonymousUserDTO = new AnonymousUserDTO();
someAnonymousUserDTO.setSessionId(someSegueAnonymousUserId);
String validOAuthProvider = "test";
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, 500);
String validDateString = sdf.format(calendar.getTime());
expect(request.getSession()).andReturn(dummySession).atLeastOnce();
// empty as not logged in.
Cookie[] cookieWithoutSessionInfo = {};
expect(request.getCookies()).andReturn(cookieWithoutSessionInfo).times(2);
// session
expect(dummySession.getAttribute(Constants.ANONYMOUS_USER)).andReturn(someSegueAnonymousUserId).atLeastOnce();
// id
// Mock CSRF checks
expect(dummySession.getAttribute(Constants.STATE_PARAM_NAME)).andReturn(CSRF_TEST_VALUE).atLeastOnce();
expect(request.getParameter(Constants.STATE_PARAM_NAME)).andReturn(CSRF_TEST_VALUE).atLeastOnce();
// Mock URL params extract stuff
expect(request.getQueryString()).andReturn(validQueryStringFromProvider).atLeastOnce();
expect(request.getRequestURL()).andReturn(sb);
// Mock extract auth code call
expect(dummyAuth.extractAuthCode(fullResponseUrlFromProvider)).andReturn(someAuthCode);
// Mock exchange code for token call
expect(dummyAuth.exchangeCode(someAuthCode)).andReturn(someProviderGeneratedLookupValue).atLeastOnce();
expect(((IFederatedAuthenticator) dummyAuth).getAuthenticationProvider()).andReturn(AuthenticationProvider.TEST).atLeastOnce();
// User object back from provider
UserFromAuthProvider providerUser = new UserFromAuthProvider(someProviderUniqueUserId, "TestFirstName", "TestLastName", "test@test.com", EmailVerificationStatus.VERIFIED, Role.STUDENT, new Date(), Gender.MALE);
// Mock get User Information from provider call
expect(((IFederatedAuthenticator) dummyAuth).getUserInfo(someProviderGeneratedLookupValue)).andReturn(providerUser).atLeastOnce();
// Expect this to be a new user and to register them (i.e. return null
// from database)
expect(dummyDatabase.getByLinkedAccount(AuthenticationProvider.TEST, someProviderUniqueUserId)).andReturn(null).atLeastOnce();
RegisteredUser mappedUser = new RegisteredUser(null, "TestFirstName", "testLastName", "test@test.com", Role.STUDENT, new Date(), Gender.MALE, new Date(), null, null, null, null);
mappedUser.setSessionToken(0);
expect(dummyDatabase.getAuthenticationProvidersByUsers(Collections.singletonList(mappedUser))).andReturn(new HashMap<RegisteredUser, List<AuthenticationProvider>>() {
{
put(mappedUser, Lists.newArrayList(AuthenticationProvider.GOOGLE));
}
}).atLeastOnce();
expect(dummyDatabase.getSegueAccountExistenceByUsers(Collections.singletonList(mappedUser))).andReturn(ImmutableMap.of(mappedUser, false)).atLeastOnce();
RegisteredUserDTO mappedUserDTO = new RegisteredUserDTO();
expect(dummyMapper.map(providerUser, RegisteredUser.class)).andReturn(mappedUser).atLeastOnce();
expect(dummyMapper.map(mappedUser, RegisteredUserDTO.class)).andReturn(mappedUserDTO).atLeastOnce();
expect(dummyMapper.map(au, AnonymousUserDTO.class)).andReturn(someAnonymousUserDTO).anyTimes();
// handle duplicate account check.
expect(dummyDatabase.getByEmail(providerUser.getEmail())).andReturn(null).once();
// A main part of the test is to check the below call happens
expect(dummyDatabase.registerNewUserWithProvider(mappedUser, AuthenticationProvider.TEST, someProviderUniqueUserId)).andReturn(mappedUser).atLeastOnce();
mappedUser.setId(someSegueUserId);
expect(dummyDatabase.getById(someSegueUserId)).andReturn(mappedUser);
Map<String, String> sessionInformation = getSessionInformationAsAMap(authManager, someSegueUserId.toString(), validDateString, mappedUser.getSessionToken());
Cookie[] cookieWithSessionInfo = getCookieArray(sessionInformation);
// Expect a session to be created
response.addCookie(cookieWithSessionInfo[0]);
expectLastCall().once();
expect(request.getCookies()).andReturn(cookieWithSessionInfo).anyTimes();
dummyQuestionDatabase.mergeAnonymousQuestionAttemptsIntoRegisteredUser(someAnonymousUserDTO, mappedUserDTO);
expectLastCall().once();
expect(dummyQueue.getEmailTemplateDTO("email-template-registration-confirmation-federated")).andReturn(new EmailTemplateDTO()).once();
dummyQueue.sendTemplatedEmailToUser(anyObject(), anyObject(), anyObject(), anyObject());
expectLastCall().once();
replay(dummySession, request, dummyAuth, dummyQuestionDatabase, dummyMapper, dummyDatabase, dummyLocalAuth, dummyQueue, dummyUserCache);
// Act
RegisteredUserDTO u = userManager.authenticateCallback(request, response, validOAuthProvider, false);
// Assert
verify(dummySession, request, dummyAuth, dummyQuestionDatabase);
assertTrue(u instanceof RegisteredUserDTO);
}
use of uk.ac.cam.cl.dtg.isaac.dto.users.AnonymousUserDTO in project isaac-api by isaacphysics.
the class QuestionManager method recordQuestionAttempt.
/**
* Record a question attempt for a given user.
* @param user - user that made the attempt.
* @param questionResponse - the outcome of the attempt to be persisted.
*/
public void recordQuestionAttempt(final AbstractSegueUserDTO user, final QuestionValidationResponseDTO questionResponse) throws SegueDatabaseException {
QuestionValidationResponse questionResponseDO = this.mapper.getAutoMapper().map(questionResponse, QuestionValidationResponse.class);
String questionPageId = extractPageIdFromQuestionId(questionResponse.getQuestionId());
if (user instanceof RegisteredUserDTO) {
RegisteredUserDTO registeredUser = (RegisteredUserDTO) user;
this.questionAttemptPersistenceManager.registerQuestionAttempt(registeredUser.getId(), questionPageId, questionResponse.getQuestionId(), questionResponseDO);
log.debug("Question information recorded for user: " + registeredUser.getId());
} else if (user instanceof AnonymousUserDTO) {
AnonymousUserDTO anonymousUserDTO = (AnonymousUserDTO) user;
this.questionAttemptPersistenceManager.registerAnonymousQuestionAttempt(anonymousUserDTO.getSessionId(), questionPageId, questionResponse.getQuestionId(), questionResponseDO);
} else {
log.error("Unexpected user type. Unable to record question response");
}
}
Aggregations