package controllers;
import com.typesafe.config.ConfigFactory;
import managers.*;
import models.*;
import models.enums.AccountRole;
import org.springframework.util.Assert;
import play.Configuration;
import play.api.i18n.Lang;
import play.data.Form;
import play.data.FormFactory;
import play.i18n.MessagesApi;
import play.mvc.Http;
import play.mvc.Http.Context;
import play.mvc.Result;
import play.mvc.Security;
import views.html.landingpage;
import javax.inject.Inject;
import java.util.Date;
/**
* This class provides several authorization methods for security reasons.
*/
public class Secured extends Security.Authenticator {
private final Configuration configuration;
private final FormFactory formFactory;
private final MessagesApi messagesApi;
@Inject
public Secured(Configuration configuration, FormFactory formFactory, MessagesApi messagesApi) {
this.configuration = configuration;
this.formFactory = formFactory;
this.messagesApi = messagesApi;
}
/**
* Returns the ID of the currently logged in user.
*
* @param ctx HTTP context
* @return ID or null
*/
@Override
public String getUsername(Context ctx) {
// see if user is logged in
if (ctx.session().get("id") == null)
return null;
// see if the session is expired
String previousTick = ctx.session().get("userTime");
if (previousTick != null && !previousTick.equals("")) {
long previousT = Long.valueOf(previousTick);
long currentT = new Date().getTime();
long timeout = Long.valueOf(configuration.getString("sessionTimeout")) * 1000 * 60;
long passedT = currentT - previousT;
if (passedT > timeout && !ctx.session().containsKey("rememberMe")) {
// session expired
ctx.session().clear();
play.mvc.Controller.flash("info", messagesApi.get(Lang.defaultLang(), "error.sessionExpired"));
return null;
}
}
// update time in session
String tickString = Long.toString(new Date().getTime());
ctx.session().put("userTime", tickString);
return ctx.session().get("id");
}
/**
* Returns Result instance to landing page (on un-authorization).
* @param ctx HTTP context
* @return Result instance
*/
@Override
@SuppressWarnings("unchecked")
public Result onUnauthorized(Context ctx) {
// cookie outdated? save originURL to prevent redirect to index page after login
ctx.session().put("originURL", ctx.request().path());
Form<Login> form = formFactory.form(Login.class).bindFromRequest();
if (Component.getFromContext(Component.ContextIdent.loginForm) != null) {
form = (Form<Login>) Component.getFromContext(Component.ContextIdent.loginForm);
}
return unauthorized(landingpage.render(form));
}
/**
* Redirect a call to his origin url or main page if he used a new tab.
* This can be used if objects cant be found (accounts, posts, groups, notification ...)
* @param request a user request
* @Result Result instance
*/
public static Result nullRedirect(Http.Request request) {
if (request.getHeader("referer") != null) {
return redirect(request.getHeader("referer"));
} else {
return redirect(routes.Application.index());
}
}
/**
* Returns true, if the currently logged in user is admin.
*
* @return True, if admin
*/
public static boolean isAdmin() {
Account current = Component.currentAccount();
return current.role == AccountRole.ADMIN;
}
/**
* Returns true, if the currently logged in user is the @param user.
*
* @return True, if currentUser.id is @param id
*/
public static boolean isMe(Account account) {
Account current = Component.currentAccount();
return current.equals(account);
}
/**
* Returns true, if the currently logged in user is unknown to the @param account
* Known people are: admin, myself, friends
*
* @param account Account
* @return True, if given currently logged in user to given account
*/
public static boolean isUnknown(Account account) {
if (!Secured.isFriend(account) && !Secured.isMe(account) && !Secured.isAdmin()) {
return true;
}
return false;
}
/**
* Returns true, if an account is member of a group.
*
* @param group Group
* @param account Account
* @return True, if account is member
*/
public static boolean isMemberOfGroup(Group group, Account account){
return GroupManager.isMember(group, account);
}
/**
* Returns true, if an account is owner of a group.
*
* @param group Group
* @param account Account
* @return True, if account is owner of group
*/
public static boolean isOwnerOfGroup(Group group, Account account) {
return group != null && group.owner.equals(account);
}
/**
* Returns true, if the currently logged in account is allowed to create a course.
*
* @return True, if account is allowed to create course
*/
public static boolean createCourse() {
Account current = Component.currentAccount();
return current.role == AccountRole.TUTOR || current.role == AccountRole.ADMIN;
}
/**
* Returns true, if the currently logged in account is allowed to view a specific group.
*
* @param group Group to view
* @return True, if currently logged in account is allowed to view group
*/
public static boolean viewGroup(Group group) {
Account current = Component.currentAccount();
if (group == null) {
return false;
}
if (Secured.isAdmin()) {
return true;
}
switch (group.groupType) {
case open:
return true;
case close:
if (Secured.isMemberOfGroup(group, current)) {
return true;
}
case course:
if (Secured.isMemberOfGroup(group, current)) {
return true;
}
default:
return false;
}
}
/**
* Returns true, if the currently logged in account is allowed to edit a specific group.
*
* @param group Group
* @return True, if logged in account is allowed to edit group
*/
public static boolean editGroup(Group group) {
Account current = Component.currentAccount();
return group != null && (Secured.isAdmin() || Secured.isOwnerOfGroup(group, current));
}
/**
* Returns true, if the currently logged in account is allowed to delete a specific group.
*
* @param group Group
* @return True, if logged in account is allowed to delete group
*/
public static boolean deleteGroup(Group group) {
Account current = Component.currentAccount();
return group != null && (Secured.isAdmin() || Secured.isOwnerOfGroup(group, current));
}
/**
* Returns true, if the currently logged in account is allowed to remove a specific account from a group.
*
* @param group Group
* @param account Account to remove
* @return True, if logged in account is allowed to remove account from group
*/
public static boolean removeGroupMember(Group group, Account account) {
Account current = Component.currentAccount();
return group != null
&& !Secured.isOwnerOfGroup(group, account)
&& (Secured.isAdmin() || Secured.isOwnerOfGroup(group, current) || current.equals(account));
}
/**
* Returns true, if currently logged in account is allowed to invite a member to a specific group.
*
* @param group Group to invite
* @return True, if logged in account is allowed to invite to group
*/
public static boolean inviteMember(Group group) {
Account current = Component.currentAccount();
return group != null && (Secured.isOwnerOfGroup(group, current) || Secured.isAdmin());
}
/**
* Returns true, if the currently logged in account is allowed to accept an invitation.
*
* @param groupAccount Group account
* @return True, if logged in account is allowed to accept an invitation
*/
public static boolean acceptInvitation(GroupAccount groupAccount) {
Account current = Component.currentAccount();
return groupAccount != null && (Secured.isAdmin() || groupAccount.account.equals(current));
}
/**
* Returns true, if the currently logged in account is allowed to view a specific post.
*
* @param post Post to view
* @return True, if logged in account is allowed to view post
*/
public static boolean viewPost(Post post) {
Account current = Component.currentAccount();
if (post == null) {
return false;
}
if (Secured.isAdmin()) {
return true;
}
if(post.belongsToAccount()) {
return post.account.equals(current) || Secured.isFriend(post.account);
}
if (post.belongsToGroup()) {
switch (post.group.groupType) {
case open:
return true;
case close:
if (Secured.isMemberOfGroup(post.group, current)) {
return true;
}
case course:
if (Secured.isMemberOfGroup(post.group, current)) {
return true;
}
default:
return false;
}
}
return false;
}
/**
* Returns true, if currently logged in account is allowed to delete a specific post.
*
* @param post Post
* @param account Account
* @return True, if logged in account is allowed to delete post
*/
public static boolean isAllowedToDeletePost(Post post, Account account) {
if (post == null) {
return false;
}
if (Secured.isAdmin()) {
return true;
}
if (Secured.isOwnerOfPost(post, account)) {
return true;
}
// is Post
if (post.belongsToGroup()) {
if (Secured.isOwnerOfGroup(post.group, account)) {
return true;
}
}
if (post.belongsToAccount()) {
if (post.account.equals(account)) {
return true;
}
}
// is Comment
return post.parent != null && Secured.isAllowedToDeletePost(post.parent, account);
}
/**
* Returns true, if currently logged in account is allowed to edit a specific post.
* ! This does not consider the time limit, see {@code isPostStillEditable}!
*
* @param post Post
* @param account Account
* @return true, if logged in account is allowed to edit post
*/
public static boolean isAllowedToEditPost(Post post, Account account) {
if (post == null) {
return false;
}
if (Secured.isAdmin()) {
return true;
}
if (Secured.isOwnerOfPost(post, account)) {
return true;
}
return false;
}
/**
* Returns true, if given account has bookmarked a specific post.
*
* @param account Account
* @param post Post
* @return true, if given account has bookmarked the given post
*/
public static boolean isPostBookmarkedByAccount(Account account, Post post) {
if (post == null) {
return false;
}
return PostBookmarkManager.isPostBookmarkedByAccount(account, post);
}
/**
* Returns true if the post is still editable by the given account.
* This includes the {@code isAllowedToEditPost} check
*
* @param post Post
* @param account Account
* @return true, if post still editable (or admin account)
*/
public static boolean isPostStillEditable(Post post, Account account) {
if(!isAllowedToEditPost(post, account)) {
return false;
}
if (post == null) {
return false;
}
if (Secured.isAdmin()) {
return true;
}
int limit = ConfigFactory.load().getInt("htwplus.post.editTimeLimit"); // the limit in minutes
if (new Date(post.createdAt.getTime() + 1000 * 60 * limit).compareTo(new Date(System.currentTimeMillis())) < 0) { // older than X minutes ?
return false;
}
return true;
}
/**
* Returns true if the post is still editable by the given account.
* This includes the {@code isAllowedToEditPost} check
*
* @param post Post
* @param account Account
* @return true, if post still editable (or admin account)
*/
public static boolean isPostStillEditableWithTolerance(Post post, Account account) {
if(!isAllowedToEditPost(post, account)) {
return false;
}
if (post == null) {
return false;
}
if (Secured.isAdmin()) {
return true;
}
int limit = ConfigFactory.load().getInt("htwplus.post.editTimeLimit") + ConfigFactory.load().getInt("htwplus.post.editTimeLimitTolerance"); // the limit in minutes
if (new Date(post.createdAt.getTime() + 1000 * 60 * limit).compareTo(new Date(System.currentTimeMillis())) < 0) { // older than X minutes ?
return false;
}
return true;
}
/**
* Returns true, if an account is the owner of a post.
*
* @param post Post
* @param account Account
* @return True, if account is owner of post
*/
public static boolean isOwnerOfPost(Post post, Account account) {
return post != null && post.owner.equals(account);
}
/**
* Returns true, if the currently logged in account is allowed to view comments on a specific post.
*
* @param post Post to view comments
* @return True, if logged in account is allowed to view comments of post
*/
public static boolean viewComments(Post post) {
Account current = Component.currentAccount();
if (post == null) {
return false;
}
if (Secured.isAdmin()) {
return true;
}
if (post.belongsToAccount()) {
return post.account.equals(current) || Secured.isFriend(post.account);
}
if (post.belongsToGroup()) {
switch (post.group.groupType) {
case open:
return true;
case close:
if (Secured.isMemberOfGroup(post.group, current)) {
return true;
}
case course:
if (Secured.isMemberOfGroup(post.group, current)) {
return true;
}
default:
return false;
}
}
return false;
}
/**
* Returns true, if the currently logged in account is allowed to add a comment to a specific post.
*
* @param post Post to comment
* @return True, if logged in account is allowed to add comment
*/
public static boolean addComment(Post post) {
Account current = Component.currentAccount();
if (post == null) {
return false;
}
if (Secured.isAdmin()) {
return true;
}
if (post.belongsToGroup()) {
return Secured.isMemberOfGroup(post.group, current);
}
return post.belongsToAccount() && (post.account.equals(current) || Secured.isFriend(post.account));
}
/**
* Returns true, if the currently logged in account has a friendship with a specific account.
*
* @param account Account to check friendship
* @return True, if logged in account has friendship to account
*/
public static boolean isFriend(Account account) {
return FriendshipManager.alreadyFriendly2(Component.currentAccount(), account);
}
/**
* Returns true, if the currently logged in account is allowed to edit a specific account.
*
* @param account Account to edit
* @return True, if logged in account is allowed to edit account
*/
public static boolean editAccount(Account account) {
if(Secured.isAdmin())
return true;
return Component.currentAccount().equals(account);
}
/**
* Returns true, if the currently logged in account is allowed to delete a specific account.
*
* @param account the account to be deleted
* @return True, if logged in account is allowed to delete the specified account
*/
public static boolean deleteAccount(Account account) {
if(Secured.isAdmin())
return true;
Account current = Component.currentAccount();
return current.equals(account);
}
/**
* Returns true, if the currently logged in account is allowed to upload media into a specific group.
*
* @param group Group to upload media to
* @return True, if logged in account is allowed to upload media to group
*/
public static boolean uploadMedia(Group group) {
Account current = Component.currentAccount();
if (group == null) {
return false;
}
if (Secured.isAdmin()) {
return true;
}
switch (group.groupType) {
case open:
if (Secured.isMemberOfGroup(group, current)) {
return true;
}
case close:
if (Secured.isMemberOfGroup(group, current)) {
return true;
}
case course:
if (Secured.isOwnerOfGroup(group, current)) {
return true;
}
default:
return false;
}
}
/**
* Returns true, if the currently logged in account is allowed to view a specific media.
*
* @param media Media to view
* @return True, if logged in account is allowed to see the medias folder
*/
public static boolean viewMedia(Media media) {
Assert.notNull(media);
return Secured.viewFolder(media.findRoot());
}
/**
* Returns true, if the currently logged in account is allowed to delete media from its associated group.
*
* @param media Media to be deleted
* @return True, if logged in account is allowed to delete media
*/
public static boolean deleteMedia(Media media) {
Assert.notNull(media);
Account current = Component.currentAccount();
Group group = media.findGroup();
return Secured.isAdmin() || Secured.isOwnerOfGroup(group, current) || media.owner.equals(current);
}
/**
* Returns true, if the currently logged in account is allowed to delete a folder from its associated group.
*
* @param folder Folder to be deleted
* @return True, if logged in account is allowed to delete media
*/
public static boolean deleteFolder(Folder folder) {
Account current = Component.currentAccount();
return Secured.isAdmin() || folder.owner.equals(current);
}
/**
* Returns true, if the current user has access to a notification.
*
* @param notification Notification to be checked
* @return True, if user has access
*/
public static boolean hasAccessToNotification(Notification notification) {
return notification.recipient.equals(Component.currentAccount());
}
public static boolean isNotNull(Object object) {
return object != null;
}
/**
* Checks if the currently logged in user is allowed to see the given folder.
* @param folder folder which should be checked
* @return true if the currently logged in account is allowed to see the folder.
*/
public static boolean viewFolder(Folder folder) {
if (folder == null) {
return false;
}
if (isAdmin()) {
return true;
}
// if its a group folder, check group rights
Folder rootFolder = FolderManager.findRoot(folder);
if (rootFolder.group != null) {
return viewGroup(rootFolder.group);
}
// if its an account folder, everybody can see it
// todo: necessary view management
if (folder.account != null) {
return true;
}
return false;
}
}