/* * 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 com.google.common.base.Throwables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.util.StringUtils; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.support.SessionStatus; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.view.RedirectView; import teams.domain.*; import teams.interceptor.LoginInterceptor; import teams.service.GrouperTeamService; import teams.service.TeamInviteService; import teams.util.AuditLog; import teams.util.ControllerUtil; import teams.util.TokenUtil; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import java.io.IOException; import java.util.Date; import java.util.Locale; import java.util.Optional; import static org.springframework.web.bind.annotation.RequestMethod.GET; import static org.springframework.web.bind.annotation.RequestMethod.POST; import static teams.interceptor.LoginInterceptor.PERSON_SESSION_KEY; import static teams.util.TokenUtil.TOKENCHECK; import static teams.util.TokenUtil.checkTokens; import static teams.util.ViewUtil.escapeViewParameters; /** * {@link Controller} that handles the add member page of a logged in user. */ @Controller @SessionAttributes(TokenUtil.TOKENCHECK) public class AddMemberController { private static final Logger LOG = LoggerFactory.getLogger(AddMemberController.class); protected static final String INVITE_SEND_INVITE_SUBJECT = "invite.SendInviteSubject"; protected static final String ROLES_PARAM = "roles"; protected static final String INVITATION_FORM_PARAM = "invitationForm"; protected static final String RESEND_INVITATION_COMMAND_PARAM = "resendInvitationCommand"; @Autowired private TeamInviteService teamInviteService; @Autowired private MessageSource messageSource; @Autowired private ControllerUtil controllerUtil; /** * Shows form to invite others to your {@link Team} */ @RequestMapping(value = "/addmember.shtml", method = GET) public String addMembersToTeam(Model model, Locale locale, HttpServletRequest request) { Person person = (Person) request.getSession().getAttribute(LoginInterceptor.PERSON_SESSION_KEY); Team team = controllerUtil.getTeam(request); checkUserHasAdministrativePrivileges(person, team, Optional.empty()); InvitationForm form = new InvitationForm(); form.setTeamId(team.getId()); form.setLanguage(Language.find(locale).orElse(Language.English)); model.addAttribute(TOKENCHECK, TokenUtil.generateSessionToken()); model.addAttribute(INVITATION_FORM_PARAM, form); model.addAttribute(ROLES_PARAM, newMemberRoles(person, team)); return "addmember"; } private Role[] newMemberRoles(Person person, Team team) { if (controllerUtil.hasUserAdminPrivileges(person, team)) { return new Role[]{ Role.Admin, Role.Manager, Role.Member }; } else if (controllerUtil.hasUserAdministrativePrivileges(person, team)) { return new Role[]{ Role.Member }; } throw new RuntimeException(String.format("User %s has not enough privileges to invite others in team %s", person.getId(), team.getId())); } /** * In case someone clicks the cancel button */ @RequestMapping(value = "/doaddmember.shtml", method = POST, params = "cancelAddMember") public RedirectView cancelAddMembers(@ModelAttribute InvitationForm form, HttpServletRequest request, SessionStatus status) { status.setComplete(); return new RedirectView(escapeViewParameters("detailteam.shtml?team=%s", form.getTeamId()), false, true, false); } @RequestMapping(value = "/doaddmember.shtml", method = POST) public String doAddMembersToTeam(@ModelAttribute(TOKENCHECK) String sessionToken, @RequestParam String token, @ModelAttribute InvitationForm form, BindingResult result, HttpServletRequest request, SessionStatus status, Model model) throws IOException { Person person = (Person) request.getSession().getAttribute(PERSON_SESSION_KEY); checkTokens(sessionToken, token, status); String teamId = form.getTeamId(); Team team = controllerUtil.getTeamById(teamId); checkUserHasAdministrativePrivileges(person, team, Optional.of(status)); correctRoleIfNeeded(person, form, team); new InvitationFormValidator().validate(form, result); InternetAddress[] emails = null; try { emails = getAllEmailAddresses(form); } catch (AddressException e) { result.rejectValue("emails", "error.WrongFormattedEmailList"); } if (result.hasErrors()) { model.addAttribute(ROLES_PARAM, newMemberRoles(person, team)); return "addmember"; } doInviteMembers(team, person, emails, form); AuditLog.log("User {} sent invitations for team {}, with role {} to addresses: {}", person.getId(), teamId, form.getIntendedRole(), emails); status.setComplete(); return escapeViewParameters("redirect:detailteam.shtml?team=%s", teamId); } // if a non admin tries to add a role admin or manager -> make invitation for member private void correctRoleIfNeeded(Person person, InvitationForm form, Team team) { boolean isAdmin = controllerUtil.hasUserAdminPrivileges(person, team); if (!(isAdmin || Role.Member.equals(form.getIntendedRole()))) { form.setIntendedRole(Role.Member); } } @RequestMapping(value = "/resendInvitation.shtml", method = GET) public String resendInvitation(@RequestParam("id") String invitationId, Model model, HttpServletRequest request) { Person person = (Person) request.getSession().getAttribute(PERSON_SESSION_KEY); Invitation invitation = teamInviteService.findInvitationByInviteId(invitationId) .orElseThrow(() -> new IllegalArgumentException("Cannot find the invitation. Invitations expire after 14 days.")); ResendInvitationCommand command = new ResendInvitationCommand(invitation); invitation.getLatestInvitationMessage().ifPresent(msg -> command.setMessageText(msg.getMessage())); model.addAttribute(RESEND_INVITATION_COMMAND_PARAM, command); model.addAttribute(ROLES_PARAM, new Role[] {Role.Member, Role.Manager, Role.Admin}); return "resendinvitation"; } @RequestMapping(value = "/doResendInvitation.shtml", method = RequestMethod.POST) public String doResendInvitation(Model model, @Valid @ModelAttribute ResendInvitationCommand command, BindingResult result, @ModelAttribute(TOKENCHECK) String sessionToken, @RequestParam String token, HttpServletRequest request, SessionStatus status) { if (result.hasErrors()) { model.addAttribute(ROLES_PARAM, new Role[] {Role.Member, Role.Manager, Role.Admin}); return "resendinvitation"; } Person person = (Person) request.getSession().getAttribute(PERSON_SESSION_KEY); checkTokens(sessionToken, token, status); InvitationMessage invitationMessage = new InvitationMessage(command.getMessageText(), person.getId()); Invitation invitation = teamInviteService.findInvitationByInviteId(command.getInvitationId()) .orElseThrow(() -> new IllegalArgumentException("Cannot find the invitation. Invitations expire after 14 days.")); Team team = controllerUtil.getTeamById(invitation.getTeamId()); command.apply(invitation); invitation.addInvitationMessage(invitationMessage); invitation.setTimestamp(new Date().getTime()); teamInviteService.saveOrUpdate(invitation); checkUserHasAdministrativePrivileges(person, team, Optional.of(status)); String subject = messageSource.getMessage(INVITE_SEND_INVITE_SUBJECT, new Object[] {team.getName()}, invitation.getLanguage().locale()); controllerUtil.sendInvitationMail(team, invitation, subject, person); status.setComplete(); return escapeViewParameters("redirect:detailteam.shtml?team=%s", invitation.getTeamId()); } /** * Combines the input of the emails field and the csv file */ private InternetAddress[] getAllEmailAddresses(InvitationForm form) throws IOException, AddressException { StringBuilder sb = new StringBuilder(); String emailString = form.getEmails(); boolean appendEmails = StringUtils.hasText(emailString); if (form.hasCsvFile()) { sb.append(form.getCsvFileEmails()); if (appendEmails) { sb.append(','); } } if (appendEmails) { sb.append(emailString); } return InternetAddress.parse(sb.toString()); } private void doInviteMembers(Team team, Person inviter, InternetAddress[] emails, InvitationForm form) { String subject = messageSource.getMessage(INVITE_SEND_INVITE_SUBJECT, new Object[] {team.getName()}, form.getLanguage().locale()); for (InternetAddress email : emails) { String emailAddress = email.getAddress(); Invitation invitation = teamInviteService.findOpenInvitation(emailAddress, team) .orElse(new Invitation(emailAddress, team.getId())); if (invitation.isDeclined()) { continue; } InvitationMessage invitationMessage = new InvitationMessage(form.getMessage(), inviter.getId()); invitation.addInvitationMessage(invitationMessage); invitation.setTimestamp(new Date().getTime()); invitation.setIntendedRole(form.getIntendedRole()); invitation.setLanguage(form.getLanguage()); teamInviteService.saveOrUpdate(invitation); controllerUtil.sendInvitationMail(team, invitation, subject, inviter); AuditLog.log("Sent invitation and saved to database: team: {}, inviter: {}, email: {}, role: {}, hash: {}", team.getId(), inviter.getId(), emailAddress, form.getIntendedRole(), invitation.getInvitationHash()); } } private void checkUserHasAdministrativePrivileges(Person person, Team team, Optional<SessionStatus> status) { if (!controllerUtil.hasUserAdministrativePrivileges(person, team)) { if (status.isPresent()) { status.get().setComplete(); } throw new RuntimeException(String.format( "Requester (%s) is not member or does not have the correct privileges to add (a) member(s)", person.getId())); } } @ModelAttribute("languages") public Language[] languages() { Locale locale = LocaleContextHolder.getLocale(); if (locale.toString().equals("en")) { return Language.enLanguages(); } else { return Language.nlLanguages(); } } }