001 package org.hackystat.sensorbase.resource.sensorbase; 002 003 import org.hackystat.sensorbase.resource.projects.ProjectManager; 004 import org.hackystat.sensorbase.resource.projects.jaxb.Project; 005 import org.hackystat.sensorbase.resource.sensordata.SensorDataManager; 006 import org.hackystat.sensorbase.resource.sensordatatypes.SdtManager; 007 import org.hackystat.sensorbase.resource.users.UserManager; 008 import org.hackystat.sensorbase.resource.users.jaxb.User; 009 import org.hackystat.sensorbase.server.Server; 010 import org.restlet.Context; 011 import org.restlet.data.CharacterSet; 012 import org.restlet.data.Language; 013 import org.restlet.data.MediaType; 014 import org.restlet.data.Request; 015 import org.restlet.data.Response; 016 import org.restlet.data.Status; 017 import org.restlet.resource.Representation; 018 import org.restlet.resource.Resource; 019 import org.restlet.resource.StringRepresentation; 020 import org.restlet.resource.Variant; 021 022 /** 023 * An abstract superclass for all SensorBase resources that supplies common 024 * initialization and validation processing. 025 * <p> 026 * Initialization processing includes: 027 * <ul> 028 * <li> Extracting the authenticated user identifier (when authentication available) 029 * <li> Extracting the user email from the URI (when available) 030 * <li> Declares that the TEXT/XML representational variant is supported. 031 * <li> Providing instance variables bound to the ProjectManager, SdtManager, UserManager, and 032 * SensorDataManager. 033 * </ul> 034 * <p> 035 * Validation processing involves a set of "validated" methods. These check the values 036 * of various parameters in the request, potentially initializing instance variables 037 * as a result. If the validation process fails, these methods set the Restlet 038 * Status value appropriately and return false. 039 * 040 * @author Philip Johnson 041 * 042 */ 043 public abstract class SensorBaseResource extends Resource { 044 045 /** The authenticated user, retrieved from the ChallengeResponse, or null. */ 046 protected String authUser = null; 047 048 /** To be retrieved from the URL as the 'user' template parameter, or null. */ 049 protected String uriUser = null; 050 051 /** The user instance corresponding to the user indicated in the URI string, or null. */ 052 protected User user = null; 053 054 /** The projectName found within the URL string, or null. */ 055 protected String projectName = null; 056 057 /** The project corresponding to the projectName, or null. */ 058 protected Project project = null; 059 060 /** The ProjectManager. */ 061 protected ProjectManager projectManager = null; 062 063 /** The UserManager. */ 064 protected UserManager userManager = null; 065 066 /** The SdtManager. */ 067 protected SdtManager sdtManager = null; 068 069 /** The SensorDataManager. */ 070 protected SensorDataManager sensorDataManager = null; 071 072 /** The server. */ 073 protected Server server; 074 075 /** Everyone generally wants to create one of these, so declare it here. */ 076 protected String responseMsg; 077 078 /** 079 * Provides the following representational variants: TEXT_XML. 080 * @param context The context. 081 * @param request The request object. 082 * @param response The response object. 083 */ 084 public SensorBaseResource(Context context, Request request, Response response) { 085 super(context, request, response); 086 if (request.getChallengeResponse() != null) { 087 this.authUser = request.getChallengeResponse().getIdentifier(); 088 } 089 this.server = (Server)getContext().getAttributes().get("SensorBaseServer"); 090 this.uriUser = (String) request.getAttributes().get("user"); 091 this.projectName = (String) request.getAttributes().get("projectname"); 092 093 this.projectManager = (ProjectManager)getContext().getAttributes().get("ProjectManager"); 094 this.userManager = (UserManager)getContext().getAttributes().get("UserManager"); 095 this.sdtManager = (SdtManager)getContext().getAttributes().get("SdtManager"); 096 this.sensorDataManager = 097 (SensorDataManager)getContext().getAttributes().get("SensorDataManager"); 098 getVariants().clear(); // copied from BookmarksResource.java, not sure why needed. 099 getVariants().add(new Variant(MediaType.TEXT_XML)); 100 } 101 102 103 /** 104 * The Restlet getRepresentation method which must be overridden by all concrete Resources. 105 * @param variant The variant requested. 106 * @return The Representation. 107 */ 108 @Override 109 public abstract Representation represent(Variant variant); 110 111 /** 112 * Creates and returns a new Restlet StringRepresentation built from xmlData. 113 * The xmlData will be prefixed with a processing instruction indicating UTF-8 and version 1.0. 114 * @param xmlData The xml data as a string. 115 * @return A StringRepresentation of that xmldata. 116 */ 117 public static StringRepresentation getStringRepresentation(String xmlData) { 118 StringBuilder builder = new StringBuilder(500); 119 builder.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); 120 builder.append(xmlData); 121 return new StringRepresentation(builder, MediaType.TEXT_XML, Language.ALL, CharacterSet.UTF_8); 122 } 123 124 /** 125 * Returns true if the authorized user is the administrator. 126 * Otherwise sets the Response status and returns false. 127 * @return True if the authorized user is the admin. 128 */ 129 protected boolean validateAuthUserIsAdmin() { 130 try { 131 if (userManager.isAdmin(this.authUser)) { 132 return true; 133 } 134 else { 135 this.responseMsg = ResponseMessage.adminOnly(this); 136 getResponse().setStatus(Status.CLIENT_ERROR_UNAUTHORIZED, removeNewLines(this.responseMsg)); 137 return false; 138 } 139 } 140 catch (RuntimeException e) { 141 this.responseMsg = ResponseMessage.internalError(this, this.getLogger(), e); 142 getResponse().setStatus(Status.SERVER_ERROR_INTERNAL, removeNewLines(this.responseMsg)); 143 } 144 return false; 145 } 146 147 /** 148 * Returns true if the user in the URI string is defined in the UserManager. 149 * Otherwise sets the Response status and returns false. 150 * If it returns true, then this.user has the corresponding User instance. 151 * @return True if the URI user is a real user. 152 */ 153 protected boolean validateUriUserIsUser() { 154 try { 155 this.user = this.userManager.getUser(this.uriUser); 156 if (this.user == null) { 157 this.responseMsg = ResponseMessage.undefinedUser(this, this.uriUser); 158 getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST, removeNewLines(this.responseMsg)); 159 return false; 160 } 161 else { 162 return true; 163 } 164 } 165 catch (RuntimeException e) { 166 this.responseMsg = ResponseMessage.internalError(this, this.getLogger(), e); 167 getResponse().setStatus(Status.SERVER_ERROR_INTERNAL, removeNewLines(this.responseMsg)); 168 } 169 return false; 170 } 171 172 173 /** 174 * Returns true if the project name in the URI string is defined in the ProjectManager. 175 * Otherwise sets the Response status and returns false. 176 * If it returns true, then this.project has the corresponding Project instance. 177 * @return True if the URI project name is a real project. 178 */ 179 protected boolean validateUriProjectName() { 180 try { 181 this.project = projectManager.getProject(this.user, this.projectName); 182 if (this.project == null) { 183 this.responseMsg = ResponseMessage.undefinedProject(this, this.user, this.projectName); 184 getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST, removeNewLines(this.responseMsg)); 185 return false; 186 } 187 else { 188 return true; 189 } 190 } 191 catch (RuntimeException e) { 192 this.responseMsg = ResponseMessage.internalError(this, this.getLogger(), e); 193 getResponse().setStatus(Status.SERVER_ERROR_INTERNAL, removeNewLines(this.responseMsg)); 194 } 195 return false; 196 } 197 198 /** 199 * Returns true if the authorized user is the owner of the project in the URL string. 200 * Otherwise sets the Response status and returns false. 201 * @return True if the authorized user is the owner of the Project. 202 */ 203 protected boolean validateProjectOwner() { 204 try { 205 if (project.getOwner().equals(this.authUser)) { 206 return true; 207 } 208 else { 209 this.responseMsg = ResponseMessage.notProjectOwner(this, this.authUser, this.projectName); 210 getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST, removeNewLines(this.responseMsg)); 211 return false; 212 } 213 } 214 catch (RuntimeException e) { 215 this.responseMsg = ResponseMessage.internalError(this, this.getLogger(), e); 216 getResponse().setStatus(Status.SERVER_ERROR_INTERNAL, removeNewLines(this.responseMsg)); 217 } 218 return false; 219 } 220 221 /** 222 * Returns true if the authorized user is either the admin or user in the URI string. 223 * Otherwise sets the Response status and returns false. 224 * @return True if the authorized user is the admin or the URI user. 225 */ 226 protected boolean validateAuthUserIsAdminOrUriUser() { 227 try { 228 if (userManager.isAdmin(this.authUser) || this.uriUser.equals(this.authUser)) { 229 return true; 230 } 231 else { 232 this.responseMsg = ResponseMessage.adminOrAuthUserOnly(this, this.authUser, this.uriUser); 233 getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST, removeNewLines(this.responseMsg)); 234 return false; 235 } 236 } 237 catch (RuntimeException e) { 238 this.responseMsg = ResponseMessage.internalError(this, this.getLogger(), e); 239 getResponse().setStatus(Status.SERVER_ERROR_INTERNAL, removeNewLines(this.responseMsg)); 240 } 241 return false; 242 } 243 244 245 /** 246 * Returns true if the authorized user can view the project definition. 247 * This is true if the authorized user is the admin, the project owner, or member, 248 * spectator, or invitee. 249 * Otherwise sets the Response status and returns false. 250 * @return True if the authorized user is a project participant. 251 */ 252 protected boolean validateProjectViewer() { 253 try { 254 if (userManager.isAdmin(this.authUser) || 255 uriUser.equals(this.authUser) || 256 projectManager.isMember(this.user, this.projectName, this.authUser) || 257 projectManager.isInvited(this.user, this.projectName, this.authUser) || 258 projectManager.isSpectator(this.user, this.projectName, this.authUser)) { 259 return true; 260 } 261 else { 262 setStatusMiscError(String.format("User %s not authorized to view project %s", this.authUser, 263 this.projectName)); 264 return false; 265 } 266 } 267 catch (RuntimeException e) { 268 this.responseMsg = ResponseMessage.internalError(this, this.getLogger(), e); 269 getResponse().setStatus(Status.SERVER_ERROR_INTERNAL, removeNewLines(this.responseMsg)); 270 } 271 return false; 272 } 273 274 /** 275 * Helper function that removes any newline characters from the supplied string and 276 * replaces them with a blank line. 277 * @param msg The msg whose newlines are to be removed. 278 * @return The string without newlines. 279 */ 280 private String removeNewLines(String msg) { 281 return msg.replace(System.getProperty("line.separator"), " "); 282 } 283 284 /** 285 * Called when an exception is caught while processing a request. 286 * Just sets the response code. 287 * @param timestamp The timestamp that could not be parsed. 288 */ 289 protected void setStatusBadTimestamp (String timestamp) { 290 this.responseMsg = ResponseMessage.badTimestamp(this, timestamp); 291 getResponse().setStatus(Status.SERVER_ERROR_INTERNAL, removeNewLines(this.responseMsg)); 292 } 293 294 295 /** 296 * Called when an exception is caught while processing a request. 297 * Just sets the response code. 298 * @param e The exception that was caught. 299 */ 300 protected void setStatusInternalError (Exception e) { 301 this.responseMsg = ResponseMessage.internalError(this, this.getLogger(), e); 302 getResponse().setStatus(Status.SERVER_ERROR_INTERNAL, removeNewLines(this.responseMsg)); 303 } 304 305 /** 306 * Called when a miscellaneous "one off" error is caught during processing. 307 * @param msg A description of the error. 308 */ 309 protected void setStatusMiscError (String msg) { 310 this.responseMsg = ResponseMessage.miscError(this, msg); 311 getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST, removeNewLines(this.responseMsg)); 312 } 313 }