diff --git a/src/main/java/com/parallax/server/blocklyprop/SessionData.java b/src/main/java/com/parallax/server/blocklyprop/SessionData.java index 978b0894..0ef95882 100644 --- a/src/main/java/com/parallax/server/blocklyprop/SessionData.java +++ b/src/main/java/com/parallax/server/blocklyprop/SessionData.java @@ -1,8 +1,24 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright (c) 2019 Parallax Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the “Software”), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + package com.parallax.server.blocklyprop; import com.google.inject.servlet.SessionScoped; @@ -13,15 +29,21 @@ * User session details. * * This class contains the fields used to manage the client's session with - * the application. + * the application. The @SessionScoped decorator will enforce a single + * instance per session. * * @author Michel */ @SessionScoped public class SessionData implements Serializable { + // A cloud session user profile object private User user; + + // A cloud session user profile primary key ID private Long idUser; + + // A locale string for this user private String locale; /** @@ -54,6 +76,11 @@ public void setLocale(String locale) { this.locale = locale; } + /** + * Override the default toString method to enumerate all fields + * + * @return string representation of SessionData fields + */ @Override public String toString() { return "SessionData{" + "user=" + user + ", idUser=" + idUser + ", locale=" + locale + '}'; diff --git a/src/main/java/com/parallax/server/blocklyprop/TableOrder.java b/src/main/java/com/parallax/server/blocklyprop/TableOrder.java index bce5651d..3b597529 100644 --- a/src/main/java/com/parallax/server/blocklyprop/TableOrder.java +++ b/src/main/java/com/parallax/server/blocklyprop/TableOrder.java @@ -1,17 +1,32 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright (c) 2019 Parallax Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the “Software”), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + package com.parallax.server.blocklyprop; /** + * Enumberate possible sort orders for project listings * * @author Michel */ public enum TableOrder { - asc, desc - } diff --git a/src/main/java/com/parallax/server/blocklyprop/TableSort.java b/src/main/java/com/parallax/server/blocklyprop/TableSort.java index c7991bbe..c4b4520e 100644 --- a/src/main/java/com/parallax/server/blocklyprop/TableSort.java +++ b/src/main/java/com/parallax/server/blocklyprop/TableSort.java @@ -1,8 +1,24 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright (c) 2019 Parallax Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the “Software”), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + package com.parallax.server.blocklyprop; import com.parallax.server.blocklyprop.db.generated.Tables; @@ -10,6 +26,7 @@ import org.jooq.TableField; /** + * A list of the possible fields on which to sort project data * * @author Michel */ @@ -21,14 +38,14 @@ public enum TableSort { user(Tables.PROJECT.ID_USER), modified(Tables.PROJECT.MODIFIED); + // Map this enum to a Field in the JooQ ProjectRecord class private final TableField field; - private TableSort(TableField field) { + TableSort(TableField field) { this.field = field; } public TableField getField() { return field; } - } diff --git a/src/main/java/com/parallax/server/blocklyprop/config/SetupConfig.java b/src/main/java/com/parallax/server/blocklyprop/config/SetupConfig.java index 10bafdf9..49aae174 100644 --- a/src/main/java/com/parallax/server/blocklyprop/config/SetupConfig.java +++ b/src/main/java/com/parallax/server/blocklyprop/config/SetupConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Parallax Inc. + * Copyright (c) 2019 Parallax Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software * and associated documentation files (the “Software”), to deal in the Software without @@ -64,6 +64,7 @@ protected Injector getInjector() { @Override protected void configure() { + bind(Configuration.class).toInstance(configuration); bind(SessionData.class); diff --git a/src/main/java/com/parallax/server/blocklyprop/converter/UserConverter.java b/src/main/java/com/parallax/server/blocklyprop/converter/UserConverter.java index 48a5f892..ad43b9de 100644 --- a/src/main/java/com/parallax/server/blocklyprop/converter/UserConverter.java +++ b/src/main/java/com/parallax/server/blocklyprop/converter/UserConverter.java @@ -10,6 +10,7 @@ import com.parallax.server.blocklyprop.db.generated.tables.records.UserRecord; /** + * Convert user object to a Json string * * @author Michel */ diff --git a/src/main/java/com/parallax/server/blocklyprop/db/dao/UserDao.java b/src/main/java/com/parallax/server/blocklyprop/db/dao/UserDao.java index e199c047..779246b9 100644 --- a/src/main/java/com/parallax/server/blocklyprop/db/dao/UserDao.java +++ b/src/main/java/com/parallax/server/blocklyprop/db/dao/UserDao.java @@ -18,9 +18,24 @@ public interface UserDao { @Deprecated UserRecord create(Long idCloudSession); - + + /** + * Update the blockly user screen name + * + * @param idUser - is the long integer id for the blockly user record + * @param screenName - is ghe new screen name to store in the user record + */ + void updateScreenName(Long idUser, String screenName); + + /** + * + * @param idCloudSession + * @param screenName + * @return + */ UserRecord create(Long idCloudSession, String screenName); + /** * Retrieve a BP user record * @@ -38,16 +53,34 @@ public interface UserDao { */ UserRecord getUser(Long idCloudSession, String screenName); - + /** + * + * @return + */ List getAll(); + /** + * + * @param idUser + * @param roles + */ void setRoles(Long idUser, Set roles); + /** + * + * @param idUser + * @return + */ List getRoles(Long idUser); - Long getUserIdForCloudSessionUserId(Long id); - - @Deprecated - public void updateScreenname(Long idUser, String screenname); - + /** + * Obtain the blocklyprop user ID from the supplied cloud session user id + * + * @param idCloudSession + * The user profile ID + * + * @return + * Returns a Long integer blocklyprop user ID if successful, otherwise returns zero + */ + Long getUserIdForCloudSessionUserId(Long idCloudSession); } diff --git a/src/main/java/com/parallax/server/blocklyprop/db/dao/impl/SessionDaoImpl.java b/src/main/java/com/parallax/server/blocklyprop/db/dao/impl/SessionDaoImpl.java index 42bf8faa..edab7453 100644 --- a/src/main/java/com/parallax/server/blocklyprop/db/dao/impl/SessionDaoImpl.java +++ b/src/main/java/com/parallax/server/blocklyprop/db/dao/impl/SessionDaoImpl.java @@ -1,8 +1,24 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright (c) 2019 Parallax Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the “Software”), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + package com.parallax.server.blocklyprop.db.dao.impl; import com.google.inject.Inject; @@ -62,7 +78,8 @@ public void setConfiguration(Configuration configuration) { */ @Override public void create(SessionRecord session) { - LOG.info("Create a session. Timeout set to: {}", session.getTimeout()); + + LOG.debug("Create a session. Timeout set to: {}", session.getTimeout()); // Log session details if the configuration file permits it printSessionInfo("create", session); @@ -98,7 +115,9 @@ public void create(SessionRecord session) { */ @Override public SessionRecord readSession(String idSession) throws NullPointerException { - LOG.debug("Getting session details"); + + LOG.debug("Getting session {} details", idSession); + SessionRecord sessionRecord = null; try { @@ -112,9 +131,8 @@ public SessionRecord readSession(String idSession) throws NullPointerException { catch (org.jooq.exception.DataAccessException sqex) { LOG.error("Database exception {}", sqex.getMessage()); } - finally { - return sessionRecord; - } + + return sessionRecord; } /** @@ -124,6 +142,7 @@ public SessionRecord readSession(String idSession) throws NullPointerException { */ @Override public void updateSession(SessionRecord session) throws NullPointerException { + LOG.debug("Update a session"); try { @@ -158,8 +177,12 @@ public void updateSession(SessionRecord session) throws NullPointerException { */ @Override public void deleteSession(String idSession) { + LOG.info("Deleting session {}", idSession); - create.deleteFrom(Tables.SESSION).where(Tables.SESSION.IDSESSION.eq(idSession)).execute(); + + create.deleteFrom(Tables.SESSION) + .where(Tables.SESSION.IDSESSION.eq(idSession)) + .execute(); } /** diff --git a/src/main/java/com/parallax/server/blocklyprop/db/dao/impl/UserDaoImpl.java b/src/main/java/com/parallax/server/blocklyprop/db/dao/impl/UserDaoImpl.java index ef0e7814..be056238 100644 --- a/src/main/java/com/parallax/server/blocklyprop/db/dao/impl/UserDaoImpl.java +++ b/src/main/java/com/parallax/server/blocklyprop/db/dao/impl/UserDaoImpl.java @@ -125,18 +125,29 @@ public UserRecord create(Long idCloudSession, String screenName) { return record; } - @Override public List getAll() { - return create.selectFrom(Tables.USER).fetch(); + return create + .selectFrom(Tables.USER) + .fetch(); } + /** + * Get a Blockly UserRecord + * + * @param idUser + * Long integer ID of the user record to retrieve + * + * @return + * Returns a UserRecord object if successful, otherwise returns a null + */ @Override public UserRecord getUser(Long idUser) { return create .selectFrom(Tables.USER) - .where(Tables.USER.ID.equal(idUser)) + .where(Tables.USER.ID + .equal(idUser)) .fetchOne(); } @@ -145,8 +156,10 @@ public UserRecord getUser(Long idCloudSession, String screenName) { // Obtain the BP user id from the CS user id return create .selectFrom(Tables.USER) - .where(Tables.USER.IDCLOUDSESSION.eq(idCloudSession)) - .and(Tables.USER.SCREENNAME.eq(screenName)) + .where(Tables.USER.IDCLOUDSESSION + .eq(idCloudSession)) + .and(Tables.USER.SCREENNAME + .eq(screenName)) .fetchOne(); } @@ -167,30 +180,45 @@ public void setRoles(Long idUser, Set roles) { if (!roles.contains(roleRecord.getName())) { create .delete(Tables.SEC_USER_ROLE) - .where(Tables.SEC_USER_ROLE.ID_USER.equal(idUser)) - .and(Tables.SEC_USER_ROLE.ID_ROLE.equal(roleRecord.getId())) + .where(Tables.SEC_USER_ROLE.ID_USER + .equal(idUser)) + .and(Tables.SEC_USER_ROLE.ID_ROLE + .equal(roleRecord.getId())) .execute(); } } for (Role role : roles) { if (!currentAssignedRoles.getValues(Tables.SEC_ROLE.NAME).contains(role)) { - Long idRole = create.select(Tables.SEC_ROLE.ID).from(Tables.SEC_ROLE).where(Tables.SEC_ROLE.NAME.equal(role)).fetchOne(Tables.SEC_ROLE.ID); + Long idRole = create + .select(Tables.SEC_ROLE.ID) + .from(Tables.SEC_ROLE) + .where(Tables.SEC_ROLE.NAME.equal(role)) + .fetchOne(Tables.SEC_ROLE.ID); + if (idRole == null || idRole == 0) { SecRoleRecord roleRecord = createRole(role); idRole = roleRecord.getId(); } - create.insertInto(Tables.SEC_USER_ROLE, Tables.SEC_USER_ROLE.ID_USER, Tables.SEC_USER_ROLE.ID_ROLE) - .values(idUser, idRole).execute(); + create.insertInto(Tables.SEC_USER_ROLE, Tables.SEC_USER_ROLE.ID_USER, Tables.SEC_USER_ROLE.ID_ROLE) + .values(idUser, idRole) + .execute(); } } } private Result getRawRoles(Long idUser) { - Result currentAssignedRoles = create.select(Tables.SEC_ROLE.ID, Tables.SEC_ROLE.NAME).from(Tables.SEC_ROLE) - .join(Tables.SEC_USER_ROLE).on(Tables.SEC_USER_ROLE.ID_ROLE.equal(Tables.SEC_ROLE.ID)) - .where(Tables.SEC_USER_ROLE.ID_USER.equal(idUser)).fetch().into(Tables.SEC_ROLE); + Result currentAssignedRoles = create + .select(Tables.SEC_ROLE.ID, Tables.SEC_ROLE.NAME) + .from(Tables.SEC_ROLE) + .join(Tables.SEC_USER_ROLE) + .on(Tables.SEC_USER_ROLE.ID_ROLE + .equal(Tables.SEC_ROLE.ID)) + .where(Tables.SEC_USER_ROLE.ID_USER + .equal(idUser)) + .fetch() + .into(Tables.SEC_ROLE); return currentAssignedRoles; } @@ -201,8 +229,11 @@ public List getRoles(Long idUser) { } private SecRoleRecord createRole(Role role) { - SecRoleRecord record = create.insertInto(Tables.SEC_ROLE, Tables.SEC_ROLE.NAME) - .values(role).returning().fetchOne(); + SecRoleRecord record = create + .insertInto(Tables.SEC_ROLE, Tables.SEC_ROLE.NAME) + .values(role) + .returning() + .fetchOne(); return record; } @@ -216,14 +247,14 @@ private SecRoleRecord createRole(Role role) { * @return The BP user id */ @Override - @Deprecated public Long getUserIdForCloudSessionUserId(Long id) { // Obtain the BP user id from the CS user id Long idUser = create .select(Tables.USER.ID) .from(Tables.USER) - .where(Tables.USER.IDCLOUDSESSION.eq(id)) + .where(Tables.USER.IDCLOUDSESSION + .eq(id)) .fetchOneInto(Long.class); if (idUser == null) { @@ -234,23 +265,40 @@ public Long getUserIdForCloudSessionUserId(Long id) { } } + + /** + * Replace the blockly user screen name + * + * @param idUser - is the long integer id for the blockly user record + * @param screenName - is ghe new screen name to store in the user record + */ @Override - @Deprecated - public void updateScreenname(Long idUser, String screenname) { + public void updateScreenName(Long idUser, String screenName) { LOG.info("Attempting to update screen name for user: {} ", idUser); - - UserRecord user = create.selectFrom(Tables.USER) - .where(Tables.USER.ID.eq(idUser)) + + // Fetch the blockly user record + UserRecord user = create + .selectFrom(Tables.USER) + .where(Tables.USER.ID + .eq(idUser)) .fetchOne(); if (user != null) { - if ( ! Objects.equals(user.getScreenname(), screenname)) { - LOG.info("Changing screen name from {} to {}", user.getScreenname(), screenname); + // Compare the existing screen name with the proposed screen name + if ( ! Objects.equals(user.getScreenname(), screenName)) { + LOG.info("Changing screen name from {} to {}", user.getScreenname(), screenName); - user.setScreenname(screenname); - user.update(); + create.update(Tables.USER) + .set(Tables.USER.SCREENNAME, screenName) + .where(Tables.USER.ID.eq(idUser)) + .execute(); + + LOG.info("The screen name is now {}", user.getScreenname()); } } + else { + LOG.warn("Unable to locate a blockly user record for blockly id {}", idUser); + } } } diff --git a/src/main/java/com/parallax/server/blocklyprop/rest/RestProfile.java b/src/main/java/com/parallax/server/blocklyprop/rest/RestProfile.java index aa065c82..c85ce814 100644 --- a/src/main/java/com/parallax/server/blocklyprop/rest/RestProfile.java +++ b/src/main/java/com/parallax/server/blocklyprop/rest/RestProfile.java @@ -1,8 +1,24 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright (c) 2019 Parallax Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the “Software”), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + package com.parallax.server.blocklyprop.rest; import com.cuubez.visualizer.annotation.Detail; @@ -23,6 +39,9 @@ import com.parallax.client.cloudsession.exceptions.WrongAuthenticationSourceException; import com.parallax.client.cloudsession.exceptions.EmailNotConfirmedException; import com.parallax.client.cloudsession.objects.User; + +import com.parallax.server.blocklyprop.services.UserService; + import javax.ws.rs.FormParam; import javax.ws.rs.POST; import javax.ws.rs.Path; @@ -46,6 +65,7 @@ public class RestProfile { private CloudSessionLocalUserService cloudSessionLocalUserService; private CloudSessionUserService cloudSessionUserService; private Configuration configuration; + private UserService userService; @Inject public void setConfiguration(Configuration configuration) { @@ -54,6 +74,15 @@ public void setConfiguration(Configuration configuration) { cloudSessionUserService = new CloudSessionUserService(configuration.getString("cloudsession.baseurl")); } + /** + * Inject a user service object so we can update the screen name + * @param userService + */ + @Inject + public void setUserService(UserService userService) { + this.userService = userService; + } + @POST @Path("/base") @Detail("Save base profile data") @@ -68,14 +97,29 @@ public Response saveBase( LOG.info("REST:/rest/profile/base/ Post request received"); JsonObject result = new JsonObject(); + if (Strings.isNullOrEmpty(screenname)) { result.addProperty("success", false); - result.addProperty("message", "fields-missing"); + result.addProperty("message", "screen-name--missing"); return Response.ok(result.toString()).build(); } else { try { + // Contact the cloud session server to update the user profile User user = cloudSessionUserService.changeUserInfo(id, screenname); if (user != null) { + // The update was successful, update the screen name in the + // blocklyprop user table + // TODO: Update the screen name field in blocklyprop.user table + + Long idUser = userService.getIdUser(user.getId()); + if (idUser > 0) { + userService.setScreenName(idUser, screenname); + LOG.info("Screen name for {} has been changed to {}", username, screenname); + } + else { + LOG.warn("Unable to locate blockly user record for cloudSession id {}", user.getId()); + } + result.addProperty("success", true); result.addProperty("screenname", user.getScreenname()); return Response.ok(result.toString()).build(); diff --git a/src/main/java/com/parallax/server/blocklyprop/rest/RestProject.java b/src/main/java/com/parallax/server/blocklyprop/rest/RestProject.java index 30490097..04d5ff19 100644 --- a/src/main/java/com/parallax/server/blocklyprop/rest/RestProject.java +++ b/src/main/java/com/parallax/server/blocklyprop/rest/RestProject.java @@ -47,6 +47,8 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; + +import org.apache.shiro.SecurityUtils; import org.apache.shiro.authz.AuthorizationException; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; @@ -149,11 +151,16 @@ public Response get( try { // Get the logged in user id for the current session + LOG.info("Requesting blockly user id from BlocklyPropSecurityUtils.getCurrentUserId()"); Long idUser = BlocklyPropSecurityUtils.getCurrentUserId(); +// LOG.info("Getting subject: {} ", SecurityUtils.getSubject()); + + // Return FORBIDDEN if we cannot identify the current user. This could // mean that the user is not logged in or that some underlying issue // is causing the authentication system to fail. + LOG.info("Received blockly user id: {}", idUser); if (idUser == 0) { // Current session is not logged in. return Response.status(Response.Status.FORBIDDEN).build(); diff --git a/src/main/java/com/parallax/server/blocklyprop/rest/RestUser.java b/src/main/java/com/parallax/server/blocklyprop/rest/RestUser.java index 4d7656aa..ad881022 100644 --- a/src/main/java/com/parallax/server/blocklyprop/rest/RestUser.java +++ b/src/main/java/com/parallax/server/blocklyprop/rest/RestUser.java @@ -1,17 +1,35 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright (c) 2019 Parallax Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the “Software”), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + package com.parallax.server.blocklyprop.rest; import com.cuubez.visualizer.annotation.Detail; import com.cuubez.visualizer.annotation.Group; import com.cuubez.visualizer.annotation.HttpCode; import com.cuubez.visualizer.annotation.Name; + import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.inject.Inject; + import com.parallax.server.blocklyprop.converter.UserConverter; import com.parallax.server.blocklyprop.db.generated.tables.pojos.User; import com.parallax.server.blocklyprop.db.generated.tables.records.UserRecord; @@ -27,6 +45,7 @@ /** + * Respond to REST /user endpoint requests * * @author Michel */ @@ -35,9 +54,9 @@ @HttpCode("500>Internal Server Error,200>Success Response") public class RestUser { + // Logger handle private static final Logger LOG = LoggerFactory.getLogger(RestUser.class); - private UserService userService; @Inject @@ -45,20 +64,29 @@ public void setUserService(UserService userService) { this.userService = userService; } + + /** + * List of all user objects + * + * @return + * Returns a list of all user objects. + */ @GET @Path("/") @Detail("Get all users") @Name("Get all users") @Produces("application/json") public Response get() { + //FixMe: Endpoint /rest/user/ returns a list of ALL users. This needs to be regulated. LOG.info("REST:/rest/user/ Get request received"); - + JsonArray result = new JsonArray(); List users = userService.getAllUsers(); - JsonArray result = new JsonArray(); - for (UserRecord user : users) { - result.add(UserConverter.toJson(user)); + if (users != null) { + for (UserRecord user : users) { + result.add(UserConverter.toJson(user)); + } } return Response.ok(result.toString()).build(); diff --git a/src/main/java/com/parallax/server/blocklyprop/security/BlocklyPropSecurityUtils.java b/src/main/java/com/parallax/server/blocklyprop/security/BlocklyPropSecurityUtils.java index a17d1e27..17538a5b 100644 --- a/src/main/java/com/parallax/server/blocklyprop/security/BlocklyPropSecurityUtils.java +++ b/src/main/java/com/parallax/server/blocklyprop/security/BlocklyPropSecurityUtils.java @@ -1,8 +1,24 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright (c) 2019 Parallax Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the “Software”), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + package com.parallax.server.blocklyprop.security; import com.parallax.client.cloudsession.objects.User; @@ -10,17 +26,36 @@ import com.parallax.server.blocklyprop.services.impl.SecurityServiceImpl; import org.apache.shiro.SecurityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * @author Michel */ public class BlocklyPropSecurityUtils extends SecurityUtils { + // Get a logger instance + private static final Logger LOG = LoggerFactory.getLogger(BlocklyPropSecurityUtils.class); + + /** + * + * @return + */ public static Long getCurrentUserId() { - SessionData sessionData = SecurityServiceImpl.getSessionData(); - if (sessionData != null) { - return sessionData.getIdUser(); + + try { + SessionData sessionData = SecurityServiceImpl.getSessionData(); + if (sessionData != null) { + return sessionData.getIdUser(); + } } + catch (Exception ex) { + LOG.info("Exception trapped. Message is: {}.", ex.getMessage()); + } + + LOG.info("Session data not found"); + return null; } diff --git a/src/main/java/com/parallax/server/blocklyprop/security/BlocklyPropSessionDao.java b/src/main/java/com/parallax/server/blocklyprop/security/BlocklyPropSessionDao.java index 87282b2f..84eec6ac 100644 --- a/src/main/java/com/parallax/server/blocklyprop/security/BlocklyPropSessionDao.java +++ b/src/main/java/com/parallax/server/blocklyprop/security/BlocklyPropSessionDao.java @@ -7,18 +7,17 @@ import com.parallax.server.blocklyprop.db.generated.tables.records.SessionRecord; import com.parallax.server.blocklyprop.services.impl.SessionServiceImpl; + import java.io.Serializable; import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.UUID; +import java.util.*; + import org.apache.commons.lang.SerializationUtils; import org.apache.shiro.session.Session; import org.apache.shiro.session.UnknownSessionException; import org.apache.shiro.session.mgt.SimpleSession; import org.apache.shiro.session.mgt.eis.SessionDAO; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,50 +25,101 @@ * Session persistence manager * * @author Michel + * + * @implNote + * Notes from the Shiro JavaDocs: + * Data Access Object design pattern specification to enable Session access to an EIS + * (Enterprise Information System). It provides your four typical CRUD methods: + * create(org.apache.shiro.session.Session), + * readSession(java.io.Serializable), + * update(org.apache.shiro.session.Session), + * and delete(org.apache.shiro.session.Session). + * + * The remaining getActiveSessions() method exists as a support mechanism to pre-emptively + * orphaned sessions, typically by ValidatingSessionManagers), and should be as efficient + * as possible, especially if there are thousands of active sessions. Large scale/high + * performance implementations will often return a subset of the total active sessions and + * perform validation a little more frequently, rather than return a massive set and + * infrequently validate. */ public class BlocklyPropSessionDao implements SessionDAO { - // Get a logger instance + + /** + * Get an instance of the logger initialized to this class + */ private static final Logger LOG = LoggerFactory.getLogger(SessionServiceImpl.class); /** - * + * Inserts a new Session record into the underling EIS (a relational database in this + * implementation). + * * @param session - * @return + * the Session object to create in the EIS. + * + * @return + * the EIS id (e.g. primary key) of the created Session object. + * + * @implNote + * After this method is invoked, the Session.getId() method executed on the argument + * must return a valid session identifier. That is, the following should always be + * true: + * + * Serializable id = create( session ); + * id.equals( session.getId() ) == true + * + * Implementations are free to throw any exceptions that might occur due to integrity + * violation constraints or other EIS related errors. */ @Override public Serializable create(Session session) { - LOG.debug("Create BlocklyProp session"); + LOG.trace("Create BlocklyProp session"); // Set session timeout for 8 hours session.setTimeout(28800000); SimpleSession simpleSession = (SimpleSession) session; + + // Create a unique string and save into the session object String uuid = UUID.randomUUID().toString(); - simpleSession.setId(uuid); + + // Get a reference to the static session service and create + // a session record from the session object and store it in the + // sessionDao backing store SessionServiceImpl.getSessionService().create(convert(simpleSession)); - LOG.debug("Session timeout is: {}", simpleSession.getTimeout()); LOG.info("Creating session: {}", simpleSession.getId()); + // Return a unique session identifier return uuid; } /** - * + * Retrieves the session from the EIS uniquely identified by the specified sessionId. + * * @param sessionId + * the system-wide unique identifier of the Session object to retrieve from the EIS. + * * @return - * @throws UnknownSessionException + * the persisted session in the EIS identified by sessionId. + * + * @throws UnknownSessionException + * if there is no EIS record for any session with the specified sessionId */ @Override public Session readSession(Serializable sessionId) throws UnknownSessionException { - LOG.debug("Reading session: {}", sessionId); + + LOG.trace("Reading session: {}", sessionId); + + // Check parameter for sanity + if (sessionId == null) { + LOG.warn("Attempt to retrieve session with a null UUID parameter"); + throw new UnknownSessionException(); + } try { // Obtain an existing session object SessionRecord sessionRecord - = SessionServiceImpl - .getSessionService() - .readSession(sessionId.toString()); + = SessionServiceImpl.getSessionService().readSession(sessionId.toString()); if (sessionRecord != null) { return convert(sessionRecord); @@ -84,13 +134,26 @@ public Session readSession(Serializable sessionId) throws UnknownSessionExceptio } /** - * + * Updates (persists) data from a previously created Session instance in the EIS identified + * by {@link Session#getId() session.getId()}. This effectively propagates the data in the + * argument to the EIS record previously saved. + * * @param session - * @throws UnknownSessionException + * session - the Session to update + * + * @throws UnknownSessionException + * if no existing EIS session record exists with the identifier of session.getSessionId() + * + * @implNote + * In addition to UnknownSessionException, implementations are free to throw any other + * exceptions that might occur due to integrity violation constraints or other EIS related + * errors. */ @Override public void update(Session session) throws UnknownSessionException { - LOG.debug("Update session: {}", session.getId()); + + LOG.trace("Update session: {}", session.getId()); + try { // updateSession() can throw a NullPointerException if something goes wrong SessionServiceImpl.getSessionService().updateSession(convert(session)); @@ -102,55 +165,116 @@ public void update(Session session) throws UnknownSessionException { } /** - * - * @param session + * Deletes the associated EIS record of the specified session. If there never existed a + * session EIS record with the identifier of session.getId(), then this method does nothing. + * + * @param session + * session - the session to delete. */ @Override public void delete(Session session) { - LOG.debug("Removing session {}", session.getId()); + + LOG.trace("Removing session {}", session.getId()); + SessionServiceImpl.getSessionService().deleteSession(session.getId().toString()); } /** - * - * @return + * Returns all sessions in the EIS that are considered active, meaning all sessions that + * have not been stopped or expired. This is primarily used to validate potential orphans. + * + * @return + * a Collection of Sessions that are considered active, or an empty collection or null if + * there are no active sessions. + * + * @implNote + * This method should be as efficient as possible, especially in larger systems where there + * might be thousands of active sessions. Large scale/high performance implementations will + * often return a subset of the total active sessions and perform validation a little more + * frequently, rather than return a massive set and validate infrequently. If efficient and + * possible, it would make sense to return the oldest unstopped sessions available, ordered + * by lastAccessTime. + * + * Ideally this method would only return active sessions that the EIS was certain should be + * invalided. Typically that is any session that is not stopped and where its + * lastAccessTimestamp is older than the session timeout. For example, if sessions were + * backed by a relational database or SQL-92 'query-able' enterprise cache, you might return + * something similar to the results returned by this query (assuming SimpleSessions were + * being stored): + * + * select * + * from sessions s + * where s.lastAccessTimestamp < ? and s.stopTimestamp is null + * + * where the ? parameter is a date instance equal to 'now' minus the session timeout + * (e.g. now - 30 minutes). */ @Override public Collection getActiveSessions() { - LOG.debug("Getting all active sessions"); + + LOG.trace("Getting all active sessions"); Collection sessionRecords = SessionServiceImpl.getSessionService().getActiveSessions(); - List sessions = new ArrayList(); + List sessions = new ArrayList<>(); + for (SessionRecord sessionRecord : sessionRecords) { sessions.add(convert(sessionRecord)); } + return sessions; } - protected SessionRecord convert(Session session) { - LOG.debug("Converting session {} to a SessionRecord object", session.getId()); - + + /** + * Convert a Session object into a SessionRecord object + * + * @param session + * the session to convert into a SessionRecord + * + * @return + * a SessionRecord object containing the details necessary to persist the object + * into an EIS. + */ + private SessionRecord convert(Session session) { + LOG.trace("Converting session {} to a SessionRecord object", session.getId()); + + // Cast the Session parameter into a SimpleSession reference SimpleSession ssession = (SimpleSession) session; + SessionRecord sessionRecord = new SessionRecord(); sessionRecord.setIdsession(session.getId().toString()); sessionRecord.setStarttimestamp(new Timestamp(session.getStartTimestamp().getTime())); sessionRecord.setLastaccesstime(new Timestamp(session.getLastAccessTime().getTime())); sessionRecord.setTimeout(session.getTimeout()); sessionRecord.setHost(session.getHost()); + + // Gather the session attributes into a HashMap that can be persisted into the + // SessionRecord object if (ssession.getAttributes() != null) { HashMap attributes = (HashMap) ssession.getAttributes(); + + // Logging attributes + // LOG.debug("Session attributes:"); + // attributes.forEach( (k,v) -> LOG.debug("Key: {}, Value: {}", k, v)); + sessionRecord.setAttributes(SerializationUtils.serialize(attributes)); } + return sessionRecord; } /** - * + * Concert a SessionRecord object to a Session object + * * @param sessionRecord - * @return + * the SessionRecord object to convert + * + * @return + * a Session object. The session object attributes may be missing if the original + * SessionRecord object contained non-string data. */ - protected Session convert(SessionRecord sessionRecord) { - LOG.debug("Converting SessionRecord {} into a SimpleSession object", sessionRecord.getIdsession()); + private Session convert(SessionRecord sessionRecord) { + LOG.trace("Converting SessionRecord {} into a SimpleSession object", sessionRecord.getIdsession()); SimpleSession ssession = new SimpleSession(); ssession.setId(sessionRecord.getIdsession()); @@ -158,14 +282,24 @@ protected Session convert(SessionRecord sessionRecord) { ssession.setLastAccessTime(sessionRecord.getLastaccesstime()); ssession.setTimeout(sessionRecord.getTimeout()); ssession.setHost(sessionRecord.getHost()); - + + // Gather the session attributes into a HashMap that can be persisted into the + // Session object if (sessionRecord.getAttributes() != null) { - HashMap attributes - = (HashMap) - SerializationUtils.deserialize(sessionRecord.getAttributes()); - - ssession.setAttributes(attributes); + // In case there is something in the session attributes that isn't a string value + // We can trap the issue here and deal with it. The @SuppressWarnings tells the IDE + // that we have thought about this and taken appropriate defensive measures. + try { + @SuppressWarnings("unchecked") + HashMap attributes + = (HashMap) SerializationUtils.deserialize(sessionRecord.getAttributes()); + ssession.setAttributes(attributes); + } + catch (ClassCastException ex) { + LOG.warn("Unable to convert SessionRecord attributes in session {}", sessionRecord.getIdsession() ); + } } + return ssession; } diff --git a/src/main/java/com/parallax/server/blocklyprop/security/CloudSessionAuthenticationRealm.java b/src/main/java/com/parallax/server/blocklyprop/security/CloudSessionAuthenticationRealm.java index c4d242b4..7a4e4db6 100644 --- a/src/main/java/com/parallax/server/blocklyprop/security/CloudSessionAuthenticationRealm.java +++ b/src/main/java/com/parallax/server/blocklyprop/security/CloudSessionAuthenticationRealm.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Parallax Inc. + * Copyright (c) 2019 Parallax Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software * and associated documentation files (the “Software”), to deal in the Software without @@ -66,11 +66,10 @@ */ public class CloudSessionAuthenticationRealm extends AuthorizingRealm { - /** - * Class logging handle - */ + // Get a handle to a logger for this class private static Logger LOG = LoggerFactory.getLogger(CloudSessionAuthenticationRealm.class); + /** * Convenience implementation that returns * getAuthenticationTokenClass().isAssignableFrom( token.getClass() ); @@ -90,6 +89,7 @@ public boolean supports(AuthenticationToken token) { return true; } + /** * Retrieves the AuthorizationInfo for the given principals from the * underlying data store. @@ -112,6 +112,7 @@ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal return authorizationInfo; } + /** * Retrieves authentication data from an implementation-specific data source * (RDBMS, LDAP, etc) for the given authentication token. @@ -141,12 +142,11 @@ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { - /* - * Any leading and/or trailing white space contained in the credentials + LOG.info("Obtaining authentication info"); + + /* Any leading and/or trailing white space contained in the credentials * (password) has been stripped out before it gets here. */ - LOG.info("Obtaining authentication info"); - try { if (token instanceof OAuthToken) { // Principal = email @@ -157,7 +157,6 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) token.getCredentials(), "CloudSession"); } else { - LOG.info("Authentication is using local login authority"); // Principal = login @@ -166,6 +165,8 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) // Credentials = password String credentials = new String((char[]) token.getCredentials()); + LOG.info("Authenticating user '{}'", principal); + // Thia can throw a NullPointerException User user = SecurityServiceImpl.authenticateLocalUserStatic( principal, @@ -176,6 +177,8 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) return null; } + LOG.info("User {} is authenticated", principal); + try { return new SimpleAccount( token.getPrincipal(), @@ -185,32 +188,34 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) LOG.error("Unexpected exception creating account object", t); } } - return null; - } catch (UnknownUserException ex) { + throw new AuthenticationException("Unable to authenticate token"); + } + catch (UnknownUserException ex) { LOG.warn("Authentication failed. Message: {}", ex.getMessage()); throw new AuthenticationException(ex.getMessage()); - - } catch (UserBlockedException ex) { + } + catch (UserBlockedException ex) { LOG.warn("Blocked user {}", ex); throw new AuthenticationException(ex.getMessage()); - - } catch (EmailNotConfirmedException ex) { + } + catch (EmailNotConfirmedException ex) { LOG.warn("Authentication failed. Message: {}", ex.getMessage()); throw new AuthenticationException("EmailNotConfirmed"); - - } catch (InsufficientBucketTokensException ex) { + } + catch (InsufficientBucketTokensException ex) { LOG.info("Insufficient bucket tokens: {}", ex.getMessage()); throw new AuthenticationException(ex.getMessage()); - - } catch (NullPointerException npe) { + } + catch (NullPointerException npe) { LOG.warn("NullPointer", npe); throw new AuthenticationException(npe.getMessage()); - - } catch (Throwable t) { + } + catch (Throwable t) { // This is a catchall exception handler that kicks the can back // to the caller LOG.warn("Throwable", t); } + return null; } diff --git a/src/main/java/com/parallax/server/blocklyprop/security/CloudSessionCredentialsMatcher.java b/src/main/java/com/parallax/server/blocklyprop/security/CloudSessionCredentialsMatcher.java index eb9e6aaa..13ef2ba6 100644 --- a/src/main/java/com/parallax/server/blocklyprop/security/CloudSessionCredentialsMatcher.java +++ b/src/main/java/com/parallax/server/blocklyprop/security/CloudSessionCredentialsMatcher.java @@ -1,8 +1,24 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright (c) 2019 Parallax Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the “Software”), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + package com.parallax.server.blocklyprop.security; import java.util.Arrays; @@ -10,14 +26,29 @@ import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.credential.SimpleCredentialsMatcher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + /** * * @author Michel */ public class CloudSessionCredentialsMatcher extends SimpleCredentialsMatcher { + // Get a handle to a logger for this class + private static Logger LOG = LoggerFactory.getLogger(CloudSessionCredentialsMatcher.class); + + /** + * + * @param tokenCredentials + * @param accountCredentials + * @return + */ @Override protected boolean equals(Object tokenCredentials, Object accountCredentials) { + LOG.info("Testing for equivalent credentials"); + if (isByteSource(tokenCredentials) && isByteSource(accountCredentials)) { byte[] tokenBytes = toBytes(tokenCredentials); byte[] accountBytes = toBytes(accountCredentials); @@ -27,11 +58,17 @@ protected boolean equals(Object tokenCredentials, Object accountCredentials) { } } + /** + * + * @param token + * @param info + * @return + */ @Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { + LOG.info("Testing auth token against auth information"); Object tokenCredentials = getCredentials(token); Object accountCredentials = getCredentials(info); return equals(tokenCredentials, accountCredentials); } - } diff --git a/src/main/java/com/parallax/server/blocklyprop/services/AuthenticationService.java b/src/main/java/com/parallax/server/blocklyprop/services/AuthenticationService.java index 2d5fbfbc..61b4faab 100644 --- a/src/main/java/com/parallax/server/blocklyprop/services/AuthenticationService.java +++ b/src/main/java/com/parallax/server/blocklyprop/services/AuthenticationService.java @@ -1,18 +1,43 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright (c) 2019 Parallax Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the “Software”), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + package com.parallax.server.blocklyprop.services; import com.parallax.client.cloudsession.objects.User; /** + * Interface for user authentication * * @author Michel */ public interface AuthenticationService { - public User authenticate(String username, String password); + /** + * Process a user authentication request + * + * @param username - user email address + * @param password - password submitted in the request + * + * @return - a user profile object if successful, otherwise return a null + */ + User authenticate(String username, String password); } diff --git a/src/main/java/com/parallax/server/blocklyprop/services/SessionService.java b/src/main/java/com/parallax/server/blocklyprop/services/SessionService.java index b61606a6..dd70a989 100644 --- a/src/main/java/com/parallax/server/blocklyprop/services/SessionService.java +++ b/src/main/java/com/parallax/server/blocklyprop/services/SessionService.java @@ -1,8 +1,24 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright (c) 2019 Parallax Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the “Software”), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + package com.parallax.server.blocklyprop.services; import com.parallax.server.blocklyprop.db.generated.tables.records.SessionRecord; @@ -23,5 +39,4 @@ public interface SessionService { void deleteSession(String idSession); Collection getActiveSessions(); - } diff --git a/src/main/java/com/parallax/server/blocklyprop/services/UserService.java b/src/main/java/com/parallax/server/blocklyprop/services/UserService.java index 8a975e48..78f58d7a 100644 --- a/src/main/java/com/parallax/server/blocklyprop/services/UserService.java +++ b/src/main/java/com/parallax/server/blocklyprop/services/UserService.java @@ -1,8 +1,24 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright (c) 2019 Parallax Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the “Software”), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + package com.parallax.server.blocklyprop.services; import com.parallax.server.blocklyprop.db.generated.tables.pojos.User; @@ -16,12 +32,27 @@ public interface UserService { User getUser(Long idUser); - + + @Deprecated User getUser(Long idCloudSessionUser, String screenName); + /** + * Get the blockly user id from the user profile cloud session id + * + * @param idCloudSession user profile primary key + * + * @return the blockly user id + */ + Long getIdUser(Long idCloudSession); + + + List getAllUsers(); - public String getUserScreenName(Long idUser); + String getUserScreenName(Long idUser); + + void setScreenName(Long idUser, String screenName); + void setLocale(String locale); diff --git a/src/main/java/com/parallax/server/blocklyprop/services/impl/AuthenticationServiceImpl.java b/src/main/java/com/parallax/server/blocklyprop/services/impl/AuthenticationServiceImpl.java index 56bfb8ec..c7d44811 100644 --- a/src/main/java/com/parallax/server/blocklyprop/services/impl/AuthenticationServiceImpl.java +++ b/src/main/java/com/parallax/server/blocklyprop/services/impl/AuthenticationServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Parallax Inc. + * Copyright (c) 2019 Parallax Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software * and associated documentation files (the “Software”), to deal in the Software without @@ -25,18 +25,22 @@ import com.google.inject.Provider; import com.google.inject.Singleton; import com.google.inject.persist.Transactional; + import com.parallax.client.cloudsession.CloudSessionUserService; import com.parallax.client.cloudsession.exceptions.ServerException; import com.parallax.client.cloudsession.exceptions.UnknownUserException; import com.parallax.client.cloudsession.objects.User; import com.parallax.server.blocklyprop.services.AuthenticationService; import com.parallax.server.blocklyprop.services.TokenGeneratorService; + import javax.servlet.http.HttpSession; import org.apache.commons.configuration.Configuration; + import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/parallax/server/blocklyprop/services/impl/SecurityServiceImpl.java b/src/main/java/com/parallax/server/blocklyprop/services/impl/SecurityServiceImpl.java index 1a939325..04a34c5d 100644 --- a/src/main/java/com/parallax/server/blocklyprop/services/impl/SecurityServiceImpl.java +++ b/src/main/java/com/parallax/server/blocklyprop/services/impl/SecurityServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Parallax Inc. + * Copyright (c) 2019 Parallax Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software * and associated documentation files (the “Software”), to deal in the Software without @@ -21,16 +21,10 @@ package com.parallax.server.blocklyprop.services.impl; -import com.google.common.base.Preconditions; -import com.google.common.base.Strings; -import com.google.inject.Inject; -import com.google.inject.Provider; -import com.google.inject.Singleton; -import com.google.inject.persist.Transactional; - import com.parallax.client.cloudsession.CloudSessionAuthenticateService; import com.parallax.client.cloudsession.CloudSessionRegisterService; import com.parallax.client.cloudsession.CloudSessionUserService; +import com.parallax.client.cloudsession.objects.User; import com.parallax.client.cloudsession.exceptions.EmailNotConfirmedException; import com.parallax.client.cloudsession.exceptions.InsufficientBucketTokensException; import com.parallax.client.cloudsession.exceptions.NonUniqueEmailException; @@ -42,19 +36,25 @@ import com.parallax.client.cloudsession.exceptions.UnknownUserIdException; import com.parallax.client.cloudsession.exceptions.UserBlockedException; import com.parallax.client.cloudsession.exceptions.WrongAuthenticationSourceException; -import com.parallax.client.cloudsession.objects.User; - import com.parallax.server.blocklyprop.SessionData; -import com.parallax.server.blocklyprop.db.dao.UserDao; import com.parallax.server.blocklyprop.services.SecurityService; - +import com.parallax.server.blocklyprop.services.SessionService; +import com.parallax.server.blocklyprop.db.dao.UserDao; import com.parallax.server.blocklyprop.db.generated.tables.records.UserRecord; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; +import com.google.inject.persist.Transactional; import java.util.Calendar; -import java.util.Set; + + import org.apache.commons.configuration.Configuration; import org.apache.commons.validator.routines.EmailValidator; import org.apache.shiro.SecurityUtils; +// import org.apache.shiro.session.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -67,56 +67,51 @@ @Transactional public class SecurityServiceImpl implements SecurityService { - /** - * Handle to logging facility - */ + // Handle to logging facility for the SecurityServiceImpl class private static final Logger LOG = LoggerFactory.getLogger(SecurityServiceImpl.class); - - /** - * - */ - private static SecurityServiceImpl instance; - /** - * Web client session details - */ - private Provider sessionData; + // A static instance of this object + private static SecurityServiceImpl instance; - /** - * Application configuration settings - */ + // Application configuration settings private Configuration configuration; - - /** - * - */ - private EmailValidator emailValidator = EmailValidator.getInstance(); - /** - * Interface to the Cloud Session user account registration service - */ + // Interface to the Cloud Session user account registration service private CloudSessionRegisterService registerService; - - /** - * Interface to the Cloud Session user authentication service - */ + + // Interface to the Cloud Session user authentication service private CloudSessionAuthenticateService authenticateService; - - /** - * Interface to the Cloud Session user account/profile services - */ + + // Interface to the Cloud Session user account/profile services private CloudSessionUserService userService; + // Injects an instance of SessionData here + private Provider sessionData; + + // Access to the BlocklyProp user details + private UserDao userDao; + + private SessionService sessionService; + + @Inject + public void setSessionService(SessionService sessionService) { + this.sessionService = sessionService; + } + /** - * Access to the BlocklyProp user details + * Get a static instance of the EmailValidator object. + * + * @implNote + * The default configuration for the validator is to not allow local and TLDs */ - private UserDao userDao; - + private EmailValidator emailValidator = EmailValidator.getInstance(); + + /** * Constructor - * */ public SecurityServiceImpl() { + // // TODO: Correct the 'this' construct in the constructor // // Notes from: https://www.securecoding.cert.org/confluence/display/java/TSM01-J.+Do+not+let+the+this+reference+escape+during+object+construction @@ -127,38 +122,46 @@ public SecurityServiceImpl() { instance = this; } + /** - * Set the session's data provider + * Implements the Providers setSessionDataProvider interface * * This is a callback used by the Shiro package to provide a connection * between the application and the Shiro session management services. * - * @param sessionDataProvider + * @param sessionDataProvider + * is a class that models the session data + * */ @Inject public void setSessionDataProvider(Provider sessionDataProvider) { this.sessionData = sessionDataProvider; } + /** * Set the session's user database object in the blocklyprop system. * - * @param userDao + * @param userDao + * is the DAO interface to User data instance store */ @Inject public void setUserDao(UserDao userDao) { this.userDao = userDao; } + /** * Configure cloud session service endpoints * - * @param configuration + * @param configuration + * A application configuration object */ - @Inject public void setConfiguration(Configuration configuration) { + LOG.debug("Setting cloud session configuration"); + this.configuration = configuration; // Set the source for the cloud session registration services @@ -177,6 +180,7 @@ public void setConfiguration(Configuration configuration) { configuration.getString("cloudsession.baseurl")); } + /** * Validate new user data and create a new user account * @@ -194,12 +198,23 @@ public void setConfiguration(Configuration configuration) { * @param birthYear int Year component of the user's birthday. COPPA field * @param parentEmail String sponsor email address. COPPA field * @param parentEmailSource int Sponsor classification. COPPA - * @return + * + * @return The cloud session user ID if successful or zero upon failure + * * @throws NonUniqueEmailException + * A user account with the provided email address already exists + * * @throws PasswordVerifyException + * The two password values provided do not match + * * @throws PasswordComplexityException + * The provided password does not meet the requirements for a secure password + * * @throws ScreennameUsedException - * @throws IllegalStateException + * A user account with the provided screen name already exists + * + * @throws IllegalStateException + * */ @Override public Long register( @@ -217,71 +232,60 @@ public Long register( ScreennameUsedException, IllegalStateException{ + LOG.info("Registering a new user: {}({})", email, screenname); + + // Instantiate a new cloud session user profile object User user = new User(); - // Log a few things - LOG.debug("In register: parameter screen name: {}", screenname); - LOG.debug("In register: parameter email: {}", email); - LOG.debug("In register: parameter month: {}", birthMonth); - LOG.debug("In register: parameter year: {}", birthYear); - LOG.debug("In register: parameter sponsor email: {}", parentEmail); - LOG.debug("In register: parameter sponsor type selection: {}", parentEmailSource); - // Perform basic sanity checks on inputs // Throws NullPointerException if screenname is null - LOG.debug("Resgistering new user: {}", screenname); Preconditions.checkNotNull(screenname, "ScreenNameNull"); // User email address is required and must be reasonably valid - LOG.debug("Verifying email address has been supplied"); Preconditions.checkNotNull(email, "UserEmailNull"); - - LOG.debug("Verifying email address is reasonable"); Preconditions.checkState( emailValidator.isValid(email), "Email address format is incorrect"); - LOG.debug("Verifying that a password was provided"); + // The password fields must contain something Preconditions.checkNotNull(password, "PasswordIsNull"); - - LOG.debug("Verify that second copy of password was provided"); Preconditions.checkNotNull(passwordConfirm, "PasswordConfirmIsNull"); - + // Verify that we have valid COPPA data before continuing // Birth month Preconditions.checkNotNull(birthMonth, "BirthMonthNull"); - LOG.debug("Verify that month is provided: {}", birthMonth); Preconditions.checkState((birthMonth != 0), "BirthMonthNotSet"); // Birth year Preconditions.checkNotNull(birthYear, "BirthYearNull"); - LOG.debug("Verify that year is provided: {}", birthYear); Preconditions.checkState( (Calendar.getInstance().get(Calendar.YEAR) != birthYear), "BirthYearNotSet"); // Get additional information if the registrant is under 13 years old if (user.isCoppaEligible(birthMonth, birthYear)) { - LOG.debug("User is subject to COPPA regulations"); - // We must have a sponsor email address for COPPA eligible users Preconditions.checkNotNull( parentEmail, "SponsorEmailNull"); - + // Verify that the sponsor email address is reasonable if (parentEmail != null && parentEmail.length() > 0) { - LOG.debug("Verify that optional user email address is reasonable"); Preconditions.checkState( - emailValidator.isValid(parentEmail), - "SponsorEmail"); + emailValidator.isValid(parentEmail), + "SponsorEmail"); } + + LOG.info("User is COPPA restricted"); + LOG.info("Sponsor email address is: {}", parentEmail); } + /* ------------------------------------------------------------------ + * Attempt to register the user account data with the cloud session + * service. If successful, the method call will return a cloud + * session user id for the newly created account + * -----------------------------------------------------------------*/ try { - // Attempt to register the user account data with the cloud session - // service. If successful, the method call will return a cloud - // session user id for the newly created account LOG.info("Registering user account with cloud-service"); Long idCloudSessionUser = registerService.registerUser( email, @@ -296,6 +300,7 @@ public Long register( // Create a BlocklyProp user account record if (idCloudSessionUser > 0) { + LOG.info("Creating matching blocklyprop user record for {}", screenname); userDao.create(idCloudSessionUser, screenname); } @@ -341,7 +346,7 @@ public static User authenticateLocalUserStatic( } /** - * Authenticate a user from the provided userID + * Get an instance of an authenticated user object * * @param idUser * @@ -389,7 +394,6 @@ public User authenticateLocalUser(String email, String password) throws // Query Cloud Session interface User user = authenticateService.authenticateLocalUser(email, password); - LOG.info("User authenticated"); return user; } catch (UnknownUserException uue) { @@ -412,7 +416,7 @@ public User authenticateLocalUser(String email, String password) throws throw wase; } catch (NullPointerException npe) { - LOG.error("Authetication threw Null Pointer Exception"); + LOG.error("Authentication threw Null Pointer Exception"); throw npe; } catch (ServerException se) { @@ -442,7 +446,9 @@ public User authenticateLocalUser(Long idUser) throws UnknownUserIdException, UserBlockedException, EmailNotConfirmedException { - + + // FixMe: UserBlockledException is never thrown in client.cloudsession. + try { User user = userService.getUser(idUser); LOG.info("User authenticated"); @@ -461,20 +467,26 @@ public User authenticateLocalUser(Long idUser) throws * Return user session data * * @return SessionData object containing user session details or null + * + * @implNote + * The SessionData object stores three attributes: + * user - A cloud session user profile object + * idUser - the blocklprop user primary key ID + * locale - the locale string used for this session */ public static SessionData getSessionData() { LOG.debug("Getting user session data"); - + SessionData sessionData = instance.sessionData.get(); - + if (sessionData == null) { LOG.warn("Error obtaining session data"); + return null; } - - LOG.debug("Session data - {}", sessionData.toString()); - + // Check for a BP user id if (sessionData.getIdUser() == null) { + LOG.debug("No user ID is associated with the current session"); // No BP user id found, is the user in this session authenticated? if (SecurityUtils.getSubject().isAuthenticated()) { @@ -482,59 +494,87 @@ public static SessionData getSessionData() { // The user identified by this session is authenticated. Perform // a fun exercise to locate the BP user id for this authenticated // user. - LOG.debug("Session data missing a valid BP id for an authenticated user"); + LOG.debug("Obtaining session data for authenticated user"); try { // Getting a user record using the account email address String principal = (String) SecurityUtils.getSubject().getPrincipal(); // Display the user's email address - LOG.debug("Getting pricipal: {}", principal ); + LOG.debug("Principal is: {}", principal ); // Get the user account/profile record + String emailAddress = (String) SecurityUtils.getSubject().getPrincipal(); + LOG.debug("Getting user profile for {}", emailAddress); + + // Retrieve a blocky user record using an email address User user = instance.userService.getUser( (String) SecurityUtils.getSubject().getPrincipal()); - + // Did we get a user account object if (user != null) { - LOG.debug("Session User: {}", user.getScreenname()); - LOG.debug("Session UserId: {}", user.getId()); - LOG.debug("Session locale: {}", user.getLocale()); - + LOG.debug("User Profile: {}({}), ID: {}", + user.getEmail(), + user.getScreenname(), + user.getId()); + LOG.debug("Session Locale is: {}",sessionData.getLocale()); + // Yes, User account local may have changed if (!Strings.isNullOrEmpty(sessionData.getLocale())) { if (!sessionData.getLocale().equals(user.getLocale())) { try { // User locale changed. Let's update the user // account with new locale - LOG.info("Changing user {} locale", user.getScreenname()); + LOG.debug("Changing user {} locale", user.getScreenname()); user = instance.userService.changeUserLocale( user.getId(), sessionData.getLocale()); + } catch (UnknownUserIdException ex) { LOG.error("UnknownUserId exception detected. {}", ex.getMessage()); } } } - - LOG.debug("Setting session user data for {}", user.getScreenname()); - sessionData.setUser(user); - - LOG.debug("Getting BP user id"); - UserRecord bpUser = instance.userDao.getUser(user.getId(), user.getScreenname()); + + // Store the user profile into the session + sessionData.setUser(user); + + LOG.debug("Checking {}", sessionData.getUser().getScreenname()); + + // Getting the blocklyprop user record + Long idBlocklyUser = instance.userDao.getUserIdForCloudSessionUserId(user.getId()); + + LOG.debug("Obtained BlocklyProp user id {} from cloud session id {} ", + idBlocklyUser, + user.getId() ); + + UserRecord bpUser = instance.userDao.getUser(idBlocklyUser); + if (bpUser != null) { - LOG.debug("Setting BP user id to: {}", bpUser.getId()); - sessionData.setIdUser(bpUser.getId()); + LOG.debug("Retrieved blockly user record: bpID: {}, csID: {}, Name: {}", + bpUser.getId(), + bpUser.getIdcloudsession(), + bpUser.getScreenname() ); + + + // Verify that the screen name matches in both databases + if (! bpUser.getScreenname().equals(user.getScreenname())) { + LOG.info("Updating bp screen name from {} to {}", + bpUser.getScreenname(), + user.getScreenname()); + + instance.userDao.updateScreenName( + bpUser.getId(), + user.getScreenname()); + } }else{ LOG.warn("Warning! Setting BP user id to zero"); sessionData.setIdUser(0L); } - /* - * This should never be necessary until the user profile page - * offers the capability to change the user's screen name - */ -// instance.userDao.updateScreenname( -// sessionData.getIdUser(), -// user.getScreenname()); + sessionData.setIdUser(idBlocklyUser); + sessionData.setLocale(user.getLocale()); + + + //TODO: Persist the updated sessionData } } catch (UnknownUserException ex) { LOG.error("Unknown user ID. {}", ex); @@ -543,6 +583,8 @@ public static SessionData getSessionData() { } } } + + LOG.debug("Returning session data"); return sessionData; } } diff --git a/src/main/java/com/parallax/server/blocklyprop/services/impl/SessionServiceImpl.java b/src/main/java/com/parallax/server/blocklyprop/services/impl/SessionServiceImpl.java index caaab48f..45ead890 100644 --- a/src/main/java/com/parallax/server/blocklyprop/services/impl/SessionServiceImpl.java +++ b/src/main/java/com/parallax/server/blocklyprop/services/impl/SessionServiceImpl.java @@ -1,8 +1,24 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright (c) 2019 Parallax Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the “Software”), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + package com.parallax.server.blocklyprop.services.impl; import com.google.inject.Inject; @@ -11,7 +27,6 @@ import com.parallax.server.blocklyprop.db.dao.SessionDao; import com.parallax.server.blocklyprop.db.generated.tables.records.SessionRecord; import com.parallax.server.blocklyprop.services.SessionService; -//import java.util.Arrays; import java.util.Collection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,7 +45,7 @@ public class SessionServiceImpl implements SessionService { // Retain session state private static SessionService sessionService; - // Session database acceess object + // Session database access object private SessionDao sessionDao; public SessionServiceImpl() { @@ -44,8 +59,8 @@ public void setSessionDao(SessionDao sessionDao) { @Override public void create(SessionRecord session) { - log.info("Creating a new user session with timeout set to {}", session.getTimeout()); - + log.debug("Creating a new user session"); + //TODO: Verify session attributes element has data when saving. sessionDao.create(session); } @@ -77,10 +92,10 @@ public Collection getActiveSessions() { return sessionDao.getActiveSessions(); } + public static SessionService getSessionService() { log.debug("Get current session service instance"); return sessionService; } - } diff --git a/src/main/java/com/parallax/server/blocklyprop/services/impl/UserServiceImpl.java b/src/main/java/com/parallax/server/blocklyprop/services/impl/UserServiceImpl.java index bcb0dffa..c3198dd1 100644 --- a/src/main/java/com/parallax/server/blocklyprop/services/impl/UserServiceImpl.java +++ b/src/main/java/com/parallax/server/blocklyprop/services/impl/UserServiceImpl.java @@ -1,21 +1,40 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright (c) 2019 Parallax Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the “Software”), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + package com.parallax.server.blocklyprop.services.impl; import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.inject.persist.Transactional; + import com.parallax.client.cloudsession.CloudSessionUserService; import com.parallax.client.cloudsession.exceptions.ServerException; import com.parallax.client.cloudsession.exceptions.UnknownUserIdException; + import com.parallax.server.blocklyprop.db.dao.UserDao; import com.parallax.server.blocklyprop.db.generated.tables.pojos.User; import com.parallax.server.blocklyprop.db.generated.tables.records.UserRecord; import com.parallax.server.blocklyprop.security.BlocklyPropSecurityUtils; import com.parallax.server.blocklyprop.services.UserService; + import java.util.List; import org.apache.commons.configuration.Configuration; import org.slf4j.Logger; @@ -33,26 +52,47 @@ public class UserServiceImpl implements UserService { private static UserService USER_SERVICE; - private Configuration configuration; - private UserDao userDao; + /** + * User profile + */ private CloudSessionUserService userService; public UserServiceImpl() { UserServiceImpl.USER_SERVICE = this; } + /** + * BlocklyProp user record + */ + private UserDao userDao; + @Inject public void setUserDao(UserDao userDao) { this.userDao = userDao; } + /** + * Application configuration object + */ + private Configuration configuration; + @Inject public void setConfiguration(Configuration configuration) { this.configuration = configuration; userService = new CloudSessionUserService(configuration.getString("cloudsession.baseurl")); } + + /** + * Locate a blocklyprop user record from the bp user ID + * + * @param idUser + * The blocklyprop user ID + * + * @return + * A populated blocklyprop user instance + */ @Override public User getUser(Long idUser) { if (userDao != null) { @@ -62,9 +102,34 @@ public User getUser(Long idUser) { LOG.error("UserDAO is not initialized before first use!"); return null; } - } - + + + /** + * Look up the blockly user id from the user profile cloud session id + * + * @param idCloudSession + * The user profile key id + * + * @return + * Returns a Long integer representing the blockly record primary key id + */ + public Long getIdUser(Long idCloudSession) { + return userDao.getUserIdForCloudSessionUserId(idCloudSession); + } + + + /** + * Locate a blocklyprop user record from the user profile ID and screen name + * + * @param idCloudSessionUser + * + * @param screenName + * + * @return + * + */ + @Deprecated @Override public User getUser(Long idCloudSessionUser, String screenName) { if (userDao != null) { @@ -76,6 +141,13 @@ public User getUser(Long idCloudSessionUser, String screenName) { } } + + /** + * Get a list of blocklyprop user objects + * + * @return + * Returns a list of blocklyprop user objects + */ @Override public List getAllUsers() { if (userDao != null) { @@ -87,6 +159,17 @@ public List getAllUsers() { } } + + /** + * Get the blocklyprop user screen name + * + * @param idUser + * Provide the blocklyprop user ID + * + * @return + * Returns a string containing the user's screen name or an empty string + * if the blocklyprop user record was not found + */ @Override public String getUserScreenName(Long idUser) { @@ -101,21 +184,31 @@ public String getUserScreenName(Long idUser) { } } catch (NullPointerException ex) { - LOG.error("Error retreiving name for userID: {}", idUser); + LOG.error("Error retrieving name for userID: {}", idUser); } return name; } @Override public void setLocale(String locale) { + LOG.info("Setting locale {}", locale); + if (SecurityServiceImpl.getSessionData() != null) { + LOG.info("Retrieved SessionData object"); + LOG.info("SessionData {}",SecurityServiceImpl.getSessionData().toString() ); + if (SecurityServiceImpl.getSessionData().getUser() != null) { + LOG.info("Retrieved SessionData User object"); try { + LOG.info("Loading user profile to update locale"); com.parallax.client.cloudsession.objects.User user = BlocklyPropSecurityUtils.getUserInfo(); - if (!user.getLocale().equals(locale)) { - LOG.info("Setting user locale: {} - {}", user.getId(), locale); - user = userService.changeUserLocale(user.getId(), locale); - BlocklyPropSecurityUtils.setUserInfo(user); + + if (user != null) { + if (!user.getLocale().equals(locale)) { + LOG.info("Setting user locale: {} - {}", user.getId(), locale); + user = userService.changeUserLocale(user.getId(), locale); + BlocklyPropSecurityUtils.setUserInfo(user); + } } } catch (UnknownUserIdException uuie) { LOG.error("Unknown user id", uuie); @@ -128,6 +221,23 @@ public void setLocale(String locale) { } } + // + + /** + * Update the user screen name stored in the blockyprop.users table + * + * @param idUser is the blocklyprop user primary key + * + * @param screenName is the new screen name text to store + */ + public void setScreenName(Long idUser, String screenName) { + userDao.updateScreenName(idUser, screenName); + + // TODO: Set session screen name attribute + + } + + public static UserService getUserService() { return UserServiceImpl.USER_SERVICE; } diff --git a/src/main/java/com/parallax/server/blocklyprop/servlets/AuthenticationServlet.java b/src/main/java/com/parallax/server/blocklyprop/servlets/AuthenticationServlet.java index 91670fe6..ab1f9f18 100644 --- a/src/main/java/com/parallax/server/blocklyprop/servlets/AuthenticationServlet.java +++ b/src/main/java/com/parallax/server/blocklyprop/servlets/AuthenticationServlet.java @@ -27,11 +27,9 @@ import com.parallax.client.cloudsession.objects.User; import com.parallax.server.blocklyprop.services.AuthenticationService; import java.io.IOException; -import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.configuration.Configuration; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.web.util.SavedRequest; import org.apache.shiro.web.util.WebUtils; @@ -50,39 +48,16 @@ @Singleton public class AuthenticationServlet extends HttpServlet { - /** - * Handle for any logging activity - */ + // Handle for any logging activity private final Logger LOG = LoggerFactory.getLogger(AuthenticationServlet.class); - - /** - * Application configuration settings - */ - private Configuration configuration; - - - /** - * An instance of this class - */ + //An instance of this class private AuthenticationService authenticationService; - - /** - * Initialize the application configuration - * - * @param configuration - */ - @Inject - public void setConfiguration(Configuration configuration) { - this.configuration = configuration; - } - - /** * Initialize an instance of the Authentication service * - * @param authenticationService + * @param authenticationService - inject an authentication service object */ @Inject public void setAuthenticationService(AuthenticationService authenticationService) { @@ -91,17 +66,25 @@ public void setAuthenticationService(AuthenticationService authenticationService } - + /** + * Process the authentication post request + * + * @param request - Http request object + * @param resp - Http response returned to the caller + * + * @throws IOException - an I/O error was detected + */ @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doPost(HttpServletRequest request, HttpServletResponse resp) + throws IOException { LOG.info("REST:/authenticate/ Post request received"); + // Set the content type of the Http response resp.setContentType("application/json"); - String username = req.getParameter("username"); - String password = req.getParameter("password"); + String username = request.getParameter("username"); + String password = request.getParameter("password"); LOG.info("Authenticating user '{}'", username); @@ -120,14 +103,21 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) } if (user != null) { - SavedRequest savedRequest = WebUtils.getAndClearSavedRequest(req); + // Authentication succeeded + + /* A SavedRequest object maintains request data for a request that was + * redirected, so that after authentication the user can be redirected + * to the originally requested page. + */ + SavedRequest savedRequest = WebUtils.getAndClearSavedRequest(request); + if (savedRequest != null) { LOG.info("Redirecting to third-part authenticator"); resp.sendRedirect(savedRequest.getRequestUrl()); } else { - JsonObject response = new JsonObject(); response.addProperty("success", true); + JsonObject userJson = new JsonObject(); userJson.addProperty("id-user", user.getId()); userJson.addProperty("screenname", user.getScreenname()); @@ -153,5 +143,4 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) resp.getWriter().write(response.toString()); } } - } diff --git a/src/main/java/com/parallax/server/blocklyprop/servlets/PrivacyPolicyServlet.java b/src/main/java/com/parallax/server/blocklyprop/servlets/PrivacyPolicyServlet.java index 7ca31a87..5a1e7deb 100644 --- a/src/main/java/com/parallax/server/blocklyprop/servlets/PrivacyPolicyServlet.java +++ b/src/main/java/com/parallax/server/blocklyprop/servlets/PrivacyPolicyServlet.java @@ -1,40 +1,53 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright (c) 2019 Parallax Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the “Software”), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + package com.parallax.server.blocklyprop.servlets; import com.google.inject.Singleton; import java.io.IOException; -import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + /** + * Return the privacy policy page * * @author developer */ @Singleton public class PrivacyPolicyServlet extends HttpServlet { - - /** - * Application logging system access - */ - private static Logger LOG = LoggerFactory.getLogger(PrivacyPolicyServlet.class); - - // + // Application logging system access + private static final Logger LOG = LoggerFactory.getLogger(PrivacyPolicyServlet.class); + /** * Handles the HTTP GET method. * * @param request servlet request * @param response servlet response - * * @throws ServletException if a servlet-specific error occurs * @throws IOException if an I/O error occurs */ @@ -43,10 +56,10 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { LOG.debug("Requesting child privacy page"); - + request.getRequestDispatcher( "WEB-INF/servlet/coppa/privacy-policy.jsp") - .forward(request, response); + .forward(request, response); } /** @@ -57,6 +70,5 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) @Override public String getServletInfo() { return "Parallax COPPA policy page"; - }// - + } } diff --git a/src/main/java/com/parallax/server/blocklyprop/servlets/ProfileServlet.java b/src/main/java/com/parallax/server/blocklyprop/servlets/ProfileServlet.java index ae769816..bd5119b1 100644 --- a/src/main/java/com/parallax/server/blocklyprop/servlets/ProfileServlet.java +++ b/src/main/java/com/parallax/server/blocklyprop/servlets/ProfileServlet.java @@ -1,8 +1,24 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright (c) 2019 Parallax Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the “Software”), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + package com.parallax.server.blocklyprop.servlets; import com.google.common.base.Strings; @@ -55,8 +71,12 @@ public class ProfileServlet extends HttpServlet { @Inject public void setConfiguration(Configuration configuration) { this.configuration = configuration; - cloudSessionLocalUserService = new CloudSessionLocalUserService(configuration.getString("cloudsession.server"), configuration.getString("cloudsession.baseurl")); - cloudSessionUserService = new CloudSessionUserService(configuration.getString("cloudsession.baseurl")); + cloudSessionLocalUserService = new CloudSessionLocalUserService( + configuration.getString("cloudsession.server"), + configuration.getString("cloudsession.baseurl")); + + cloudSessionUserService = new CloudSessionUserService( + configuration.getString("cloudsession.baseurl")); } @Inject @@ -75,6 +95,10 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se LOG.info("REST:/profile/ Get request received"); User user = BlocklyPropSecurityUtils.getUserInfo(); + if (user == null) { + req.setAttribute("base-error", "Unknown user"); + req.getRequestDispatcher("WEB-INF/servlet/profile/profile.jsp").forward(req, resp); + } req.setAttribute("id", user.getId()); req.setAttribute("email", user.getEmail()); req.setAttribute("screenname", user.getScreenname()); diff --git a/src/main/java/com/parallax/server/blocklyprop/servlets/TextileIndexServlet.java b/src/main/java/com/parallax/server/blocklyprop/servlets/TextileIndexServlet.java index 9ab3314c..3a76743e 100644 --- a/src/main/java/com/parallax/server/blocklyprop/servlets/TextileIndexServlet.java +++ b/src/main/java/com/parallax/server/blocklyprop/servlets/TextileIndexServlet.java @@ -1,52 +1,69 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright (c) 2019 Parallax Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the “Software”), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + package com.parallax.server.blocklyprop.servlets; import com.google.inject.Singleton; -import com.parallax.server.blocklyprop.utils.ServletUtils; -import com.parallax.server.blocklyprop.utils.TextileReader; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.shiro.session.UnknownSessionException; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** + * Display the BlocklyProp home page * * @author Michel + * + * TODO: Refactor this into a file in the application servlets directory */ @Singleton public class TextileIndexServlet extends HttpServlet { - /** - * Application logging facility - */ + // Application logging facility private static final Logger LOG = LoggerFactory.getLogger(TextileIndexServlet.class); - private final TextileReader textileFileReader = new TextileReader(); - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - + + LOG.info("Processing a Http request for '/'"); + try { - String html = textileFileReader.readFile("index", ServletUtils.getLocale(req), req.isSecure()); - req.setAttribute("html", html); - req.getRequestDispatcher("/WEB-INF/servlet/index.jsp").forward(req, resp); + // Direct the request to the home page + request.getRequestDispatcher( + "WEB-INF/servlet/index.jsp") + .forward(request, response); } - catch (UnknownSessionException ex) { - // Redrect the session back to the home page. - LOG.error("Session has expired. Request was for: {}", req.getRequestURI()); - resp.flushBuffer(); - req.getRequestDispatcher("/WEB-INF/servlet/index.jsp").forward(req, resp); + catch (ServletException se) { + LOG.warn("Servlet exception encountered while serving /index.jsp"); + LOG.warn("Error message is: {}", se.getMessage()); + } + catch (IOException eio) { + LOG.warn("I/O exception encountered while serving /index/jsp"); + LOG.warn("Error message is: {}", eio.getMessage()); } } - } diff --git a/src/main/resources/com/parallax/server/blocklyprop/internationalization/translations.properties b/src/main/resources/com/parallax/server/blocklyprop/internationalization/translations.properties index 66a1ce1d..1cd5310a 100644 --- a/src/main/resources/com/parallax/server/blocklyprop/internationalization/translations.properties +++ b/src/main/resources/com/parallax/server/blocklyprop/internationalization/translations.properties @@ -28,7 +28,7 @@ footer.clientdownloadlink = BlocklyProp-client # Application version numbers. application.major = 1 application.minor = 1 -application.build = 455 +application.build = 456 html.content_missing = Content missing diff --git a/src/main/resources/documents/index.textile b/src/main/resources/documents/index.textile deleted file mode 100644 index d3f86c60..00000000 --- a/src/main/resources/documents/index.textile +++ /dev/null @@ -1,14 +0,0 @@ - -
- - - -*Blockly for Propeller Multicore:* Making amazing projects and learning to code just became easier - -!(cdn full-width)/images/home-banner.png! - -
diff --git a/src/main/resources/shiro.ini b/src/main/resources/shiro.ini index 22d40217..23a16291 100644 --- a/src/main/resources/shiro.ini +++ b/src/main/resources/shiro.ini @@ -1,5 +1,5 @@ # -# Copyright (c) 2018 Parallax Inc. +# Copyright (c) 2019 Parallax Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy of this software # and associated documentation files (the “Software”), to deal in the Software without @@ -21,64 +21,98 @@ [main] -# Supports direct (plain) comparison for credentials of type byte[], char[], -# and Strings, and if the arguments do not match these types, then reverts back -# to simple Object.equals comparison. -credentialsMatcher = org.apache.shiro.authc.credential.SimpleCredentialsMatcher +# --------------- +# Session Manager +# --------------- +# Web-application capable SessionManager implementation. This provides a +# handler for HttpRequest and HttpResponse integrated with the Shiro system. +# The security manager will use the DefaultWebSessionManager interface. +# -------------------------------------------------------------------------- +sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager +securityManager.sessionManager = $sessionManager +# ------------------------- +# Enable session ID cookies +# ------------------------- +securityManager.sessionManager.sessionIdCookieEnabled = true -# A credentials matcher that always returns true when matching credentials no matter -# what arguments are passed in. This can be used for testing or when credentials are -# implicitly trusted for a particular Realm. -allow_all_credentialsMatcher = org.apache.shiro.authc.credential.AllowAllCredentialsMatcher +# -------------------------------------------------------------------------- +# Disable session management when operating within a multi-host environment +# -------------------------------------------------------------------------- +securityManager.sessionManager.deleteInvalidSessions = false +securityManager.sessionManager.sessionValidationSchedulerEnabled = false +securityManager.sessionManager.globalSessionTimeout = 28800000 -# cloudSessionMatcher = com.parallax.server.blocklyprop.security.CloudSessionCredentialsMatcher +# Attach the default session validation scheduler +sessionValidationScheduler = org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler +securityManager.sessionManager.sessionValidationScheduler = $sessionValidationScheduler +# Run once per day +securityManager.sessionManager.sessionValidationScheduler.interval = 86400000 -# Define interface to the backend storage to hold user credentials -cloudsessionRealm = com.parallax.server.blocklyprop.security.CloudSessionAuthenticationRealm +# ---------------- +# Backing Storage +# ---------------- +# +# Configure the SessionDao to use the BlocklyPropSessionDao class to implement +# the SessionDao interface. Configure the Security Manager to use this interface +# to persist session data to a backend EIS +# ------------------------------------------------------------------------------- +sessionDao = com.parallax.server.blocklyprop.security.BlocklyPropSessionDao +securityManager.sessionManager.sessionDAO = $sessionDao -# Tell the Shiro security manager to use the CloudSession Realm -securityManager.realms = $cloudsessionRealm -# Configure the session manager -# Web-application capable SessionManager implementation. -sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager -securityManager.sessionManager = $sessionManager +# Security Manager Backend Storage (User Accounts) +# ------------------------------------------------- +# Define interface to the backend storage to hold user credentials. This is the +# Cloud Session interface for user authentication +cloudsessionRealm = com.parallax.server.blocklyprop.security.CloudSessionAuthenticationRealm +securityManager.realms = $cloudsessionRealm -# Configure a SessionDAO and then set it: -sessionDao = com.parallax.server.blocklyprop.security.BlocklyPropSessionDao -securityManager.sessionManager.sessionDAO = $sessionDao -securityManager.sessionManager.sessionIdCookieEnabled = true -# Attach the default session validation scheduler -sessionValidationScheduler = org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler +# Credential Matching +# ------------------- +# Supports direct (plain) comparison for credentials of type byte[], char[], +# and Strings, and if the arguments do not match these types, then reverts back +# to simple Object.equals comparison. +credentialsMatcher = org.apache.shiro.authc.credential.SimpleCredentialsMatcher -# Run once per day -sessionValidationScheduler.interval = 86400000 +#============================================ +# TODO: Determine if this should be wired up +#============================================ +# This appears to be implemented and not referenced in this configuration file +cloudSessionMatcher = com.parallax.server.blocklyprop.security.CloudSessionCredentialsMatcher -# TESTING - Run every 30 seconds -# sessionValidationScheduler.interval = 300000 -securityManager.sessionManager.sessionValidationScheduler = $sessionValidationScheduler +####### TESTING AND DEV SETTINGS ######## +# A credentials matcher that always returns true when matching credentials no matter +# what arguments are passed in. This can be used for testing or when credentials are +# implicitly trusted for a particular Realm. +# ----------------------------------------------------------------------------------- +# allow_all_credentialsMatcher = org.apache.shiro.authc.credential.AllowAllCredentialsMatcher +#-########################################################################################## -# -------------------------------------------------------------------------- -# Disable session management when operating within a multi-host environment -# -------------------------------------------------------------------------- -securityManager.sessionManager.deleteInvalidSessions = false -securityManager.sessionManager.sessionValidationSchedulerEnabled = false -# Set global default session timeout to eight hours -securityManager.sessionManager.globalSessionTimeout = 28800000 ssl.enabled = false shiro.loginUrl = /login.jsp +# Static user accounts go here +# ---------------------------- +[users] + +# Authorization through roles is implemented here +# ----------------------------------------------- +[roles] + + +# Manager access to urls and url groups +# -------------------------------------- [urls] # # A list of accessable URLs @@ -102,7 +136,10 @@ shiro.loginUrl = /login.jsp # Public pages / = anon, ssl /index = anon, ssl + +# Display community projects /projects.jsp = anon, ssl + /public/** = anon /ping = anon /sessionapi = anon @@ -130,3 +167,5 @@ shiro.loginUrl = /login.jsp /** = authc, user, ssl #Testing + + diff --git a/src/main/webapp/WEB-INF/servlet/index.jsp b/src/main/webapp/WEB-INF/servlet/index.jsp index 227131f7..2973256f 100644 --- a/src/main/webapp/WEB-INF/servlet/index.jsp +++ b/src/main/webapp/WEB-INF/servlet/index.jsp @@ -20,7 +20,7 @@ --%> <%-- - Document : login + Document : index.jsp Created on : 24-mei-2015, 18:41:02 Author : Michel diff --git a/src/main/webapp/login.jsp b/src/main/webapp/login.jsp index 05ea760b..2e97de6b 100644 --- a/src/main/webapp/login.jsp +++ b/src/main/webapp/login.jsp @@ -1,5 +1,5 @@ <%-- - ~ Copyright (c) 2018 Parallax Inc. + ~ Copyright (c) 2019 Parallax Inc. ~ ~ Permission is hereby granted, free of charge, to any person obtaining a copy of this software ~ and associated documentation files (the “Software”), to deal in the Software without @@ -17,7 +17,7 @@ ~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ~ SOFTWARE. - --%> +--%> <%-- Document : login Created on : 24-mei-2015, 18:41:02 @@ -66,17 +66,12 @@ "> - " target="oauth">Log in using Google - - - <%@ include file="/WEB-INF/includes/pageparts/footer.jsp"%> - \ No newline at end of file diff --git a/src/main/webapp/my/projects.jsp b/src/main/webapp/my/projects.jsp index 303cec9a..c98da1c8 100644 --- a/src/main/webapp/my/projects.jsp +++ b/src/main/webapp/my/projects.jsp @@ -6,6 +6,7 @@ <%@page contentType="text/html" pageEncoding="UTF-8"%> <%@ include file="/WEB-INF/includes/include.jsp"%> + @@ -29,32 +30,25 @@ + My Projects - <%@ include file="/WEB-INF/includes/pageparts/menu.jsp"%> -
-

- -
- - <%@ include file="/WEB-INF/includes/pageparts/footer.jsp"%> - \ No newline at end of file diff --git a/src/main/webapp/projects.jsp b/src/main/webapp/projects.jsp index 991e687f..ed6df55a 100644 --- a/src/main/webapp/projects.jsp +++ b/src/main/webapp/projects.jsp @@ -1,5 +1,5 @@ <%-- - ~ Copyright (c) 2018 Parallax Inc. + ~ Copyright (c) 2019 Parallax Inc. ~ ~ Permission is hereby granted, free of charge, to any person obtaining a copy of this software ~ and associated documentation files (the “Software”), to deal in the Software without @@ -20,9 +20,11 @@ --%> <%-- - Document : projects + Document : projects.jsp Created on : 24-mei-2015, 18:41:02 Author : Michel + Notes : Display the community projects in a table format + TODO: UI-Convert to client side content with project data sourced from server endpoint. --%> <%@page contentType="text/html" pageEncoding="UTF-8"%>