/* * Copyright 2012 SURFnet bv, The Netherlands * * 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 teams.control; import nl.surfnet.coin.stoker.Stoker; import nl.surfnet.coin.stoker.StokerEntry; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.SessionAttributes; import org.springframework.web.bind.support.SessionStatus; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.view.RedirectView; import teams.Application; import teams.domain.*; import teams.interceptor.LoginInterceptor; import teams.service.GrouperTeamService; import teams.service.TeamInviteService; import teams.service.TeamsDao; import teams.util.AuditLog; import teams.util.ControllerUtil; import teams.util.TokenUtil; import javax.servlet.http.HttpServletRequest; import java.io.UnsupportedEncodingException; import java.util.*; import static java.util.stream.Collectors.toList; import static teams.util.TokenUtil.checkTokens; import static teams.util.ViewUtil.escapeViewParameters; /** * {@link Controller} that handles the accept/decline of an Invitation */ @Controller @SessionAttributes({"invitation", TokenUtil.TOKENCHECK}) public class InvitationController { @Autowired private TeamInviteService teamInviteService; @Autowired private GrouperTeamService grouperTeamService; @Autowired private ControllerUtil controllerUtil; @Autowired(required = false) private TeamsDao teamsDao; @Autowired(required = false) private Stoker stoker; @Value("${grouperPowerUser}") private String grouperPowerUser; @Value("${group-name-context}") private String groupNameContext; @Autowired private Environment environment; /** * RequestMapping to show the accept invitation page. * * @param modelMap {@link ModelMap} * @param request {@link HttpServletRequest} * @return accept invitation page */ @RequestMapping(value = "/acceptInvitation.shtml") public String accept(ModelMap modelMap, HttpServletRequest request) throws UnsupportedEncodingException { Optional<Invitation> invitationO = getInvitationByRequest(request); if (redirectToInvitationErrorPage(invitationO, modelMap)) { return "invitationexception"; } Invitation invitation = invitationO.get(); String teamId = invitation.getTeamId(); if (!StringUtils.hasText(teamId)) { throw new RuntimeException("Invalid invitation"); } Team team = controllerUtil.getTeamById(teamId); modelMap.addAttribute("invitation", invitation); modelMap.addAttribute("team", team); modelMap.addAttribute("date", new Date(invitation.getTimestamp())); modelMap.addAttribute("groupzyEnabled", environment.acceptsProfiles(Application.GROUPZY_PROFILE_NAME)); if (environment.acceptsProfiles(Application.GROUPZY_PROFILE_NAME)) { Collection<TeamServiceProvider> serviceProviders = teamsDao.forTeam(groupNameContext + teamId); Collection<StokerEntry> eduGainServiceProviders = stoker.getEduGainServiceProviders( serviceProviders.stream().map(TeamServiceProvider::getSpEntityId).collect(toList())); modelMap.addAttribute("serviceProviders", eduGainServiceProviders); } return "acceptinvitation"; } private boolean redirectToInvitationErrorPage(Optional<Invitation> invitationO, ModelMap modelMap) { if (!invitationO.isPresent()) { modelMap.addAttribute("action", "missing"); return true; } Invitation invitation = invitationO.get(); if (invitation.isDeclined()) { modelMap.addAttribute("action", "declined"); return true; } if (invitation.isAccepted()) { modelMap.addAttribute("action", "accepted"); String teamId = invitation.getTeamId(); String teamUrl = escapeViewParameters("detailteam.shtml?team=%s", teamId); modelMap.addAttribute("teamUrl", teamUrl); return true; } return false; } /** * RequestMapping to accept an invitation. If everything is okay, it redirects * to your new team detail view. * * @param request {@link HttpServletRequest} * @return detail view of your new team * @throws UnsupportedEncodingException if the server does not support utf-8 */ @RequestMapping(value = "/doAcceptInvitation.shtml") public ModelAndView doAccept(ModelMap modelMap, HttpServletRequest request) { Person person = (Person) request.getSession().getAttribute(LoginInterceptor.PERSON_SESSION_KEY); Optional<Invitation> invitationO = getInvitationByRequest(request); if (redirectToInvitationErrorPage(invitationO, modelMap)) { return new ModelAndView("invitationexception", modelMap); } Invitation invitation = invitationO.get(); String teamId = invitation.getTeamId(); if (!StringUtils.hasText(teamId)) { throw new RuntimeException("Invalid invitation"); } Team team = controllerUtil.getTeamById(teamId); String memberId = person.getId(); grouperTeamService.addMember(team, person); Role intendedRole = invitation.getIntendedRole(); if (person.isGuest() && Role.Admin.equals(intendedRole)) { // cannot make a guest Admin invitation.setIntendedRole(Role.Manager); } intendedRole = invitation.getIntendedRole(); grouperTeamService.addMemberRole(team, memberId, intendedRole, grouperPowerUser); AuditLog.log("User {} accepted invitation for team {} with intended role {}", person.getId(), teamId, intendedRole); invitation.setAccepted(true); teamInviteService.saveOrUpdate(invitation); return new ModelAndView(new RedirectView(escapeViewParameters("detailteam.shtml?team=%s", teamId)), Collections.emptyMap()); } /** * RequestMapping to decline an invitation as receiver. * This URL is bypassed in {@link LoginInterceptor} * * @param modelMap {@link ModelMap} * @param request {@link HttpServletRequest} * @return view for decline result */ @RequestMapping(value = "/declineInvitation.shtml") public String decline(ModelMap modelMap, HttpServletRequest request) { String viewTemplate = "invitationdeclined"; Person person = (Person) request.getSession().getAttribute(LoginInterceptor.PERSON_SESSION_KEY); Optional<Invitation> invitationO = getInvitationByRequest(request); if (!invitationO.isPresent()) { // even if we can't find the invitation, we'll display success! return viewTemplate; } Invitation invitation = invitationO.get(); invitation.setDeclined(true); teamInviteService.saveOrUpdate(invitation); AuditLog.log("User {} declined invitation for team {} with intended role {}", person.getId(), invitation.getTeamId(), invitation.getIntendedRole()); return viewTemplate; } /** * RequestMapping to delete an invitation as admin * * @param request {@link javax.servlet.http.HttpServletRequest} * @return redirect to detailteam if everything is okay * @throws UnsupportedEncodingException in the rare condition utf-8 is not supported */ @RequestMapping(value = "/deleteInvitation.shtml") public RedirectView deleteInvitation(HttpServletRequest request, @ModelAttribute(TokenUtil.TOKENCHECK) String sessionToken, @RequestParam String token, @RequestParam String id, SessionStatus status, ModelMap modelMap) { checkTokens(sessionToken, token, status); Person person = (Person) request.getSession().getAttribute(LoginInterceptor.PERSON_SESSION_KEY); if (person == null) { status.setComplete(); return new RedirectView("landingpage.shtml"); } Invitation invitation = teamInviteService.findInvitationByInviteId(id).orElseThrow(IllegalArgumentException::new); String teamId = invitation.getTeamId(); Team team = controllerUtil.getTeamById(teamId); if (!controllerUtil.hasUserAdministrativePrivileges(person, team)) { throw new RuntimeException("Requester (" + person.getId() + ") is not member or does not have the correct " + "privileges to delete (a) member(s)"); } teamInviteService.delete(invitation); AuditLog.log( "User {} deleted invitation for email {} for team {} with intended role {}", person.getId(), invitation.getEmail(), invitation.getTeamId(), invitation.getIntendedRole()); status.setComplete(); modelMap.clear(); return new RedirectView(escapeViewParameters("detailteam.shtml?team=%s", teamId)); } @RequestMapping("/myinvitations.shtml") public String myInvitations(ModelMap modelMap, HttpServletRequest request) { Person person = (Person) request.getSession().getAttribute(LoginInterceptor.PERSON_SESSION_KEY); String email = person.getEmail(); if (!StringUtils.hasText(email)) { throw new IllegalArgumentException("Your profile does not contain an email address"); } List<Invitation> invitations = teamInviteService.findPendingInvitationsByEmail(email); modelMap.addAttribute("invitations", invitations); List<Team> invitedTeams = new ArrayList<>(); for (Invitation invitation : invitations) { Team team = controllerUtil.getTeamById(invitation.getTeamId()); if (team != null) { invitedTeams.add(team); } } modelMap.addAttribute("teams", invitedTeams); return "myinvitations"; } private Optional<Invitation> getInvitationByRequest(HttpServletRequest request) { String invitationId = request.getParameter("id"); if (!StringUtils.hasText(invitationId)) { throw new IllegalArgumentException("Missing parameter to identify the invitation"); } return teamInviteService.findInvitationByInviteId(invitationId); } }