Java tutorial
/* * Copyright 2012-2013 inBloom, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.slc.sli.api.security.pdp; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.slc.sli.api.config.BasicDefinitionStore; import org.slc.sli.api.config.EntityDefinition; import org.slc.sli.api.constants.PathConstants; import org.slc.sli.api.constants.ResourceNames; import org.slc.sli.api.exceptions.UriMutationException; import org.slc.sli.api.security.SLIPrincipal; import org.slc.sli.api.security.context.APIAccessDeniedException; import org.slc.sli.api.security.context.resolver.EdOrgHelper; import org.slc.sli.api.security.context.resolver.GradingPeriodHelper; import org.slc.sli.api.security.context.resolver.SectionHelper; import org.slc.sli.api.util.SecurityUtil; import org.slc.sli.api.util.SessionUtil; import org.slc.sli.common.constants.EntityNames; import org.slc.sli.common.constants.ParameterConstants; import org.slc.sli.common.util.datetime.DateHelper; import org.slc.sli.domain.Entity; import org.slc.sli.domain.NeutralCriteria; import org.slc.sli.domain.NeutralQuery; import org.slc.sli.domain.Repository; import org.slc.sli.domain.enums.Right; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.ws.rs.core.PathSegment; import java.util.*; import java.util.regex.Matcher; /** * Infers context about the {user,requested resource} pair, and restricts * blanket API calls to smaller (and generally more manageable) scope. */ @Component public class UriMutator { private static final Logger LOG = LoggerFactory.getLogger(UriMutator.class); public static final int NUM_SEGMENTS_IN_TWO_PART_REQUEST = 3; public static final int NUM_SEGMENTS_IN_ONE_PART_REQUEST = 2; @Resource private EdOrgHelper edOrgHelper; @Resource private SectionHelper sectionHelper; @Resource private RootSearchMutator rootSearchMutator; @Autowired @Qualifier("validationRepo") private Repository<Entity> repo; @Autowired private BasicDefinitionStore definitionStore; @Autowired private DateHelper dateHelper; private Map<String, MutateInfo> teacherSectionMutations; @Autowired private GradingPeriodHelper gradingPeriodHelper; private static final List<Pair<String, String>> PARAMETER_RESOURCE_PAIRS = Arrays.asList( Pair.of(ParameterConstants.STUDENT_UNIQUE_STATE_ID, ResourceNames.STUDENTS), Pair.of(ParameterConstants.STAFF_UNIQUE_STATE_ID, ResourceNames.STAFF), Pair.of(ParameterConstants.PARENT_UNIQUE_STATE_ID, ResourceNames.PARENTS), Pair.of(ParameterConstants.STATE_ORGANIZATION_ID, ResourceNames.EDUCATION_ORGANIZATIONS)); /** * Acts as a filter to determine if the requested resource, given knowledge * of the user requesting it, should be rewritten. An empty path or * queryString in the returned {@link MutatedContainer} indicates they don't * need to be rewritten. * * @param segments * List of Path Segments representing request URI. * @param queryParameters * String containing query parameters. * @param principal * User requesting resource. * @param clientId * The clientId String from the OAuth2 ClientToken. * @return MutatedContainer representing {mutated path (if necessary), * mutated parameters (if necessary)}, where path will be null or * parameters the empty string if they didn't need to be rewritten. */ public MutatedContainer mutate(List<PathSegment> segments, String queryParameters, SLIPrincipal principal, String clientId) { Entity user = principal.getEntity(); // First check for mutations dictated by query parameters Map<String, String> parameters = MutatorUtil.getParameterMap(queryParameters); for (Pair<String, String> parameterResourcePair : PARAMETER_RESOURCE_PAIRS) { String parameter = parameterResourcePair.getLeft(); String resource = parameterResourcePair.getRight(); if (parameters.containsKey(parameter)) { EntityDefinition definition = definitionStore.lookupByResourceName(resource); if (definition != null) { NeutralQuery query = new NeutralQuery(new NeutralCriteria(parameter, NeutralCriteria.OPERATOR_EQUAL, parameters.get(parameter))); Entity e = repo.findOne(definition.getType(), query); if (e != null) { MutatedContainer newMutated = new MutatedContainer(); String path = String.format("/%s/%s", resource, e.getEntityId()); if (EntityNames.TEACHER.equals(e.getType())) { path = String.format("/teachers/%s", e.getEntityId()); } else if (EntityNames.STAFF.equals(e.getType())) { path = String.format("/staff/%s", e.getEntityId()); } newMutated.setPath(path); newMutated.setQueryParameters(queryParameters); LOG.info("Rewriting URI to {} based on natural keys", newMutated.getPath()); return newMutated; } } } } // Next check for "general mutations" MutatedContainer generalMutation = doGeneralMutations(stringifyPathSegments(segments), queryParameters, user); if (generalMutation != null) { return generalMutation; } MutatedContainer mutated; if (segments.size() < NUM_SEGMENTS_IN_TWO_PART_REQUEST) { if (shouldSkipMutation(segments, queryParameters, principal, clientId)) { // Send a response that indicates no mutation is necessary mutated = new MutatedContainer(); mutated.setPath(null); mutated.setQueryParameters(queryParameters); } else { if (segments.size() == 1) { // api/v1 mutated = mutateBaseUri(segments.get(0).getPath(), ResourceNames.HOME, queryParameters, user); } else { mutated = mutateBaseUri(segments.get(0).getPath(), segments.get(1).getPath(), queryParameters, user); } } } else { mutated = mutateUriBasedOnRole(segments, queryParameters, user); } return mutated; } private Set<String> publicResourcesThatAllowSearch; @PostConstruct void init() { publicResourcesThatAllowSearch = new HashSet<String>( Arrays.asList(ResourceNames.EDUCATION_ORGANIZATIONS, ResourceNames.SCHOOLS)); teacherSectionMutations = new HashMap<String, MutateInfo>() { private static final long serialVersionUID = 1L; { // TWO TYPE put(joinPathSegments(PathConstants.ASSESSMENTS, PathConstants.STUDENT_ASSESSMENTS), new MutateInfo( "/sections/%s/studentSectionAssociations/students/studentAssessments", "assessmentId")); put(joinPathSegments(PathConstants.COURSES, PathConstants.COURSE_TRANSCRIPTS), new MutateInfo( "/sections/%s/studentSectionAssociations/students/studentAcademicRecords/courseTranscripts", "courseId")); put(joinPathSegments(PathConstants.COURSE_OFFERINGS, PathConstants.SECTIONS), new MutateInfo("/sections/%s/", "courseOfferingId")); put(joinPathSegments(PathConstants.SESSIONS, PathConstants.SECTIONS), new MutateInfo("/sections/%s/", "sessionId")); put(joinPathSegments(PathConstants.LEARNING_OBJECTIVES, PathConstants.STUDENT_COMPETENCIES), new MutateInfo("/sections/%s/studentSectionAssociations/studentCompetencies", "objectiveId.learningObjectiveId")); put(joinPathSegments(PathConstants.GRADING_PERIODS, PathConstants.REPORT_CARDS), new MutateInfo( "/sections/%s/studentSectionAssociations/students/reportCards", "gradingPeriodId")); put(joinPathSegments(PathConstants.GRADING_PERIODS, PathConstants.GRADES), new MutateInfo("/sections/%s/studentSectionAssociations/grades", "gradingPeriodId")); put(joinPathSegments(PathConstants.SESSIONS, PathConstants.STUDENT_ACADEMIC_RECORDS), new MutateInfo("/sections/%s/studentSectionAssociations/students/studentAcademicRecords", "sessionId")); put(joinPathSegments(PathConstants.SCHOOLS, PathConstants.STUDENT_SCHOOL_ASSOCIATIONS), new MutateInfo("/sections/%s/studentSectionAssociations/students/studentSchoolAssociations", null)); put(joinPathSegments(PathConstants.EDUCATION_ORGANIZATIONS, PathConstants.COHORTS), new MutateInfo("/teachers/%s/staffCohortAssociations/cohorts", "educationOrgId", true)); put(joinPathSegments(PathConstants.SCHOOLS, PathConstants.SECTIONS), new MutateInfo("/teachers/%s/teacherSectionAssociations/sections", null, true)); put(joinPathSegments(PathConstants.EDUCATION_ORGANIZATIONS, PathConstants.STUDENT_SCHOOL_ASSOCIATIONS), new MutateInfo("/sections/%s/studentSectionAssociations/students/studentSchoolAssociations", null)); // THREE TYPE put(joinPathSegments(PathConstants.SCHOOLS, PathConstants.SECTIONS, PathConstants.GRADEBOOK_ENTRIES), new MutateInfo("/sections/%s/gradebookEntries", null)); put(joinPathSegments(PathConstants.SCHOOLS, PathConstants.SECTIONS, PathConstants.STUDENT_SECTION_ASSOCIATIONS), new MutateInfo("/sections/%s/studentSectionAssociations", null)); put(joinPathSegments(PathConstants.SCHOOLS, PathConstants.STUDENT_SCHOOL_ASSOCIATIONS, PathConstants.STUDENTS), new MutateInfo("/sections/%s/studentSectionAssociations/students", null)); put(joinPathSegments(PathConstants.EDUCATION_ORGANIZATIONS, PathConstants.STUDENT_SCHOOL_ASSOCIATIONS, PathConstants.STUDENTS), new MutateInfo("/sections/%s/studentSectionAssociations/students", null)); // FOUR TYPE put(joinPathSegments(PathConstants.SCHOOLS, PathConstants.SECTIONS, PathConstants.STUDENT_SECTION_ASSOCIATIONS, PathConstants.GRADES), new MutateInfo("/sections/%s/studentSectionAssociations/grades", null)); put(joinPathSegments(PathConstants.SCHOOLS, PathConstants.SECTIONS, PathConstants.STUDENT_SECTION_ASSOCIATIONS, PathConstants.STUDENT_COMPETENCIES), new MutateInfo("/sections/%s/studentSectionAssociations/studentCompetencies", null)); put(joinPathSegments(PathConstants.SCHOOLS, PathConstants.STUDENT_SCHOOL_ASSOCIATIONS, PathConstants.STUDENTS, PathConstants.ATTENDANCES), new MutateInfo("/sections/%s/studentSectionAssociations/students/attendances", null)); put(joinPathSegments(PathConstants.SCHOOLS, PathConstants.STUDENT_SCHOOL_ASSOCIATIONS, PathConstants.STUDENTS, PathConstants.COURSE_TRANSCRIPTS), new MutateInfo( "/sections/%s/studentSectionAssociations/students/studentAcademicRecords/courseTranscripts", null)); put(joinPathSegments(PathConstants.SCHOOLS, PathConstants.STUDENT_SCHOOL_ASSOCIATIONS, PathConstants.STUDENTS, PathConstants.REPORT_CARDS), new MutateInfo("/sections/%s/studentSectionAssociations/students/reportCards", null)); put(joinPathSegments(PathConstants.SCHOOLS, PathConstants.STUDENT_SCHOOL_ASSOCIATIONS, PathConstants.STUDENTS, PathConstants.STUDENT_ACADEMIC_RECORDS), new MutateInfo("/sections/%s/studentSectionAssociations/students/studentAcademicRecords", null)); put(joinPathSegments(PathConstants.SCHOOLS, PathConstants.STUDENT_SCHOOL_ASSOCIATIONS, PathConstants.STUDENTS, PathConstants.STUDENT_ASSESSMENTS), new MutateInfo("/sections/%s/studentSectionAssociations/students/studentAssessments", null)); put(joinPathSegments(PathConstants.SCHOOLS, PathConstants.STUDENT_SCHOOL_ASSOCIATIONS, PathConstants.STUDENTS, PathConstants.STUDENT_GRADEBOOK_ENTRIES), new MutateInfo("/sections/%s/studentSectionAssociations/students/studentGradebookEntries", null)); put(joinPathSegments(PathConstants.SCHOOLS, PathConstants.STUDENT_SCHOOL_ASSOCIATIONS, PathConstants.STUDENTS, PathConstants.STUDENT_PARENT_ASSOCIATIONS), new MutateInfo("/sections/%s/studentSectionAssociations/students/studentParentAssociations", null)); put(joinPathSegments(PathConstants.SCHOOLS, PathConstants.TEACHER_SCHOOL_ASSOCIATIONS, PathConstants.TEACHERS, PathConstants.TEACHER_SECTION_ASSOCIATIONS), new MutateInfo("/teachers/%s/teacherSectionAssociations", null, true)); // FIVE TYPE put(joinPathSegments(PathConstants.SCHOOLS, PathConstants.STUDENT_SCHOOL_ASSOCIATIONS, PathConstants.STUDENTS, PathConstants.STUDENT_PARENT_ASSOCIATIONS, PathConstants.PARENTS), new MutateInfo( "/sections/%s/studentSectionAssociations/students/studentParentAssociations/parents", null)); put(joinPathSegments(PathConstants.SCHOOLS, PathConstants.STUDENT_SCHOOL_ASSOCIATIONS, PathConstants.STUDENTS, PathConstants.STUDENT_ACADEMIC_RECORDS, PathConstants.COURSE_TRANSCRIPTS), new MutateInfo( "/sections/%s/studentSectionAssociations/students/studentAcademicRecords/courseTranscripts", null)); } }; } private boolean shouldSkipMutation(List<PathSegment> segments, String queryParameters, SLIPrincipal principal, String clientId) { return shouldSkipMutationToEnableSearch(segments, queryParameters) || (SessionUtil.isAdminApp(clientId, repo) && hasAppAuthRight(principal) && isEducationOrganizationsEndPoint(segments)); } // Check whether the principal has the 'APP_AUTHORIZE' right for any of its // roles in any edorg private boolean hasAppAuthRight(SLIPrincipal principal) { return principal.getAllRights(true).contains(Right.APP_AUTHORIZE); } // Check whether the 'educationOrganizations' endpoint is hit directly private boolean isEducationOrganizationsEndPoint(List<PathSegment> segments) { boolean skipMutation = false; if (segments.size() == NUM_SEGMENTS_IN_ONE_PART_REQUEST) { final int baseResourceIndex = 1; if (ResourceNames.EDUCATION_ORGANIZATIONS.equals(segments.get(baseResourceIndex).getPath())) { skipMutation = true; } } return skipMutation; } private boolean shouldSkipMutationToEnableSearch(List<PathSegment> segments, String queryParameters) { boolean skipMutation = false; if (segments.size() == NUM_SEGMENTS_IN_ONE_PART_REQUEST) { String[] queries = queryParameters != null ? queryParameters.split("&") : new String[0]; for (String query : queries) { if (!query.matches( "(limit|offset|expandDepth|includeFields|excludeFields|sortBy|sortOrder|optionalFields|views|includeCustom|selector)=.+")) { final int baseResourceIndex = 1; } } } return skipMutation; } private static class MutateInfo { private final String mutatedPathFormat; private final String mutatedParameter; private final boolean usePrincipleId; private MutateInfo(String mutatedPathFormat, String mutatedParameter, boolean usePrincipleId) { this.mutatedPathFormat = mutatedPathFormat; this.mutatedParameter = mutatedParameter; this.usePrincipleId = usePrincipleId; } private MutateInfo(String mutatedPathFormat, String mutatedParameter) { this(mutatedPathFormat, mutatedParameter, false); } public String getMutatedPathFormat() { return mutatedPathFormat; } public String getMutatedParameter() { return mutatedParameter; } public boolean isUsePrincipleId() { return usePrincipleId; } } /** * Mutates the API call (not to a base entity) to a more-specific (and * generally more constrained) URI based on the user's role. * * @param segments * List of Path Segments representing request URI. * @param queryParameters * String containing query parameters. * @param user * User requesting resource. * @return MutatedContainer representing {mutated path (if necessary), * mutated parameters (if necessary)}, where path or parameters will * be null if they didn't need to be rewritten. */ private MutatedContainer mutateUriBasedOnRole(List<PathSegment> segments, String queryParameters, Entity user) throws IllegalArgumentException { MutatedContainer mutatedPathAndParameters = null; if (mutateToTeacher()) { mutatedPathAndParameters = mutateTeacherRequest(segments, queryParameters, user); } else if (mutateToStaff()) { mutatedPathAndParameters = mutateStaffRequest(segments, queryParameters, user); } else if (isStudent(user) || isParent(user)) { mutatedPathAndParameters = mutateStudentParentRequest(stringifyPathSegments(segments), queryParameters, user); } return mutatedPathAndParameters; } private MutatedContainer doGeneralMutations(List<String> segmentStrings, String queryParameters, Entity user) { MutatedContainer mutated = null; // default; indicates no "general mutations" int segmentSize = segmentStrings.size(); if (segmentSize == 2) { String baseEntity = segmentStrings.get(1); // does following mutation // v1.2/calendarDates -> v1.2/calendarDates/<user's edOrg Ids> if (baseEntity.equals(PathConstants.CALENDAR_DATES)) { mutated = MutatedContainer.generate(queryParameters, "/educationOrganizations/%s/calendarDates", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ",")); } else if (baseEntity.equals(PathConstants.CLASS_PERIODS)) { mutated = MutatedContainer.generate(queryParameters, "/educationOrganizations/%s/classPeriods", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ",")); } else if (baseEntity.equals(PathConstants.BELL_SCHEDULES)) { mutated = MutatedContainer.generate(queryParameters, "/educationOrganizations/%s/bellSchedules", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ",")); } } else if (segmentSize == 4) { String baseEntity = segmentStrings.get(1); String resourceEntity = segmentStrings.get(3); // does following mutation // v1.2/gradingPeriods/b62056a2f0463864ed2d81dd6ce121c1b7b8d950_id/calendarDates // -> v1.2/calendarDates/521570eb6dc0e988c4553b91c6c4dadc2a02c487_id // where 521570eb6dc0e988c4553b91c6c4dadc2a02c487_id is the // calendarDateReference inside gradingPeriod // b62056a2f0463864ed2d81dd6ce121c1b7b8d950_id // The ThreePartResource.java does not work for this relationship // because it wrongly assumes that calendarDate contains a reference // to gradingPeriod. if (baseEntity.equals("gradingPeriods") && resourceEntity.equals(PathConstants.CALENDAR_DATES)) { String[] gradingPeriodIds = segmentStrings.get(2).split(","); List<String> gradingPeriodCalendarDates = gradingPeriodHelper .getCalendarDatesForGradingPeriods(queryParameters, gradingPeriodIds); if (gradingPeriodCalendarDates.size() > 0) { mutated = new MutatedContainer(); mutated.setQueryParameters(queryParameters != null ? queryParameters : ""); String mutatedURL = String.format("/calendarDates/%s", StringUtils.join(gradingPeriodCalendarDates.toArray(), ",")); mutated.setPath(mutatedURL); } else { // no calendarDates associated with this grading Period LOG.info("Cannot find any calendarDates associated with gradingPeriods [{}]", Arrays.asList(gradingPeriodIds)); } } } return mutated; } private MutatedContainer mutateStudentParentRequest(List<String> segmentStrings, String queryParameters, Entity user) { MutatedContainer mutated = new MutatedContainer(); mutated.setQueryParameters(queryParameters != null ? queryParameters : ""); SLIPrincipal principal = SecurityUtil.getSLIPrincipal(); if (segmentStrings.size() == 2) { String baseEntity = segmentStrings.get(1); if (PathConstants.COURSES.equals(baseEntity)) { mutated.setPath(String.format("/schools/%s/courses", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); } else if (Arrays.asList(PathConstants.SCHOOLS, PathConstants.EDUCATION_ORGANIZATIONS) .contains(baseEntity)) { mutated.setPath( String.format("/schools/%s", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); } else if (PathConstants.COURSE_OFFERINGS.equals(baseEntity)) { mutated.setPath(String.format("/schools/%s/courseOfferings", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); } else if (PathConstants.SESSIONS.equals(baseEntity)) { mutated.setPath(String.format("/schools/%s/sessions", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); } else if (PathConstants.GRADING_PERIODS.equals(baseEntity)) { mutated.setPath(String.format("/schools/%s/sessions/gradingPeriods", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); } else if (PathConstants.GRADUATION_PLANS.equals(baseEntity)) { mutated.setPath(String.format("/schools/%s/graduationPlans", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); } else if (Arrays.asList(PathConstants.LEARNING_STANDARDS, PathConstants.LEARNING_OBJECTIVES) .contains(baseEntity)) { mutated.setPath(String.format("/%s", baseEntity)); } else if (PathConstants.STUDENT_COMPETENCY_OBJECTIVES.equals(baseEntity)) { mutated.setPath(String.format("/educationOrganizations/%s/studentCompetencyObjectives", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); } else if (PathConstants.ASSESSMENTS.equals(baseEntity)) { mutated.setPath( String.format("/students/%s/studentAssessments/assessments", getStudentIds(principal))); } else if (PathConstants.ATTENDANCES.equals(baseEntity)) { mutated.setPath(String.format("/students/%s/attendances", getStudentIds(principal))); } else if (PathConstants.COHORTS.equals(baseEntity)) { mutated.setPath( String.format("/students/%s/studentCohortAssociations/cohorts", getStudentIds(principal))); } else if (PathConstants.COURSE_TRANSCRIPTS.equals(baseEntity)) { mutated.setPath(String.format("/studentAcademicRecords/%s/courseTranscripts", getStudentAcademicRecordsIds(user))); } else if (PathConstants.GRADES.equals(baseEntity)) { mutated.setPath(String.format("/studentSectionAssociations/%s/grades", getStudentSectionAssocIds(principal))); } else if (PathConstants.GRADEBOOK_ENTRIES.equals(baseEntity)) { mutated.setPath(String.format("/sections/%s/gradebookEntries", getSectionIds(principal))); } else if (PathConstants.PARENTS.equals(baseEntity)) { mutated.setPath( String.format("/students/%s/studentParentAssociations/parents", getStudentIds(principal))); } else if (PathConstants.PROGRAMS.equals(baseEntity)) { mutated.setPath(String.format("/students/%s/studentProgramAssociations/programs", getStudentIds(principal))); } else if (PathConstants.SECTIONS.equals(baseEntity)) { mutated.setPath(String.format("/students/%s/studentSectionAssociations/sections", getStudentIds(principal))); } else if (PathConstants.REPORT_CARDS.equals(baseEntity)) { mutated.setPath(String.format("/students/%s/reportCards", getStudentIds(principal))); } else if (PathConstants.TEACHERS.equals(baseEntity)) { mutated.setPath(String.format("/sections/%s/teacherSectionAssociations/teachers", getSectionIds(principal))); } else if (PathConstants.TEACHER_SECTION_ASSOCIATIONS.equals(baseEntity)) { mutated.setPath(String.format("/sections/%s/teacherSectionAssociations", getSectionIds(principal))); } else if (PathConstants.TEACHER_SCHOOL_ASSOCIATIONS.equals(baseEntity)) { mutated.setPath(String.format("/schools/%s/teacherSchoolAssociations", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); } else if (PathConstants.STAFF.equals(baseEntity)) { mutated.setPath( String.format("/educationOrganizations/%s/staffEducationOrgAssignmentAssociations/staff", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); } else if (PathConstants.STAFF_COHORT_ASSOCIATIONS.equals(baseEntity)) { mutated.setPath(String.format("/cohorts/%s/staffCohortAssociations", getCohortIds(principal))); } else if (PathConstants.STAFF_EDUCATION_ORGANIZATION_ASSOCIATIONS.equals(baseEntity)) { mutated.setPath(String.format("/educationOrganizations/%s/staffEducationOrgAssignmentAssociations", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); } else if (PathConstants.STAFF_PROGRAM_ASSOCIATIONS.equals(baseEntity)) { mutated.setPath(String.format("/programs/%s/staffProgramAssociations", getProgramIds(principal))); } else if (PathConstants.STUDENTS.equals(baseEntity)) { if (isStudent(user)) { mutated.setPath(String.format("/sections/%s/studentSectionAssociations/students", getSectionIds(principal))); } else if (isParent(user)) { mutated.setPath( String.format("/parents/%s/studentParentAssociations/students", user.getEntityId())); } } else if (PathConstants.STUDENT_ACADEMIC_RECORDS.equals(baseEntity)) { mutated.setPath(String.format("/students/%s/studentAcademicRecords", getStudentIds(principal))); } else if (PathConstants.STUDENT_ASSESSMENTS.equals(baseEntity)) { mutated.setPath(String.format("/students/%s/studentAssessments", getStudentIds(principal))); } else if (PathConstants.STUDENT_COHORT_ASSOCIATIONS.equals(baseEntity)) { mutated.setPath(String.format("/students/%s/studentCohortAssociations", getStudentIds(principal))); } else if (PathConstants.STUDENT_COMPETENCIES.equals(baseEntity)) { mutated.setPath(String.format("/studentSectionAssociations/%s/studentCompetencies", getStudentSectionAssocIds(principal))); } else if (PathConstants.STUDENT_GRADEBOOK_ENTRIES.equals(baseEntity)) { mutated.setPath(String.format("/students/%s/studentGradebookEntries", StringUtils.join(getStudentIds(principal)))); } else if (PathConstants.STUDENT_PARENT_ASSOCIATIONS.equals(baseEntity)) { if (isStudent(user)) { mutated.setPath(String.format("/students/%s/studentParentAssociations", StringUtils.join(getStudentIds(principal)))); } else if (isParent(user)) { mutated.setPath(String.format("/parents/%s/studentParentAssociations", user.getEntityId())); } } else if (PathConstants.STUDENT_PROGRAM_ASSOCIATIONS.equals(baseEntity)) { mutated.setPath(String.format("/students/%s/studentProgramAssociations", StringUtils.join(getStudentIds(principal)))); } else if (PathConstants.STUDENT_SCHOOL_ASSOCIATIONS.equals(baseEntity)) { mutated.setPath(String.format("/students/%s/studentSchoolAssociations", StringUtils.join(getStudentIds(principal)))); } else if (PathConstants.STUDENT_SECTION_ASSOCIATIONS.equals(baseEntity)) { mutated.setPath(String.format("/students/%s/studentSectionAssociations", StringUtils.join(getStudentIds(principal)))); } else if (PathConstants.YEARLY_ATTENDANCES.equals(baseEntity)) { mutated.setPath(String.format("/students/%s/yearlyAttendances", StringUtils.join(getStudentIds(principal)))); } else if (PathConstants.DISCIPLINE_ACTIONS.equals(baseEntity)) { throw new APIAccessDeniedException("Students do not have access to discipline actions.", true); } else if (PathConstants.DISCIPLINE_INCIDENTS.equals(baseEntity)) { throw new APIAccessDeniedException("Students do not have access to discipline incidents.", true); } else if (PathConstants.STUDENT_DISCIPLINE_INCIDENT_ASSOCIATIONS.equals(baseEntity)) { throw new APIAccessDeniedException( "Students do not have access to discipline incident associations.", true); } else if (ResourceNames.HOME.equals(baseEntity)) { mutated.setPath("/home"); } else if (ResourceNames.COMPETENCY_LEVEL_DESCRIPTORS.equals(baseEntity)) { mutated.setPath("/" + baseEntity); } else { throw new IllegalArgumentException("Not supported yet..."); } } else if (segmentStrings.size() >= 4) { String baseEntity = segmentStrings.get(1); String secondEntity = segmentStrings.get(3); if (ResourceNames.STUDENT_SECTION_ASSOCIATIONS.equals(segmentStrings.get(1))) { // /studentSectionAssociation/{id}/students and // /studentSectionAssociation/{id}/sections // have to be re-written to /section/{id}/* if (ResourceNames.STUDENTS.equals(secondEntity) || ResourceNames.SECTIONS.equals(secondEntity)) { List<String> sections = sectionHelper .getSectionsFromStudentSectionAssociation(segmentStrings.get(2).split(",")); String sectionUri = String.format("/sections/%s", StringUtils.join(sections.toArray(new String[0]), ",")); if (ResourceNames.STUDENTS.equals(secondEntity)) { sectionUri = sectionUri + "/studentSectionAssociations/students"; } mutated.setPath(sectionUri); } } else if (ResourceNames.LEARNINGOBJECTIVES.equals(baseEntity) && ResourceNames.STUDENT_COMPETENCIES.equals(secondEntity)) { throw new APIAccessDeniedException("url is not accessible to students or parents", true); } } return mutated; } private MutatedContainer mutateTeacherRequest(List<PathSegment> segments, String queryParameters, Entity user) { MutatedContainer mutated = new MutatedContainer(); mutated.setQueryParameters(queryParameters); List<String> segmentStrings = stringifyPathSegments(segments); String joinedSegments = null; String baseEntityIds = null; if (segmentStrings.size() > NUM_SEGMENTS_IN_TWO_PART_REQUEST) { int ENTITY_IDS_SEGMENT_INDEX = 2; int API_VERSION_SEGMENT_INDEX = 0; baseEntityIds = segmentStrings.get(ENTITY_IDS_SEGMENT_INDEX); segmentStrings.remove(ENTITY_IDS_SEGMENT_INDEX); segmentStrings.remove(API_VERSION_SEGMENT_INDEX); joinedSegments = joinPathSegments(segmentStrings); } if (joinedSegments != null) { MutateInfo mutateInfo = teacherSectionMutations.get(joinedSegments); if (mutateInfo != null) { String ids; if (mutateInfo.isUsePrincipleId()) { ids = user.getEntityId(); } else { ids = StringUtils.join(sectionHelper.getTeachersSections(user), ","); } mutated.setPath(String.format(mutateInfo.getMutatedPathFormat(), ids)); if (mutateInfo.getMutatedParameter() != null) { verifySingleTransitiveId(baseEntityIds); mutated.setQueryParameters(mutuateQueryParameterString(mutateInfo.getMutatedParameter(), baseEntityIds, queryParameters)); } } } return mutated; } private MutatedContainer mutateStaffRequest(List<PathSegment> segments, String queryParameters, Entity user) { MutatedContainer mutated = new MutatedContainer(); mutated.setQueryParameters(queryParameters != null ? queryParameters : ""); List<String> segmentStrings = stringifyPathSegments(segments); if (segmentStrings.size() == 4) { String baseEntity = segmentStrings.get(1); String transitiveEntityId = segmentStrings.get(2); String requestedEntity = segmentStrings.get(3); String modifiedRequest = reconnectPathSegments(Arrays.asList(baseEntity, requestedEntity)); if (modifiedRequest.equals(PathConstants.ASSESSMENTS + ";" + PathConstants.STUDENT_ASSESSMENTS + ";")) { verifySingleTransitiveId(transitiveEntityId); mutated.setPath(String.format("/schools/%s/studentSchoolAssociations/students/studentAssessments", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); mutated.setQueryParameters(mutuateQueryParameterString("assessmentId", transitiveEntityId, mutated.getQueryParameters())); } else if (modifiedRequest .equals(PathConstants.COURSES + ";" + PathConstants.COURSE_TRANSCRIPTS + ";")) { verifySingleTransitiveId(transitiveEntityId); mutated.setPath(String.format( "/schools/%s/studentSchoolAssociations/students/studentAcademicRecords/courseTranscripts", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); mutated.setQueryParameters( mutuateQueryParameterString("courseId", transitiveEntityId, mutated.getQueryParameters())); } else if (modifiedRequest .equals(PathConstants.COURSE_OFFERINGS + ";" + PathConstants.SECTIONS + ";")) { verifySingleTransitiveId(transitiveEntityId); mutated.setPath(String.format("/schools/%s/sections", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); mutated.setQueryParameters(mutuateQueryParameterString("courseOfferingId", transitiveEntityId, mutated.getQueryParameters())); } else if (modifiedRequest.equals(PathConstants.GRADING_PERIODS + ";" + PathConstants.GRADES + ";")) { verifySingleTransitiveId(transitiveEntityId); mutated.setPath(String.format("/schools/%s/sections/studentSectionAssociations/grades", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); mutated.setQueryParameters(mutuateQueryParameterString("gradingPeriodId", transitiveEntityId, mutated.getQueryParameters())); } else if (modifiedRequest .equals(PathConstants.GRADING_PERIODS + ";" + PathConstants.REPORT_CARDS + ";")) { verifySingleTransitiveId(transitiveEntityId); mutated.setPath(String.format("/schools/%s/studentSchoolAssociations/students/reportCards", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); mutated.setQueryParameters(mutuateQueryParameterString("gradingPeriodId", transitiveEntityId, mutated.getQueryParameters())); } else if (modifiedRequest.equals(PathConstants.SESSIONS + ";" + PathConstants.SECTIONS + ";")) { verifySingleTransitiveId(transitiveEntityId); mutated.setPath(String.format("/schools/%s/sections", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); mutated.setQueryParameters( mutuateQueryParameterString("sessionId", transitiveEntityId, mutated.getQueryParameters())); } else if (modifiedRequest .equals(PathConstants.SESSIONS + ";" + PathConstants.STUDENT_ACADEMIC_RECORDS + ";")) { verifySingleTransitiveId(transitiveEntityId); mutated.setPath( String.format("/schools/%s/studentSchoolAssociations/students/studentAcademicRecords", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); mutated.setQueryParameters( mutuateQueryParameterString("sessionId", transitiveEntityId, mutated.getQueryParameters())); } else if (modifiedRequest .equals(PathConstants.LEARNING_OBJECTIVES + ";" + PathConstants.STUDENT_COMPETENCIES + ";")) { verifySingleTransitiveId(transitiveEntityId); mutated.setPath(String.format("/schools/%s/sections/studentSectionAssociations/studentCompetencies", StringUtils.join(edOrgHelper.getStaffEdOrgsAndChildren(), ","))); mutated.setQueryParameters(mutuateQueryParameterString("learningObjectiveId", transitiveEntityId, mutated.getQueryParameters())); } } return mutated; } /** * Joins a list of path segments and returns a string representing the path * traversed. * * @param segments * List of Strings representing Path Segments. * @return String representing the list of Path Segments. */ protected String joinPathSegments(String... segments) { return joinPathSegments(Arrays.asList(segments)); } /** * Joins a list of path segments and returns a string representing the path * traversed. * * @param segments * List of Strings representing Path Segments. * @return String representing the list of Path Segments. */ protected String joinPathSegments(List<String> segments) { StringBuilder builder = new StringBuilder(); boolean firstSegment = true; for (String segment : segments) { if (firstSegment) { firstSegment = false; } else { builder.append(";"); } builder.append(segment); } return builder.toString(); } /** * Reconnects a list of path segments and returns a string representing the * path traversed. * * @param segments * List of Strings representing Path Segments. * @return String representing the list of Path Segments. */ protected String reconnectPathSegments(List<String> segments) { StringBuilder builder = new StringBuilder(); for (String segment : segments) { builder.append(segment).append(";"); } return builder.toString(); } /** * Mutates the existing query parameter string by pre-pending the _id of the * transitive entity that's part of the rewritten URI. * * @param transitiveEntityField * Field used to identify transitive entity. * @param transitiveEntityId * UUID of the transitive entity. * @param existingParameters * Existing query parameter string. * @return String representing new query parameter string. */ protected String mutuateQueryParameterString(String transitiveEntityField, String transitiveEntityId, String existingParameters) { if (existingParameters != null) { return transitiveEntityField + "=" + transitiveEntityId + "&" + existingParameters; } else { return transitiveEntityField + "=" + transitiveEntityId; } } /** * Throws Illegal Argument exception if there are multiple _id's specified * in the transitive _id path segment. * * @param id * String representing transitive _id path segment. * @throws IllegalArgumentException * Thrown if multiple _id's are specified (only one should be * specified). */ protected void verifySingleTransitiveId(String id) throws IllegalArgumentException { if (id.split(",").length > 1) { throw new IllegalArgumentException(); } } /** * Stringifies the specified list of path segments into a list of strings. * * @param segments * List of Path Segments. * @return List of Strings representing the input list of Path Segments. */ protected List<String> stringifyPathSegments(List<PathSegment> segments) { List<String> stringified = new ArrayList<String>(); if (segments != null && !segments.isEmpty()) { for (PathSegment segment : segments) { stringified.add(segment.getPath()); } } return stringified; } private MutatedContainer mutateBaseUriForTeacher(String resource, String mutatedParameters, Entity user) { MutatedContainer mutated = new MutatedContainer(); mutated.setQueryParameters(mutatedParameters); if (ResourceNames.LEARNINGOBJECTIVES.equals(resource) || ResourceNames.LEARNINGSTANDARDS.equals(resource) || ResourceNames.ASSESSMENTS.equals(resource) || ResourceNames.COMPETENCY_LEVEL_DESCRIPTORS.equals(resource) || ResourceNames.STUDENT_COMPETENCY_OBJECTIVES.equals(resource) || ResourceNames.SESSIONS.equals(resource) || ResourceNames.COURSES.equals(resource) || ResourceNames.COURSE_OFFERINGS.equals(resource) || ResourceNames.GRADING_PERIODS.equals(resource)) { mutated.setPath("/" + resource); Map<String, String> mutatedHeaders = new HashMap<String, String>(); mutatedHeaders.put("Content-Type", "application/json"); mutated.setHeaders(mutatedHeaders); } else if (ResourceNames.HOME.equals(resource)) { mutated.setPath("/" + resource); } else if (ResourceNames.ATTENDANCES.equals(resource)) { if (mutatedParameters.contains(ParameterConstants.SECTION_ID)) { return formQueryBasedOnParameter("/sections/%s/studentSectionAssociations/students/attendances", mutatedParameters, ParameterConstants.SECTION_ID); } else { mutated.setPath(String.format("/sections/%s/studentSectionAssociations/students/attendances", StringUtils.join(sectionHelper.getTeachersSections(user), ","))); } } else if (ResourceNames.COHORTS.equals(resource)) { mutated.setPath(String.format("/staff/%s/staffCohortAssociations/cohorts", user.getEntityId())); } else if (ResourceNames.COURSE_TRANSCRIPTS.equals(resource)) { if (mutatedParameters.contains(ParameterConstants.SECTION_ID)) { return formQueryBasedOnParameter( "/sections/%s/studentSectionAssociations/students/studentAcademicRecords/courseTranscripts", mutatedParameters, ParameterConstants.SECTION_ID); } else { String ids = StringUtils.join(sectionHelper.getTeachersSections(user), ","); mutated.setPath(String.format( "/sections/%s/studentSectionAssociations/students/studentAcademicRecords/courseTranscripts", ids)); } } else if (ResourceNames.DISCIPLINE_ACTIONS.equals(resource)) { mutated.setPath(String.format("/staff/%s/disciplineActions", user.getEntityId())); } else if (ResourceNames.DISCIPLINE_INCIDENTS.equals(resource)) { mutated.setPath(String.format("/staff/%s/disciplineIncidents", user.getEntityId())); } else if (ResourceNames.EDUCATION_ORGANIZATIONS.equals(resource)) { mutated.setPath(String.format("/teachers/%s/teacherSchoolAssociations/schools", user.getEntityId())); } else if (ResourceNames.GRADES.equals(resource)) { if (mutatedParameters.contains(ParameterConstants.SECTION_ID)) { return formQueryBasedOnParameter("/sections/%s/studentSectionAssociations/grades", mutatedParameters, ParameterConstants.SECTION_ID); } else { String ids = StringUtils.join(sectionHelper.getTeachersSections(user), ","); mutated.setPath(String.format("/sections/%s/studentSectionAssociations/grades", ids)); } } else if (ResourceNames.GRADEBOOK_ENTRIES.equals(resource)) { if (mutatedParameters.contains(ParameterConstants.SECTION_ID)) { return formQueryBasedOnParameter("/sections/%s/gradebookEntries", mutatedParameters, ParameterConstants.SECTION_ID); } else { mutated.setPath(String.format("/sections/%s/gradebookEntries", StringUtils.join(sectionHelper.getTeachersSections(user), ","))); } } else if (ResourceNames.PARENTS.equals(resource)) { if (mutatedParameters.contains(ParameterConstants.SECTION_ID)) { return formQueryBasedOnParameter( "/sections/%s/studentSectionAssociations/students/studentParentAssociations/parents", mutatedParameters, ParameterConstants.SECTION_ID); } else { mutated.setPath(String.format( "/sections/%s/studentSectionAssociations/students/studentParentAssociations/parents", StringUtils.join(sectionHelper.getTeachersSections(user), ","))); } } else if (ResourceNames.PROGRAMS.equals(resource)) { mutated.setPath(String.format("/staff/%s/staffProgramAssociations/programs", user.getEntityId())); } else if (ResourceNames.REPORT_CARDS.equals(resource)) { if (mutatedParameters.contains(ParameterConstants.SECTION_ID)) { return formQueryBasedOnParameter("/sections/%s/studentSectionAssociations/students/reportCards", mutatedParameters, ParameterConstants.SECTION_ID); } else { String ids = StringUtils.join(sectionHelper.getTeachersSections(user), ","); mutated.setPath(String.format("/sections/%s/studentSectionAssociations/students/reportCards", ids)); } } else if (ResourceNames.SECTIONS.equals(resource)) { mutated.setPath(String.format("/teachers/%s/teacherSectionAssociations/sections", user.getEntityId())); } else if (ResourceNames.SCHOOLS.equals(resource)) { mutated.setPath(String.format("/teachers/%s/teacherSchoolAssociations/schools", user.getEntityId())); // teachers/id/teacherschoolassociations/schools } else if (ResourceNames.STAFF.equals(resource)) { mutated.setPath( String.format("/educationOrganizations/%s/staffEducationOrgAssignmentAssociations/staff", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); } else if (ResourceNames.STAFF_COHORT_ASSOCIATIONS.equals(resource)) { mutated.setPath(String.format("/staff/%s/staffCohortAssociations", user.getEntityId())); } else if (ResourceNames.STAFF_EDUCATION_ORGANIZATION_ASSOCIATIONS.equals(resource)) { mutated.setPath(String.format("/educationOrganizations/%s/staffEducationOrgAssignmentAssociations", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); } else if (ResourceNames.STAFF_PROGRAM_ASSOCIATIONS.equals(resource)) { mutated.setPath(String.format("/staff/%s/staffProgramAssociations", user.getEntityId())); } else if (ResourceNames.STUDENTS.equals(resource)) { if (mutatedParameters.contains(ParameterConstants.SECTION_ID)) { return formQueryBasedOnParameter("/sections/%s/studentSectionAssociations/students", mutatedParameters, ParameterConstants.SECTION_ID); } else { String ids = StringUtils.join(sectionHelper.getTeachersSections(user), ","); mutated.setPath(String.format("/sections/%s/studentSectionAssociations/students", ids)); } } else if (ResourceNames.STUDENT_ACADEMIC_RECORDS.equals(resource)) { if (mutatedParameters.contains(ParameterConstants.SECTION_ID)) { return formQueryBasedOnParameter( "/sections/%s/studentSectionAssociations/students/studentAcademicRecords", mutatedParameters, ParameterConstants.SECTION_ID); } else { String ids = StringUtils.join(sectionHelper.getTeachersSections(user), ","); mutated.setPath(String .format("/sections/%s/studentSectionAssociations/students/studentAcademicRecords", ids)); } } else if (ResourceNames.STUDENT_ASSESSMENTS.equals(resource)) { if (mutatedParameters.contains(ParameterConstants.SECTION_ID)) { return formQueryBasedOnParameter( "/sections/%s/studentSectionAssociations/students/studentAssessments", mutatedParameters, ParameterConstants.SECTION_ID); } else { String ids = StringUtils.join(sectionHelper.getTeachersSections(user), ","); mutated.setPath( String.format("/sections/%s/studentSectionAssociations/students/studentAssessments", ids)); } } else if (ResourceNames.STUDENT_COHORT_ASSOCIATIONS.equals(resource)) { mutated.setPath(String.format("/staff/%s/staffCohortAssociations/cohorts/studentCohortAssociations", user.getEntityId())); } else if (ResourceNames.STUDENT_DISCIPLINE_INCIDENT_ASSOCIATIONS.equals(resource)) { mutated.setPath(String.format("/staff/%s/disciplineIncidents/studentDisciplineIncidentAssociations", user.getEntityId())); } else if (ResourceNames.STUDENT_COMPETENCIES.equals(resource)) { if (mutatedParameters.contains(ParameterConstants.SECTION_ID)) { return formQueryBasedOnParameter("/sections/%s/studentSectionAssociations/studentCompetencies", mutatedParameters, ParameterConstants.SECTION_ID); } else { String ids = StringUtils.join(sectionHelper.getTeachersSections(user), ","); mutated.setPath(String.format("/sections/%s/studentSectionAssociations/studentCompetencies", ids)); } } else if (ResourceNames.STUDENT_COMPETENCY_OBJECTIVES.equals(resource)) { mutated.setPath(String.format("/educationOrganizations/%s/studentCompetencyObjectives", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); } else if (ResourceNames.STUDENT_GRADEBOOK_ENTRIES.equals(resource)) { if (mutatedParameters.contains(ParameterConstants.SECTION_ID)) { return formQueryBasedOnParameter( "/sections/%s/studentSectionAssociations/students/studentGradebookEntries", mutatedParameters, ParameterConstants.SECTION_ID); } else { String ids = StringUtils.join(sectionHelper.getTeachersSections(user), ","); mutated.setPath(String .format("/sections/%s/studentSectionAssociations/students/studentGradebookEntries", ids)); } } else if (ResourceNames.STUDENT_PARENT_ASSOCIATIONS.equals(resource)) { if (mutatedParameters.contains(ParameterConstants.SECTION_ID)) { return formQueryBasedOnParameter( "/sections/%s/studentSectionAssociations/students/studentParentAssociations", mutatedParameters, ParameterConstants.SECTION_ID); } else { String ids = StringUtils.join(sectionHelper.getTeachersSections(user), ","); mutated.setPath(String .format("/sections/%s/studentSectionAssociations/students/studentParentAssociations", ids)); } } else if (ResourceNames.STUDENT_PROGRAM_ASSOCIATIONS.equals(resource)) { mutated.setPath(String.format("/staff/%s/staffProgramAssociations/programs/studentProgramAssociations", user.getEntityId())); } else if (ResourceNames.STUDENT_SCHOOL_ASSOCIATIONS.equals(resource)) { if (mutatedParameters.contains(ParameterConstants.SECTION_ID)) { return formQueryBasedOnParameter( "/sections/%s/studentSectionAssociations/students/studentSchoolAssociations", mutatedParameters, ParameterConstants.SECTION_ID); } else { String ids = StringUtils.join(sectionHelper.getTeachersSections(user), ","); mutated.setPath(String .format("sections/%s/studentSectionAssociations/students/studentSchoolAssociations", ids)); } } else if (ResourceNames.STUDENT_SECTION_ASSOCIATIONS.equals(resource)) { if (mutatedParameters.contains(ParameterConstants.SECTION_ID)) { return formQueryBasedOnParameter("/sections/%s/studentSectionAssociations", mutatedParameters, ParameterConstants.SECTION_ID); } else { mutated.setPath(String.format("/sections/%s/studentSectionAssociations", StringUtils.join(sectionHelper.getTeachersSections(user), ","))); } } else if (ResourceNames.TEACHERS.equals(resource)) { mutated.setPath(String.format("/schools/%s/teacherSchoolAssociations/teachers", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); } else if (ResourceNames.TEACHER_SCHOOL_ASSOCIATIONS.equals(resource)) { mutated.setPath(String.format("/teachers/%s/teacherSchoolAssociations", user.getEntityId())); } else if (ResourceNames.TEACHER_SECTION_ASSOCIATIONS.equals(resource)) { mutated.setPath(String.format("/teachers/%s/teacherSectionAssociations", user.getEntityId())); } return mutated; } private MutatedContainer mutateBaseUriForStaff(String resource, final String mutatedParameters, Entity user, String queryParameters) { MutatedContainer mutated = new MutatedContainer(); mutated.setQueryParameters(mutatedParameters); if (ResourceNames.LEARNINGOBJECTIVES.equals(resource) || ResourceNames.LEARNINGSTANDARDS.equals(resource) || ResourceNames.ASSESSMENTS.equals(resource) || ResourceNames.COMPETENCY_LEVEL_DESCRIPTORS.equals(resource) || ResourceNames.STUDENT_COMPETENCY_OBJECTIVES.equals(resource) || ResourceNames.SESSIONS.equals(resource) || ResourceNames.COURSES.equals(resource) || ResourceNames.COURSE_OFFERINGS.equals(resource) || ResourceNames.GRADING_PERIODS.equals(resource)) { mutated.setPath("/" + resource); Map<String, String> mutatedHeaders = new HashMap<String, String>(); mutatedHeaders.put("Content-Type", "application/json"); mutated.setHeaders(mutatedHeaders); } else if (ResourceNames.HOME.equals(resource)) { mutated.setPath("/" + resource); } else if (ResourceNames.ATTENDANCES.equals(resource)) { String ids = getQueryValueForQueryParameters(ParameterConstants.STUDENT_ID, queryParameters); if (ids != null) { mutated.setQueryParameters( removeQueryFromQueryParameters(ParameterConstants.STUDENT_ID, queryParameters)); mutated.setPath(String.format("/students/%s/attendances", ids)); } else { ids = StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","); mutated.setPath(String.format("/schools/%s/studentSchoolAssociations/students/attendances", ids)); } } else if (ResourceNames.COHORTS.equals(resource)) { mutated.setPath(String.format("/staff/%s/staffCohortAssociations/cohorts", user.getEntityId())); } else if (ResourceNames.COURSE_TRANSCRIPTS.equals(resource)) { String ids = StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","); mutated.setPath(String.format( "/schools/%s/studentSchoolAssociations/students/studentAcademicRecords/courseTranscripts", ids)); } else if (ResourceNames.DISCIPLINE_ACTIONS.equals(resource)) { mutated.setPath(String.format("/staff/%s/disciplineActions", user.getEntityId())); } else if (ResourceNames.DISCIPLINE_INCIDENTS.equals(resource)) { mutated.setPath(String.format("/staff/%s/disciplineIncidents", user.getEntityId())); } else if (ResourceNames.EDUCATION_ORGANIZATIONS.equals(resource)) { mutated.setPath( String.format("/staff/%s/staffEducationOrgAssignmentAssociations/educationOrganizations", user.getEntityId())); } else if (ResourceNames.GRADES.equals(resource)) { String ids = StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","); mutated.setPath(String.format("/schools/%s/sections/studentSectionAssociations/grades", ids)); } else if (ResourceNames.GRADEBOOK_ENTRIES.equals(resource)) { mutated.setPath(String.format("/schools/%s/sections/gradebookEntries", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); } else if (ResourceNames.PARENTS.equals(resource)) { String ids = StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","); mutated.setPath(String.format( "/schools/%s/studentSchoolAssociations/students/studentParentAssociations/parents", ids)); } else if (ResourceNames.PROGRAMS.equals(resource)) { mutated.setPath(String.format("/staff/%s/staffProgramAssociations/programs", user.getEntityId())); } else if (ResourceNames.REPORT_CARDS.equals(resource)) { String ids = StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","); mutated.setPath(String.format("/schools/%s/studentSchoolAssociations/students/reportCards", ids)); } else if (ResourceNames.SCHOOLS.equals(resource)) { mutated.setPath( String.format("/staff/%s/staffEducationOrgAssignmentAssociations/schools", user.getEntityId())); } else if (ResourceNames.SECTIONS.equals(resource)) { String ids = StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","); mutated.setPath(String.format("/schools/%s/sections", ids)); } else if (ResourceNames.STAFF.equals(resource)) { String ids = StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","); mutated.setPath( String.format("/educationOrganizations/%s/staffEducationOrgAssignmentAssociations/staff", ids)); } else if (ResourceNames.STAFF_COHORT_ASSOCIATIONS.equals(resource)) { mutated.setPath(String.format("/staff/%s/staffCohortAssociations", user.getEntityId())); } else if (ResourceNames.STAFF_EDUCATION_ORGANIZATION_ASSOCIATIONS.equals(resource)) { mutated.setPath(String.format("/staff/%s/staffEducationOrgAssignmentAssociations", user.getEntityId())); } else if (ResourceNames.STAFF_PROGRAM_ASSOCIATIONS.equals(resource)) { mutated.setPath(String.format("/staff/%s/staffProgramAssociations", user.getEntityId())); } else if (ResourceNames.STUDENTS.equals(resource)) { String ids = StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","); mutated.setPath(String.format("/schools/%s/studentSchoolAssociations/students", ids)); } else if (ResourceNames.STUDENT_ACADEMIC_RECORDS.equals(resource)) { String ids = StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","); mutated.setPath( String.format("/schools/%s/studentSchoolAssociations/students/studentAcademicRecords", ids)); } else if (ResourceNames.STUDENT_ASSESSMENTS.equals(resource)) { String ids = StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","); mutated.setPath( String.format("/schools/%s/studentSchoolAssociations/students/studentAssessments", ids)); } else if (ResourceNames.STUDENT_COHORT_ASSOCIATIONS.equals(resource)) { mutated.setPath(String.format("/staff/%s/staffCohortAssociations/cohorts/studentCohortAssociations", user.getEntityId())); } else if (ResourceNames.STUDENT_COMPETENCIES.equals(resource)) { String ids = StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","); mutated.setPath( String.format("/schools/%s/sections/studentSectionAssociations/studentCompetencies", ids)); } else if (ResourceNames.STUDENT_COMPETENCY_OBJECTIVES.equals(resource)) { mutated.setPath(String.format("/educationOrganizations/%s/studentCompetencyObjectives", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); } else if (ResourceNames.STUDENT_DISCIPLINE_INCIDENT_ASSOCIATIONS.equals(resource)) { mutated.setPath(String.format("/staff/%s/disciplineIncidents/studentDisciplineIncidentAssociations", user.getEntityId())); } else if (ResourceNames.STUDENT_GRADEBOOK_ENTRIES.equals(resource)) { String ids = StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","); mutated.setPath( String.format("/schools/%s/studentSchoolAssociations/students/studentGradebookEntries", ids)); } else if (ResourceNames.STUDENT_PARENT_ASSOCIATIONS.equals(resource)) { String ids = StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","); mutated.setPath( String.format("/schools/%s/studentSchoolAssociations/students/studentParentAssociations", ids)); } else if (ResourceNames.STUDENT_PROGRAM_ASSOCIATIONS.equals(resource)) { mutated.setPath(String.format("/staff/%s/staffProgramAssociations/programs/studentProgramAssociations", user.getEntityId())); } else if (ResourceNames.STUDENT_SCHOOL_ASSOCIATIONS.equals(resource)) { String ids = StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","); mutated.setPath(String.format("/schools/%s/studentSchoolAssociations", ids)); } else if (ResourceNames.STUDENT_SECTION_ASSOCIATIONS.equals(resource)) { String ids = StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","); mutated.setPath(String.format("/schools/%s/sections/studentSectionAssociations", ids)); } else if (ResourceNames.TEACHERS.equals(resource)) { String ids = StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","); mutated.setPath(String.format("/schools/%s/teacherSchoolAssociations/teachers", ids)); } else if (ResourceNames.TEACHER_SCHOOL_ASSOCIATIONS.equals(resource)) { String ids = StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","); mutated.setPath(String.format("/schools/%s/teacherSchoolAssociations", ids)); } else if (ResourceNames.PROGRAMS.equals(resource)) { mutated.setPath(String.format("/staff/%s/staffProgramAssociations/programs", user.getEntityId())); } else if (ResourceNames.PARENTS.equals(resource)) { mutated.setPath(String.format( "/schools/%s/studentSchoolAssociations/students/studentParentAssociations/parents", StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","))); } else if (ResourceNames.TEACHER_SECTION_ASSOCIATIONS.equals(resource)) { String ids = StringUtils.join(edOrgHelper.getDirectEdorgs(user), ","); mutated.setPath(String .format("/schools/%s/teacherSchoolAssociations/teachers/teacherSectionAssociations", ids)); } return mutated; } /** * Mutates the API call (to a base entity) to a more-specific (and generally * more constrained) URI. * * @param resource * - root resource being accessed. * @param version * - API version. * @param queryParameters * - URI query parameters. * @param user * - entity representing user making API call. * @return Mutated String representing new API call, or null if no mutation * takes place. */ public MutatedContainer mutateBaseUri(String version, String resource, final String queryParameters, Entity user) { MutatedContainer mutated = new MutatedContainer(); mutated.setQueryParameters(queryParameters); if (mutated.getQueryParameters() == null) { mutated.setQueryParameters(""); } mutated.setPath(rootSearchMutator.mutatePath(version, resource, mutated.getQueryParameters())); if (mutated.getPath() == null && mutateToTeacher()) { return this.mutateBaseUriForTeacher(resource, mutated.getQueryParameters(), user); } else if (mutated.getPath() == null && mutateToStaff()) { return this.mutateBaseUriForStaff(resource, mutated.getQueryParameters(), user, mutated.getQueryParameters()); } else if (mutated.getPath() == null && isStudent(user) || isParent(user)) { return this.mutateStudentParentRequest(Arrays.<String>asList(version, resource), mutated.getQueryParameters(), user); } else { return mutated; } } private String getQueryValueForQueryParameters(String queryName, String queryParameters) { String queryValue = null; String[] queries = queryParameters.split("&"); String queryRegEx = "^" + Matcher.quoteReplacement(queryName) + "=.+"; for (String query : queries) { if (query.matches(queryRegEx)) { int indexOfQueryValue = queryRegEx.length() - 3; queryValue = query.substring(indexOfQueryValue); break; } } return queryValue; } /** * Removes the first occurrence of the specified queryName from the * specified queryParameters String. Almost redundant with * {@link removeQueryParameter}, except that method removes all occurrences. * * @param queryName Name of a parameter to remove from the queryString * @param queryParameters The full queryString */ private String removeQueryFromQueryParameters(String queryName, String queryParameters) { String queryRegEx = Matcher.quoteReplacement(queryName) + "=[^&]*&?"; String rslt = queryParameters.replaceFirst(Matcher.quoteReplacement(queryRegEx), ""); if (rslt.endsWith("&")) { // The queryRegEx will leave a trailing & if the removed parameter was the last rslt = rslt.substring(0, rslt.length() - 1); } return rslt; } private boolean isStudent(Entity principal) { return principal.getType().equals(EntityNames.STUDENT); } private boolean isParent(Entity principal) { return principal.getType().equals(EntityNames.PARENT); } /** * Determines if the URI should be mutated to a teacher context for a * federated user. * * @return - True if the user has teacher context, false otherwise */ private boolean mutateToTeacher() { return (SecurityUtil.getUserContext() == SecurityUtil.UserContext.TEACHER_CONTEXT); } /** * Determines if the URI should be mutated to a staff context for a * federated user. * * @return - True if the user has staff or dual context, false otherwise */ private boolean mutateToStaff() { return ((SecurityUtil.getUserContext() == SecurityUtil.UserContext.STAFF_CONTEXT) || (SecurityUtil.getUserContext() == SecurityUtil.UserContext.DUAL_CONTEXT)); } private String getStudentIds(SLIPrincipal principal) { Collection<String> studentIds; Entity entity = principal.getEntity(); if (isStudent(entity)) { studentIds = Arrays.asList(entity.getEntityId()); } else if (isParent(entity)) { // use evil thread local to get the student list studentIds = principal.getOwnedStudentIds(); } else { studentIds = Collections.emptyList(); } return StringUtils.join(studentIds, ","); } private String getStudentAcademicRecordsIds(Entity principal) { return StringUtils.join(getStudentRelatedRecords(principal, EntityNames.STUDENT_ACADEMIC_RECORD), ","); } private String getStudentSectionAssocIds(SLIPrincipal principal) { Set<String> assocIds = new HashSet<String>(); for (Entity student : principal.getOwnedStudentEntities()) { assocIds.addAll(getStudentRelatedRecords(student, EntityNames.STUDENT_SECTION_ASSOCIATION)); } return StringUtils.join(assocIds, ","); } private String getSectionIds(SLIPrincipal principal) { Collection<String> sectionIds = new HashSet<String>(); for (Entity student : principal.getOwnedStudentEntities()) { sectionIds.addAll(sectionHelper.getStudentsSections(student)); } return StringUtils.join(sectionIds, ","); } private String getProgramIds(SLIPrincipal principal) { Set<String> programIds = new HashSet<String>(); for (Entity student : principal.getOwnedStudentEntities()) { programIds.addAll(getProgramIdsForStudent(student)); } return StringUtils.join(programIds, ","); } private Set<String> getProgramIdsForStudent(Entity student) { Set<String> programsIds = null; if (isStudent(student)) { programsIds = getSubdocIds(student, EntityNames.STUDENT_PROGRAM_ASSOCIATION, ParameterConstants.PROGRAM_ID); } else { throw new UriMutationException("Failed to fetch Program Id due to entity type not being student"); } return programsIds; } private String getCohortIds(SLIPrincipal principal) { Set<String> cohortsIds = new HashSet<String>(); for (Entity student : principal.getOwnedStudentEntities()) { cohortsIds.addAll(getCohortIdsForStudent(student)); } return StringUtils.join(cohortsIds, ","); } private Set<String> getCohortIdsForStudent(Entity student) { Set<String> cohortsIds = null; if (isStudent(student)) { cohortsIds = getSubdocIds(student, EntityNames.STUDENT_COHORT_ASSOCIATION, ParameterConstants.COHORT_ID); } else { throw new UriMutationException("Failed to fetch Cohort Id due to entity type not being student"); } return cohortsIds; } private Set<String> getSubdocIds(Entity superdoc, String subdocType, String subdocField) { Set<String> subdocFields = new HashSet<String>(); Map<String, List<Entity>> myEmbeddedData = superdoc.getEmbeddedData(); List<Entity> myAssociations = myEmbeddedData == null ? Collections.<Entity>emptyList() : myEmbeddedData.get(subdocType); if (myAssociations != null && !myAssociations.isEmpty()) { for (Entity association : myAssociations) { if (!dateHelper.isFieldExpired(association.getBody(), ParameterConstants.END_DATE)) { subdocFields.add((String) association.getBody().get(subdocField)); } } } return subdocFields; } private List<String> getStudentRelatedRecords(Entity principal, String entityType) { NeutralQuery query = new NeutralQuery(new NeutralCriteria(ParameterConstants.STUDENT_ID, NeutralCriteria.CRITERIA_IN, SecurityUtil.getSLIPrincipal().getOwnedStudentIds())); List<String> recordIds = new ArrayList<String>(); Iterable<String> allIds = repo.findAllIds(entityType, query); for (String recordId : allIds) { recordIds.add(recordId); } return recordIds; } private MutatedContainer formQueryBasedOnParameter(String path, String parameters, String parameter) { MutatedContainer mutated = new MutatedContainer(); String[] queryParameters = parameters.split("&"); for (int i = 0; i < queryParameters.length; i++) { String queryParameter = queryParameters[i]; String[] values = queryParameter.split("="); if (values.length == 2) { if (values[0].equals(parameter) && values[1] != null && !values[1].isEmpty()) { mutated.setPath(String.format(path, values[1])); mutated.setQueryParameters(removeQueryParameter(parameters, parameter)); break; } } } return mutated; } /** * Removes all occurrences of the specified queryParameterToRemove from the * specified parameters queryString. Almost redundant with * {@link removeQueryFromQueryParameters}, except that method removes only * the first occurrence. * * @param parameters The full queryString * @param queryParameterToRemove Name of a parameter to remove from the queryString */ private String removeQueryParameter(String parameters, String queryParameterToRemove) { if (parameters == null || parameters.isEmpty()) { return parameters; } StringBuilder builder = new StringBuilder(); String[] queryParameters = parameters.split("&"); for (String queryParameter : queryParameters) { if (!queryParameter.startsWith(queryParameterToRemove + "=")) { builder.append(queryParameter).append("&"); } } if (builder.length() > 0) { return builder.substring(0, builder.length() - 2); } return ""; } /** * Inject section helper (for unit testing). * * @param sectionHelper * resolver for tying entity to sections. */ protected void setSectionHelper(SectionHelper sectionHelper) { this.sectionHelper = sectionHelper; } /** * Inject education organization helper (for unit testing). * * @param edOrgHelper * resolver for tying entity to education organizations. */ protected void setEdOrgHelper(EdOrgHelper edOrgHelper) { this.edOrgHelper = edOrgHelper; } }