View Javadoc

1   package org.kuali.student.common.ui.server.gwt;
2   
3   import java.util.HashMap;
4   import java.util.List;
5   import java.util.Map;
6   import java.util.UUID;
7   
8   import org.apache.commons.lang.StringUtils;
9   import org.apache.log4j.Logger;
10  import org.kuali.rice.kew.dto.DocumentDetailDTO;
11  import org.kuali.rice.kew.service.WorkflowUtility;
12  import org.kuali.rice.kim.bo.types.dto.AttributeSet;
13  import org.kuali.rice.kim.service.IdentityManagementService;
14  import org.kuali.student.common.assembly.data.Data;
15  import org.kuali.student.common.assembly.data.Metadata;
16  import org.kuali.student.common.assembly.transform.AuthorizationFilter;
17  import org.kuali.student.common.assembly.transform.MetadataFilter;
18  import org.kuali.student.common.assembly.transform.TransformFilter;
19  import org.kuali.student.common.assembly.transform.TransformationManager;
20  import org.kuali.student.common.assembly.transform.TransformFilter.TransformFilterAction;
21  import org.kuali.student.common.dto.DtoConstants;
22  import org.kuali.student.common.exceptions.DataValidationErrorException;
23  import org.kuali.student.common.exceptions.DoesNotExistException;
24  import org.kuali.student.common.exceptions.OperationFailedException;
25  import org.kuali.student.common.exceptions.VersionMismatchException;
26  import org.kuali.student.common.rice.StudentIdentityConstants;
27  import org.kuali.student.common.rice.authorization.PermissionType;
28  import org.kuali.student.common.ui.client.service.DataSaveResult;
29  import org.kuali.student.common.ui.shared.IdAttributes;
30  import org.kuali.student.common.util.security.SecurityUtils;
31  import org.kuali.student.common.validation.dto.ValidationResultInfo;
32  import org.kuali.student.core.assembly.transform.ProposalWorkflowFilter;
33  import org.kuali.student.core.proposal.dto.ProposalInfo;
34  import org.kuali.student.core.proposal.service.ProposalService;
35  import org.springframework.transaction.annotation.Transactional;
36  
37  @Transactional(readOnly=true,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
38  public abstract class AbstractDataService implements DataService{
39  
40  	private static final long serialVersionUID = 1L;
41  
42  	final Logger LOG = Logger.getLogger(AbstractDataService.class);
43  
44  	private TransformationManager transformationManager;
45  	
46  	private IdentityManagementService permissionService;
47  	
48  	private WorkflowUtility workflowUtilityService;
49  
50      //TODO: why do we have this reference in the base class????
51  	private ProposalService proposalService;
52  
53  	@Override
54  	public Data getData(String id) throws OperationFailedException {
55  		Map<String, Object> filterProperties = getDefaultFilterProperties();
56  		filterProperties.put(TransformFilter.FILTER_ACTION, TransformFilterAction.GET);
57  		filterProperties.put(MetadataFilter.METADATA_ID_VALUE, id);
58  		
59  		String dtoId = id;
60  		//First check if this is a proposal id
61          //TODO: Igor : Why do we check for this when getting the data for programs?
62  		try{
63  			if (proposalService != null){
64  				ProposalInfo proposalInfo = proposalService.getProposal(dtoId);
65  				filterProperties.put(ProposalWorkflowFilter.PROPOSAL_INFO, proposalInfo);
66  				dtoId = proposalInfo.getProposalReference().get(0);
67  			}			
68  
69  			Object dto = get(dtoId);
70  			if (dto != null){
71  				return transformationManager.transform(dto, getDtoClass().getName(), filterProperties);
72  			}
73  		} catch(DoesNotExistException e){
74  			return null;
75  		} catch (Exception e) {
76  			LOG.error("Error getting data",e);
77  			throw new OperationFailedException("Error getting data",e);
78  		}
79  		return null;
80  	}
81  
82  	@Override
83  	public Metadata getMetadata(String id, Map<String, String> attributes) {
84  		Map<String, Object> filterProperties = getDefaultFilterProperties();
85  		filterProperties.put(MetadataFilter.METADATA_ID_VALUE, id);
86  		
87  		//Place id attributes into filter properties
88  		String idType = (attributes != null? attributes.get(IdAttributes.ID_TYPE):null);
89  		String docType = (attributes != null ? attributes.get(StudentIdentityConstants.DOCUMENT_TYPE_NAME):null);
90  		String dtoState = (attributes != null ? attributes.get(DtoConstants.DTO_STATE):null);
91  		String dtoNextState = (attributes != null ? attributes.get(DtoConstants.DTO_NEXT_STATE):null);
92  		String workflowNode = (attributes != null ? attributes.get(DtoConstants.DTO_WORKFLOW_NODE):null);
93  				
94  		if (idType == null){
95  			filterProperties.remove(MetadataFilter.METADATA_ID_TYPE);
96  		} else {
97  			filterProperties.put(MetadataFilter.METADATA_ID_TYPE, idType);
98  		}
99  	
100 		if (docType == null){
101 			filterProperties.put(ProposalWorkflowFilter.WORKFLOW_DOC_TYPE, getDefaultWorkflowDocumentType());
102 		} else {
103 			filterProperties.put(ProposalWorkflowFilter.WORKFLOW_DOC_TYPE, docType);
104 		}
105 
106 		if (dtoState != null){
107 			filterProperties.put(DtoConstants.DTO_STATE, dtoState);			
108 		}
109 		
110 		if (dtoNextState != null){
111 			filterProperties.put(DtoConstants.DTO_NEXT_STATE, dtoNextState);			
112 		}
113 
114 		if (workflowNode != null){
115 			filterProperties.put(DtoConstants.DTO_WORKFLOW_NODE, workflowNode);			
116 		}
117 
118 		if (checkDocumentLevelPermissions()){
119 			filterProperties.put(AuthorizationFilter.DOC_LEVEL_PERM_CHECK, Boolean.TRUE.toString());
120 		}
121 		
122 		Metadata metadata = transformationManager.getMetadata(getDtoClass().getName(), filterProperties); 
123 		return metadata;
124 	}
125 
126 	@Override
127 	@Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
128 	public DataSaveResult saveData(Data data) throws OperationFailedException, DataValidationErrorException, VersionMismatchException{
129 		Map<String, Object> filterProperties = getDefaultFilterProperties();
130 		filterProperties.put(TransformFilter.FILTER_ACTION, TransformFilterAction.SAVE);
131 		
132 		DataSaveResult saveResult = new DataSaveResult();
133 		try {
134 			//Convert data object to dto object
135 			Object dto = transformationManager.transform(data, getDtoClass(), filterProperties);
136 			
137 			//This calls save method for DataService impl, which makes the needed service calls to persist dto
138 			//The service call should do it's own validation, any errors will cause DataValidationErrorException
139 			//and is handled in the catch below.
140 			dto = save(dto, filterProperties);
141 			
142 			//Validate saved data again to get validation warnings that may exist on the data
143 			List<ValidationResultInfo> validationResults = validate(dto);
144 			
145 			//Convert saved data object back to data object to send to UI
146 			Data persistedData = transformationManager.transform(dto, getDtoClass().getName(), filterProperties);			
147 			
148 			saveResult.setValue(persistedData);
149 			saveResult.setValidationResults(validationResults);			
150 		}catch (DataValidationErrorException dvee){
151 			//Throw the error, we need the the transaction to be rolled back when service throws an error.
152 			throw dvee;
153 		}catch (OperationFailedException ofe){
154 		    throw ofe;
155 		}catch (VersionMismatchException vme){
156 		    throw vme;
157 		}catch (Exception e) {
158 			LOG.error("Failed to save data",e);
159 			throw new OperationFailedException("Failed to save data",e);
160 		}
161 		
162 		return saveResult;
163 	}
164 	
165 	
166 
167 	@Override
168 	public List<ValidationResultInfo> validateData(Data data) throws OperationFailedException {
169 		List<ValidationResultInfo> validationResults;
170 		
171 		try {
172 			Metadata metadata = transformationManager.getUnfilteredMetadata(getDtoClass().getName());
173 			Object dto = transformationManager.getMapper().convertFromData(data, getDtoClass(), metadata);
174 			validationResults = validate(dto);
175 		} catch (Exception e) {
176 			throw new OperationFailedException("Unable to validate data", e);
177 		}
178 
179 		return validationResults;
180 	}
181 
182 	@Override
183 	public Boolean isAuthorized(PermissionType type, Map<String,String> attributes) {
184 		String user = SecurityUtils.getCurrentPrincipalId();
185 		boolean result = false;
186 		if (checkDocumentLevelPermissions()) {
187 			if (type == null) {
188 				return null;
189 			}
190 			String namespaceCode = type.getPermissionNamespace();
191 			String permissionTemplateName = type.getPermissionTemplateName();
192 			
193 			AttributeSet roleQuals = new AttributeSet();
194 			AttributeSet permissionDetails = new AttributeSet();
195 			
196 			if (attributes != null) {
197 				//Determine permission details and role qualifiers to pass into permission service.
198 				//We will use same attributes for permission details and role qualifiers (never hurts to use more than needed)
199 				
200 				if (proposalService != null){
201 					ProposalInfo proposalInfo = null;
202 					try {
203 						//Retrieve the proposal info provided the proposal id (passed in as KS_JEW_OBJECT_ID) or the workflow id
204 						if (attributes.containsKey(IdAttributes.IdType.KS_KEW_OBJECT_ID.toString())){
205 							proposalInfo = proposalService.getProposal(attributes.get(IdAttributes.IdType.KS_KEW_OBJECT_ID.toString()));
206 						} else if (attributes.containsKey(IdAttributes.IdType.DOCUMENT_ID.toString())){
207 							proposalInfo = proposalService.getProposalByWorkflowId(attributes.get(IdAttributes.IdType.DOCUMENT_ID.toString()));
208 						}
209 						
210 						//Check if the route status is in the list of allowed statuses
211 						DocumentDetailDTO docDetail = getWorkflowUtilityService().getDocumentDetail(Long.parseLong(proposalInfo.getWorkflowId()));
212 						String routeStatusCode = docDetail.getDocRouteStatus(); 
213 
214 						//Populate attributes with additional attributes required for permission check
215 						if (proposalInfo != null){
216 							attributes.put(IdAttributes.IdType.KS_KEW_OBJECT_ID.toString(), proposalInfo.getId());
217 							attributes.put(StudentIdentityConstants.QUALIFICATION_DATA_ID, proposalInfo.getId()); // this is what most of the permissions/roles check
218 							attributes.put(IdAttributes.IdType.DOCUMENT_ID.toString(), proposalInfo.getWorkflowId());
219 							attributes.put(StudentIdentityConstants.DOCUMENT_TYPE_NAME, proposalInfo.getType());
220 							attributes.put(StudentIdentityConstants.ROUTE_STATUS_CODE, routeStatusCode);
221 						}
222 					} catch (Exception e){
223 						LOG.error("Could not retrieve proposal to determine permission qualifiers:" + e.toString());
224 					}
225 				}
226 				
227 				permissionDetails.putAll(attributes);
228 				
229 				//Put in additional random number for role qualifiers. This is to avoid this request from being cached. 
230 				//Might want to do this only for specific templates to take advantage of caching
231 				attributes.put("RAND_NO_CACHE", UUID.randomUUID().toString());
232 				roleQuals.putAll(attributes);
233 			}
234 			if (StringUtils.isNotBlank(namespaceCode) && StringUtils.isNotBlank(permissionTemplateName)) {
235 				LOG.info("Checking Permission '" + namespaceCode + "/" + permissionTemplateName + "' for user '" + user + "'");
236 				result = getPermissionService().isAuthorizedByTemplateName(user, namespaceCode, permissionTemplateName, permissionDetails, roleQuals);
237 			}
238 			else {
239 				LOG.info("Can not check Permission with namespace '" + namespaceCode + "' and template name '" + permissionTemplateName + "' for user '" + user + "'");
240 				return Boolean.TRUE;
241 			}
242 		}
243 		else {
244 			LOG.info("Will not check for document level permissions. Defaulting authorization to true.");
245 			result = true;
246 		}
247 		LOG.info("Result of authorization check for user '" + user + "': " + result);
248 		return Boolean.valueOf(result);
249 	}
250 	
251 	public Map<String, Object> getDefaultFilterProperties(){
252 		Map<String, Object> filterProperties = new HashMap<String,Object>();
253 		filterProperties.put(MetadataFilter.METADATA_ID_TYPE, StudentIdentityConstants.QUALIFICATION_KEW_OBJECT_ID);
254 		filterProperties.put(ProposalWorkflowFilter.WORKFLOW_USER, SecurityUtils.getCurrentPrincipalId());
255 		
256 		return filterProperties;
257 	}
258 	
259 	protected DataSaveResult _saveData(Data data, Map<String, Object> filterProperties) throws OperationFailedException{
260 		try {
261 			filterProperties.put(MetadataFilter.METADATA_ID_VALUE, (String)data.query("id"));	
262 
263 			Object dto = transformationManager.transform(data, getDtoClass(),filterProperties);
264 			dto = save(dto, filterProperties);
265 				
266 			Data persistedData = transformationManager.transform(dto,getDtoClass().getName(), filterProperties);
267 			return new DataSaveResult(null, persistedData);
268 		} catch (DataValidationErrorException dvee){
269 			return new DataSaveResult(dvee.getValidationResults(), null);
270 		} catch (Exception e) {
271 			LOG.error("Unable to save", e);
272 			throw new OperationFailedException("Unable to save");
273 		}		
274 	}
275 	
276 	protected boolean checkDocumentLevelPermissions() {
277 		return false;
278 	}
279 
280 
281 
282 	public TransformationManager getTransformationManager() {
283 		return transformationManager;
284 	}
285 
286 	public void setTransformationManager(TransformationManager transformationManager) {
287 		this.transformationManager = transformationManager;
288 	}
289 
290 	public IdentityManagementService getPermissionService() {
291 		return permissionService;
292 	}
293 
294 	public void setPermissionService(IdentityManagementService permissionService) {
295 		this.permissionService = permissionService;
296 	}
297 	
298 	public ProposalService getProposalService() {
299 		return proposalService;
300 	}
301 
302 	public void setProposalService(ProposalService proposalService) {
303 		this.proposalService = proposalService;
304 	}
305 	
306 	public WorkflowUtility getWorkflowUtilityService() {
307 		return workflowUtilityService;
308 	}
309 
310 	public void setWorkflowUtilityService(WorkflowUtility workflowUtilityService) {
311 		this.workflowUtilityService = workflowUtilityService;
312 	}
313 
314 	protected abstract String getDefaultWorkflowDocumentType();
315 	
316 	protected abstract String getDefaultMetaDataState();
317 	
318 	/**
319 	 * Implement this method to make to make service call to get DTO object. The method is called
320 	 * by the get(Data) method before it invokes transformationManager to convert DTO to a Data map 
321 	 * 
322 	 * @param id DTO id
323 	 * @return the dto retrieved by calling the appropriate service method
324 	 * @throws Exception
325 	 */
326 	protected abstract Object get(String id) throws Exception;
327 	
328 	/**
329 	 * Implement this method to make a service call to get DTO object. The method is called	 
330 	 * by the save(Data) method after it invokes transformationManager to convert Data map to DTO 
331 	 * 
332 	 * @param dto
333 	 * @param properties
334 	 * @return the persisted dto object
335 	 * @throws Exception
336 	 */
337 	protected abstract Object save(Object dto, Map<String, Object> properties) throws Exception;
338 	
339 	/**
340 	 * Implement this method to make a service call to get DTO object. The method is called	 
341 	 * in the save(data) method before calling the save(dto,properties) method to validate the data
342  
343 	 * @param dto
344 	 * @return
345 	 * @throws Exception
346 	 */
347 	protected abstract List<ValidationResultInfo> validate(Object dto) throws Exception;
348 
349 	/**
350 	 * Implement this method to return the type of the dto object.
351 	 * 
352 	 * @return The object type returned and expected by the get & save dto methods
353 	 */
354 	protected abstract Class<?> getDtoClass();
355 }