/**
 * @license
 * Copyright 2018 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

package foam.nanos.auth;

import foam.core.FObject;
import foam.core.X;
import foam.dao.DAO;
import foam.dao.ProxyDAO;
import foam.util.SafetyUtil;

/**
 * Custom authentication for groupDAO
 *
 * Features:
 *   - If creating a group, you can only give it permissions implied by those
 *     that you already have.
 *   - If updating a group, you can't modify the permissions array to have a
 *     permission that isn't implied by one you have.
 *   - If updating a group, you can't modify the parent of the group such that
 *     the group gains a permission that isn't implied by one you have.
 */
public class AuthenticatedGroupDAO extends ProxyDAO {
  public AuthenticatedGroupDAO(X x, DAO delegate) {
    super(x, delegate);
  }
  public void enforceNameSpaceForGroupName(X x, Group toCheck) {
    AuthService auth = (AuthService) x.get("auth");
    Group group = toCheck;
    User user = (User) x.get("user");
    if ( ! auth.check(x, "group.create." + user.getSpid() + group.getId() ) ) {
    throw new AuthorizationException("Permission Denied. Your group name must begin with your spid."); 
  }
}

  /**
   * Make sure that the user has all permissions in the given group.
   */
  public void checkUserHasAllPermissionsInGroup(X x, Group toCheck) {
    AuthService auth = (AuthService) x.get("auth");
    String groupId;
    Group group = toCheck;

    while ( true ) {
      Permission[] permissions = group.getPermissions();

      for ( Permission permission : permissions ) {
        String id = permission.getId();
        if ( ! auth.check(x, id) ) {
          throw new AuthorizationException("Permission Denied. You do not have the '" + id + "' permission.");
        }
      }

      groupId = group.getParent();

      if ( SafetyUtil.isEmpty(groupId) ) {
        break;
      }

      group = (Group) getDelegate().find_(x, groupId);

      if ( group == null ) {
        throw new RuntimeException("The '" + toCheck.getId() + "' group has a null ancestor named '" + groupId + "'.");
      }
    }
  }

  @Override
  public FObject put_(X x, FObject obj) {
    if ( getDelegate().find_(x, obj) == null ) {
      enforceNameSpaceForGroupName(x, (Group) obj);
    }
    checkUserHasAllPermissionsInGroup(x, (Group) obj);
    return super.put_(x, obj);
  }
}

@@ -855,380 +855,8 @@
 %0A  %7D
-%0A  public void enforceNameSpaceForGroupName(X x, Group toCheck) %7B%0A    AuthService auth = (AuthService) x.get(%22auth%22);%0A    Group group = toCheck;%0A    User user = (User) x.get(%22user%22);%0A    if ( ! auth.check(x, %22group.create.%22 + user.getSpid() + group.getId() ) ) %7B%0A    throw new AuthorizationException(%22Permission Denied. Your group name must begin with your spid.%22); %0A  %7D%0A%7D
 %0A%0A  
@@ -1427,26 +1427,29 @@
 %22);%0A        
+  
 %7D%0A
+ 
       %7D%0A%0A   
@@ -1834,104 +1834,446 @@
     
-if ( getDelegate().find_(x, obj) == null ) %7B%0A      enforceNameSpaceForGroupName(x, (Group) obj);
+// If a new group is being created, check that the user has permission to create the group%0A    if ( getDelegate().find_(x, obj) == null ) %7B%0A      AuthService auth = (AuthService) x.get(%22auth%22);%0A      User user = (User) x.get(%22user%22);%0A      if ( ! auth.check(x, %22group.create.%22 + ((Group) obj).getId() ) ) %7B%0A        throw new AuthorizationException(%22Permission Denied. You do not have requisite permission to create this group.%22); %0A      %7D
 %0A   

Check permissions when a new group is created