/**
   * retrieve if delegatable
   *
   * @param action
   * @param attributeDefName
   * @return if delegatable or grant
   */
  private AttributeAssignDelegatable retrieveDelegatable(
      String action, AttributeDefName attributeDefName) {

    if (AttributeDefType.perm != attributeDefName.getAttributeDef().getAttributeDefType()) {
      throw new RuntimeException(
          "Can only delegate a permission: "
              + attributeDefName.getAttributeDef().getAttributeDefType());
    }

    Set<PermissionEntry> permissionEntries =
        GrouperDAOFactory.getFactory()
            .getPermissionEntry()
            .findByMemberIdAndAttributeDefNameId(
                GrouperSession.staticGrouperSession().getMemberUuid(), attributeDefName.getId());

    boolean isGrant = false;
    boolean isDelegate = false;
    action = StringUtils.defaultString(action, AttributeDef.ACTION_DEFAULT);
    for (PermissionEntry permissionEntry : permissionEntries) {
      if (permissionEntry.isEnabled() && StringUtils.equals(action, permissionEntry.getAction())) {
        AttributeAssignDelegatable localDelegatable =
            permissionEntry.getAttributeAssignDelegatable();
        isGrant = isGrant || (localDelegatable == AttributeAssignDelegatable.GRANT);
        isDelegate = isDelegate || localDelegatable.delegatable();
      }
    }
    if (isGrant) {
      return AttributeAssignDelegatable.GRANT;
    }
    if (isDelegate) {
      return AttributeAssignDelegatable.TRUE;
    }
    return AttributeAssignDelegatable.FALSE;
  }
  /**
   * add a multi assignable attribute
   *
   * @param action is the action on the assignment (null means default action)
   * @param attributeDefName
   * @param checkSecurity
   * @param uuid uuid of the assignment
   * @return the result including if added or already there
   */
  public AttributeAssignResult internal_addAttributeHelper(
      String action, AttributeDefName attributeDefName, boolean checkSecurity, String uuid) {

    AttributeDef attributeDef = attributeDefName.getAttributeDef();
    if (!attributeDef.isMultiAssignable()) {
      throw new RuntimeException(
          "This attribute must be multi-assignable to call this method, use the non multi-assign method: "
              + attributeDefName.getName());
    }

    if (checkSecurity) {
      this.assertCanUpdateAttributeDefName(attributeDefName);
    }

    this.assertScopeOk(attributeDef);

    AttributeAssign attributeAssign = newAttributeAssign(action, attributeDefName, uuid);

    if (StringUtils.isBlank(attributeAssign.getAttributeAssignActionId())) {
      attributeAssign.setAttributeAssignActionId(
          attributeDef.getAttributeDefActionDelegate().allowedAction(action, true).getId());
    }

    attributeAssign.saveOrUpdate(checkSecurity);

    return new AttributeAssignResult(true, attributeAssign);
  }
  /**
   * @param action is the action on the assignment (null means default action)
   * @param attributeDefName
   * @param checkSecurity
   * @return if removed or already not assigned
   */
  private AttributeAssignResult removeAttributeHelper(
      String action, AttributeDefName attributeDefName, boolean checkSecurity) {
    if (checkSecurity) {
      this.assertCanUpdateAttributeDefName(attributeDefName);
    }

    AttributeAssignResult attributeAssignResult = new AttributeAssignResult();
    attributeAssignResult.setChanged(false);
    // see if it exists
    if (!this.hasAttributeHelper(action, attributeDefName, false)) {
      return attributeAssignResult;
    }
    action = StringUtils.defaultIfEmpty(action, AttributeDef.ACTION_DEFAULT);
    Set<AttributeAssign> attributeAssigns =
        retrieveAttributeAssignsByOwnerAndAttributeDefNameId(attributeDefName.getId());
    Set<AttributeAssign> attributeAssignsToReturn = new LinkedHashSet<AttributeAssign>();
    for (AttributeAssign attributeAssign : attributeAssigns) {
      String currentAttributeAction = attributeAssign.getAttributeAssignAction().getName();
      if (StringUtils.equals(action, currentAttributeAction)) {
        attributeAssignResult.setChanged(true);
        attributeAssignsToReturn.add(attributeAssign);
        attributeAssign.delete();
      }
    }
    attributeAssignResult.setAttributeAssigns(attributeAssignsToReturn);
    return attributeAssignResult;
  }
 /**
  * retrieve an assignment (should be single assign)
  *
  * @param action
  * @param attributeDefName
  * @param checkSecurity
  * @param exceptionIfNull
  * @return the assignment
  */
 public AttributeAssign retrieveAssignment(
     String action,
     AttributeDefName attributeDefName,
     boolean checkSecurity,
     boolean exceptionIfNull) {
   if (checkSecurity) {
     this.assertCanReadAttributeDefName(attributeDefName);
   }
   Set<AttributeAssign> attributeAssigns =
       retrieveAttributeAssignsByOwnerAndAttributeDefNameId(attributeDefName.getId());
   return retrieveAssignmentHelper(action, attributeDefName, exceptionIfNull, attributeAssigns);
 }
  /**
   * @param action on the assignment
   * @param attributeDefName
   * @param checkSecurity
   * @return true if has attribute, false if not
   */
  boolean hasAttributeHelper(
      String action, AttributeDefName attributeDefName, boolean checkSecurity) {
    if (checkSecurity) {
      this.assertCanReadAttributeDefName(attributeDefName);
    }
    Set<AttributeAssign> attributeAssigns =
        retrieveAttributeAssignsByOwnerAndAttributeDefNameId(attributeDefName.getId());

    action = StringUtils.defaultIfEmpty(action, AttributeDef.ACTION_DEFAULT);
    for (AttributeAssign attributeAssign : attributeAssigns) {
      String currentAttributeAction = attributeAssign.getAttributeAssignAction().getName();
      if (StringUtils.equals(action, currentAttributeAction)) {
        return true;
      }
    }

    return false;
  }
  /**
   * @param action is the action on the assignment (null means default action)
   * @param attributeDefName
   * @param assign true to assign, false to unassign
   * @param attributeAssignDelegateOptions if there are more options, null if not
   * @return the result including if added or already there
   */
  public AttributeAssignResult delegateAttribute(
      final String action,
      final AttributeDefName attributeDefName,
      final boolean assign,
      final AttributeAssignDelegateOptions attributeAssignDelegateOptions) {

    AttributeDef attributeDef = attributeDefName.getAttributeDef();

    if (attributeDef.isMultiAssignable()) {
      throw new RuntimeException(
          "This attribute must not be multi-assignable to call "
              + "this method, use the multi-assign methods: "
              + attributeDefName.getName());
    }

    this.assertCanDelegateAttributeDefName(action, attributeDefName);

    if (attributeAssignDelegateOptions != null
        && attributeAssignDelegateOptions.isAssignAttributeAssignDelegatable()) {
      if (attributeAssignDelegateOptions.getAttributeAssignDelegatable()
              == AttributeAssignDelegatable.GRANT
          || attributeAssignDelegateOptions.getAttributeAssignDelegatable()
              == AttributeAssignDelegatable.TRUE) {
        this.assertCanGrantAttributeDefName(action, attributeDefName);
      }
    }

    AttributeAssignResult attributeAssignResult =
        (AttributeAssignResult)
            GrouperSession.callbackGrouperSession(
                GrouperSession.staticGrouperSession().internal_getRootSession(),
                new GrouperSessionHandler() {

                  public Object callback(GrouperSession grouperSession)
                      throws GrouperSessionException {

                    if (assign) {

                      // do the same thing that an assign would do
                      // do this as root since the user who can delegate might not be able to
                      // assign...
                      AttributeAssignResult attributeAssignResult2 =
                          AttributeAssignBaseDelegate.this.internal_assignAttributeHelper(
                              action, attributeDefName, false, null, null);

                      if (attributeAssignDelegateOptions != null) {

                        AttributeAssign attributeAssign =
                            attributeAssignResult2.getAttributeAssign();
                        if (attributeAssignDelegateOptions.isAssignAttributeAssignDelegatable()) {
                          attributeAssign.setAttributeAssignDelegatable(
                              attributeAssignDelegateOptions.getAttributeAssignDelegatable());
                        }
                        if (attributeAssignDelegateOptions.isAssignDisabledDate()) {
                          attributeAssign.setDisabledTime(
                              attributeAssignDelegateOptions.getDisabledTime());
                        }
                        if (attributeAssignDelegateOptions.isAssignEnabledDate()) {
                          attributeAssign.setDisabledTime(
                              attributeAssignDelegateOptions.getEnabledTime());
                        }
                        attributeAssign.saveOrUpdate(true);
                      }
                      return attributeAssignResult2;
                    }

                    return AttributeAssignBaseDelegate.this.removeAttributeHelper(
                        action, attributeDefName, false);
                  }
                });

    return attributeAssignResult;
  }
 /**
  * make sure the user can read the attribute (including looking at object if necessary)
  *
  * @param attributeDefName
  */
 public void assertCanReadAttributeDefName(AttributeDefName attributeDefName) {
   AttributeDef attributeDef = attributeDefName.getAttributeDef();
   assertCanReadAttributeDef(attributeDef);
 }
  /**
   * @param action is the action on the assignment (null means default action)
   * @param attributeDefName
   * @param checkSecurity
   * @param uuid uuid of the assignment
   * @param permissionAllowed if permission this is the allowed flag
   * @return the result including if added or already there
   */
  public AttributeAssignResult internal_assignAttributeHelper(
      String action,
      AttributeDefName attributeDefName,
      boolean checkSecurity,
      String uuid,
      PermissionAllowed permissionAllowed) {

    if (permissionAllowed == null) {
      permissionAllowed = PermissionAllowed.ALLOWED;
    }

    AttributeDef attributeDef = attributeDefName.getAttributeDef();

    if (checkSecurity) {
      this.assertCanUpdateAttributeDefName(attributeDefName);
    }

    boolean isPermission =
        AttributeDefType.perm.equals(attributeDefName.getAttributeDef().getAttributeDefType());
    if (permissionAllowed != null && permissionAllowed.isDisallowed() && !isPermission) {
      throw new RuntimeException(
          "Can only assign a permissionAllowed with attributeDefName as perm (permission) type: "
              + attributeDefName.getName()
              + ", "
              + attributeDefName.getAttributeDef().getAttributeDefType());
    }

    AttributeAssign attributeAssign = retrieveAssignment(action, attributeDefName, false, false);

    if (attributeAssign != null) {
      if (permissionAllowed != null
          && permissionAllowed.isDisallowed() != attributeAssign.isDisallowed()) {
        throw new RuntimeException(
            "Assigning disallowed: "
                + permissionAllowed.isDisallowed()
                + ", but the existing assignment "
                + attributeAssign.getId()
                + " has: "
                + attributeAssign.isDisallowed()
                + ", you need to delete assignment and reassign.");
      }
      return new AttributeAssignResult(false, attributeAssign);
    }

    attributeAssign = newAttributeAssign(action, attributeDefName, uuid);

    attributeAssign.setDisallowed(
        permissionAllowed == null ? false : permissionAllowed.isDisallowed());

    if (StringUtils.isBlank(attributeAssign.getAttributeAssignActionId())) {
      attributeAssign.setAttributeAssignActionId(
          attributeDef.getAttributeDefActionDelegate().allowedAction(action, true).getId());
    }

    this.assertScopeOk(attributeDef);

    attributeAssign.internalSetAttributeDef(attributeDef);
    attributeAssign.internalSetAttributeDefName(attributeDefName);
    attributeAssign.saveOrUpdate(checkSecurity);
    return new AttributeAssignResult(true, attributeAssign);
  }
  /**
   * @param attributeDefName
   * @return the assignments for a def name
   */
  public Set<AttributeAssign> retrieveAssignments(AttributeDefName attributeDefName) {
    this.assertCanReadAttributeDefName(attributeDefName);

    return retrieveAttributeAssignsByOwnerAndAttributeDefNameId(attributeDefName.getId());
  }