Example #1
0
def get_email_from_template(recipient_list, subject, template, settings, context):
    """Construct a message object from a template

    This creates the full MIME object that can be used to send an email with mixed HTML
    and text parts.

    FIXME(herb): we depend on the FE settings object right now. Since we're only
    getting called from the FE that's fine for now but we should clean this up.

    Args:
        recipient_list (list(str)): Email addresses that will receive this mail.
        subject (str): Subject of the email.
        template (str): Name of the template to use.
        settings (Settings): grouper.settings.Settings object grouper is run with
        context (dict(str: str)): Context for the template library.

    Returns:
        MIMEMultipart: Constructed object for the email message.
    """
    template_env = get_template_env()
    sender = settings["from_addr"]

    context["url"] = settings["url"]

    text_template = template_env.get_template("email/{}_text.tmpl".format(template)).render(
        **context
    )
    html_template = template_env.get_template("email/{}_html.tmpl".format(template)).render(
        **context
    )

    text = MIMEText(text_template, "plain", "utf-8")
    html = MIMEText(html_template, "html", "utf-8")

    msg = MIMEMultipart("alternative")
    msg["Subject"] = subject
    msg["From"] = sender
    msg["To"] = ", ".join(recipient_list)
    msg.attach(text)
    msg.attach(html)

    if "references_header" in context:
        msg["References"] = msg["In-Reply-To"] = context["references_header"]

    return msg
Example #2
0
def get_email_from_template(recipient_list, subject, template, settings, context):
    """Construct a message object from a template

    This creates the full MIME object that can be used to send an email with mixed HTML
    and text parts.

    FIXME(herb): we depend on the FE settings object right now. Since we're only
    getting called from the FE that's fine for now but we should clean this up.

    Args:
        recipient_list (list(str)): Email addresses that will receive this mail.
        subject (str): Subject of the email.
        template (str): Name of the template to use.
        settings (Settings): grouper.settings.Settings object grouper is run with
        context (dict(str: str)): Context for the template library.

    Returns:
        MIMEMultipart: Constructed object for the email message.
    """
    template_env = get_template_env()
    sender = settings["from_addr"]

    context["url"] = settings["url"]

    text_template = template_env.get_template(
        "email/{}_text.tmpl".format(template)
    ).render(**context)
    html_template = template_env.get_template(
        "email/{}_html.tmpl".format(template)
    ).render(**context)

    text = MIMEText(text_template, "plain", "utf-8")
    html = MIMEText(html_template, "html", "utf-8")

    msg = MIMEMultipart("alternative")
    msg["Subject"] = subject
    msg["From"] = sender
    msg["To"] = ", ".join(recipient_list)
    msg.attach(text)
    msg.attach(html)

    if "references_header" in context:
        msg["References"] = msg["In-Reply-To"] = context["references_header"]

    return msg
Example #3
0
def create_fe_application(settings,
                          usecase_factory,
                          deployment_name,
                          xsrf_cookies=True,
                          session=None):
    # type: (Settings, UseCaseFactory, str, bool, Callable[[], Session]) -> GrouperApplication
    tornado_settings = {
        "debug": settings.debug,
        "static_path": os.path.join(os.path.dirname(grouper.fe.__file__),
                                    "static"),
        "xsrf_cookies": xsrf_cookies,
    }
    handler_settings = {
        "session": session if session else Session,
        "template_env": get_template_env(deployment_name=deployment_name),
        "usecase_factory": usecase_factory,
    }
    handlers = [(route, handler_class, handler_settings)
                for (route, handler_class) in HANDLERS]
    return GrouperApplication(handlers, **tornado_settings)
Example #4
0
def get_application(settings, sentry_client, deployment_name):
    # type: (Settings, SentryProxy, str) -> Application
    tornado_settings = {
        "static_path": os.path.join(os.path.dirname(grouper.fe.__file__),
                                    "static"),
        "debug": settings.debug,
        "xsrf_cookies": True,
    }

    my_settings = {
        "db_session": Session,
        "template_env": get_template_env(deployment_name=deployment_name),
    }

    application = Application(HANDLERS,
                              my_settings=my_settings,
                              sentry_client=sentry_client,
                              **tornado_settings)

    return application
Example #5
0
def update_request(session, request, user, new_status, comment):
    """Update a request.

    Args:
        session(sqlalchemy.orm.session.Session): database session
        request(models.PermissionRequest): request to update
        user(models.User): user making update
        new_status(models.base.constants.REQUEST_STATUS_CHOICES): new status
        comment(str): comment to include with status change

    Raises:
        grouper.audit.UserNotAuditor in case we're trying to add an audited
            permission to a group without auditors
    """
    if request.status == new_status:
        # nothing to do
        return

    # make sure the grant can happen
    if new_status == "actioned":
        if request.permission.audited:
            # will raise UserNotAuditor if no auditors are owners of the group
            assert_controllers_are_auditors(request.group)

    # all rows we add have the same timestamp
    now = datetime.utcnow()

    # new status change row
    permission_status_change = PermissionRequestStatusChange(
            request=request,
            user_id=user.id,
            from_status=request.status,
            to_status=new_status,
            change_at=now,
            ).add(session)
    session.flush()

    # new comment
    Comment(
            obj_type=OBJ_TYPES_IDX.index("PermissionRequestStatusChange"),
            obj_pk=permission_status_change.id,
            user_id=user.id,
            comment=comment,
            created_on=now,
            ).add(session)

    # update permissionRequest status
    request.status = new_status
    session.commit()

    if new_status == "actioned":
        # actually grant permission
        try:
            grant_permission(session, request.group.id, request.permission.id, request.argument)
        except IntegrityError:
            session.rollback()

    # audit log
    AuditLog.log(
        session, user.id, "update_perm_request",
        "updated permission request to status: {}".format(new_status),
        on_group_id=request.group_id, on_user_id=request.requester_id,
        on_permission_id=request.permission.id,
    )

    session.commit()

    # send notification

    subj_template = 'email/pending_permission_request_subj.tmpl'
    subject = "Re: " + get_template_env().get_template(subj_template).render(
        permission=request.permission.name, group=request.group.name
    )

    if new_status == "actioned":
        email_template = "permission_request_actioned"
    else:
        email_template = "permission_request_cancelled"

    email_context = {
            'group_name': request.group.name,
            'action_taken_by': user.name,
            'reason': comment,
            'permission_name': request.permission.name,
            'argument': request.argument,
            }

    send_email(session, [request.requester.name], subject, email_template,
            settings, email_context)
Example #6
0
def create_request(session, user, group, permission, argument, reason):
    """
    Creates an permission request and sends notification to the responsible approvers.

    Args:
        session(sqlalchemy.orm.session.Session): database session
        user(models.User): user requesting permission
        group(models.Group): group requested permission would be applied to
        permission(models.Permission): permission in question to request
        argument(str): argument for the given permission
        reason(str): reason the permission should be granted

    Raises:
        RequestAlreadyExists if trying to create a request that is already pending
        NoOwnersAvailable if no owner is available for the requested perm + arg.
    """
    # check if group already has perm + arg pair
    for _, existing_perm_name, _, existing_perm_argument, _ in group.my_permissions():
        if permission.name == existing_perm_name and argument == existing_perm_argument:
            raise RequestAlreadyGranted()

    # check if request already pending for this perm + arg pair
    existing_count = session.query(PermissionRequest).filter(
            PermissionRequest.group_id == group.id,
            PermissionRequest.permission_id == permission.id,
            PermissionRequest.argument == argument,
            PermissionRequest.status == "pending",
            ).count()

    if existing_count > 0:
        raise RequestAlreadyExists()

    # determine owner(s)
    owners_by_arg_by_perm = get_owners_by_grantable_permission(session, separate_global=True)
    owner_arg_list = get_owner_arg_list(
        session,
        permission,
        argument,
        owners_by_arg_by_perm=owners_by_arg_by_perm,
    )

    if not owner_arg_list:
        raise NoOwnersAvailable()

    pending_status = "pending"
    now = datetime.utcnow()

    # multiple steps to create the request
    request = PermissionRequest(
            requester_id=user.id,
            group_id=group.id,
            permission_id=permission.id,
            argument=argument,
            status=pending_status,
            requested_at=now,
            ).add(session)
    session.flush()

    request_status_change = PermissionRequestStatusChange(
            request=request,
            user=user,
            to_status=pending_status,
            change_at=now,
            ).add(session)
    session.flush()

    Comment(
            obj_type=OBJ_TYPES_IDX.index("PermissionRequestStatusChange"),
            obj_pk=request_status_change.id,
            user_id=user.id,
            comment=reason,
            created_on=now,
            ).add(session)

    # send notification
    email_context = {
            "user_name": user.name,
            "group_name": group.name,
            "permission_name": permission.name,
            "argument": argument,
            "reason": reason,
            "request_id": request.id,
            "references_header": request.reference_id,
            }

    # TODO: would be nicer if it told you which group you're an approver of
    # that's causing this notification

    mail_to = []
    global_owners = owners_by_arg_by_perm[GLOBAL_OWNERS]["*"]
    non_wildcard_owners = filter(lambda grant: grant[1] != '*', owner_arg_list)
    non_global_owners = filter(lambda grant: grant[0] not in global_owners, owner_arg_list)
    if any(non_wildcard_owners):
        # non-wildcard owners should get all the notifications
        mailto_owner_arg_list = non_wildcard_owners
    elif any(non_global_owners):
        mailto_owner_arg_list = non_global_owners
    else:
        # only the wildcards so they get the notifications
        mailto_owner_arg_list = owner_arg_list

    for owner, arg in mailto_owner_arg_list:
        mail_to += [u for t, u in owner.my_members() if t == 'User']

    subj = get_template_env().get_template('email/pending_permission_request_subj.tmpl').render(
        permission=permission.name, group=group.name
    )
    send_email(session, set(mail_to), subj,
            "pending_permission_request", settings, email_context)

    return request
Example #7
0
def update_request(session, request, user, new_status, comment):
    """Update a request.

    Args:
        session(sqlalchemy.orm.session.Session): database session
        request(models.PermissionRequest): request to update
        user(models.User): user making update
        new_status(models.base.constants.REQUEST_STATUS_CHOICES): new status
        comment(str): comment to include with status change

    Raises:
        grouper.audit.UserNotAuditor in case we're trying to add an audited
            permission to a group without auditors
    """
    if request.status == new_status:
        # nothing to do
        return

    # make sure the grant can happen
    if new_status == "actioned":
        if request.permission.audited:
            # will raise UserNotAuditor if no auditors are owners of the group
            assert_controllers_are_auditors(request.group)

    # all rows we add have the same timestamp
    now = datetime.utcnow()

    # new status change row
    permission_status_change = PermissionRequestStatusChange(
            request=request,
            user_id=user.id,
            from_status=request.status,
            to_status=new_status,
            change_at=now,
            ).add(session)
    session.flush()

    # new comment
    Comment(
            obj_type=OBJ_TYPES_IDX.index("PermissionRequestStatusChange"),
            obj_pk=permission_status_change.id,
            user_id=user.id,
            comment=comment,
            created_on=now,
            ).add(session)

    # update permissionRequest status
    request.status = new_status
    session.commit()

    if new_status == "actioned":
        # actually grant permission
        try:
            grant_permission(session, request.group.id, request.permission.id, request.argument)
        except IntegrityError:
            session.rollback()

    # audit log
    AuditLog.log(
        session, user.id, "update_perm_request",
        "updated permission request to status: {}".format(new_status),
        on_group_id=request.group_id, on_user_id=request.requester_id,
        on_permission_id=request.permission.id,
    )

    session.commit()

    # send notification

    subj_template = 'email/pending_permission_request_subj.tmpl'
    subject = "Re: " + get_template_env().get_template(subj_template).render(
        permission=request.permission.name, group=request.group.name
    )

    if new_status == "actioned":
        email_template = "permission_request_actioned"
    else:
        email_template = "permission_request_cancelled"

    email_context = {
            'group_name': request.group.name,
            'action_taken_by': user.name,
            'reason': comment,
            'permission_name': request.permission.name,
            'argument': request.argument,
            }

    send_email(session, [request.requester.name], subject, email_template,
            settings, email_context)
Example #8
0
def create_request(session, user, group, permission, argument, reason):
    """
    Creates an permission request and sends notification to the responsible approvers.

    Args:
        session(sqlalchemy.orm.session.Session): database session
        user(models.User): user requesting permission
        group(models.Group): group requested permission would be applied to
        permission(models.Permission): permission in question to request
        argument(str): argument for the given permission
        reason(str): reason the permission should be granted

    Raises:
        RequestAlreadyExists if trying to create a request that is already pending
        NoOwnersAvailable if no owner is available for the requested perm + arg.
        grouper.audit.UserNotAuditor if the group has owners that are not auditors
    """
    # check if group already has perm + arg pair
    for _, existing_perm_name, _, existing_perm_argument, _ in group.my_permissions():
        if permission.name == existing_perm_name and argument == existing_perm_argument:
            raise RequestAlreadyGranted()

    # check if request already pending for this perm + arg pair
    existing_count = session.query(PermissionRequest).filter(
            PermissionRequest.group_id == group.id,
            PermissionRequest.permission_id == permission.id,
            PermissionRequest.argument == argument,
            PermissionRequest.status == "pending",
            ).count()

    if existing_count > 0:
        raise RequestAlreadyExists()

    # determine owner(s)
    owners_by_arg_by_perm = get_owners_by_grantable_permission(session, separate_global=True)
    owner_arg_list = get_owner_arg_list(
        session,
        permission,
        argument,
        owners_by_arg_by_perm=owners_by_arg_by_perm,
    )

    if not owner_arg_list:
        raise NoOwnersAvailable()

    if permission.audited:
        # will raise UserNotAuditor if any owner of the group is not an auditor
        assert_controllers_are_auditors(group)

    pending_status = "pending"
    now = datetime.utcnow()

    # multiple steps to create the request
    request = PermissionRequest(
            requester_id=user.id,
            group_id=group.id,
            permission_id=permission.id,
            argument=argument,
            status=pending_status,
            requested_at=now,
            ).add(session)
    session.flush()

    request_status_change = PermissionRequestStatusChange(
            request=request,
            user=user,
            to_status=pending_status,
            change_at=now,
            ).add(session)
    session.flush()

    Comment(
            obj_type=OBJ_TYPES_IDX.index("PermissionRequestStatusChange"),
            obj_pk=request_status_change.id,
            user_id=user.id,
            comment=reason,
            created_on=now,
            ).add(session)

    # send notification
    email_context = {
            "user_name": user.name,
            "group_name": group.name,
            "permission_name": permission.name,
            "argument": argument,
            "reason": reason,
            "request_id": request.id,
            "references_header": request.reference_id,
            }

    # TODO: would be nicer if it told you which group you're an approver of
    # that's causing this notification

    mail_to = []
    global_owners = owners_by_arg_by_perm[GLOBAL_OWNERS]["*"]
    non_wildcard_owners = filter(lambda grant: grant[1] != '*', owner_arg_list)
    non_global_owners = filter(lambda grant: grant[0] not in global_owners, owner_arg_list)
    if any(non_wildcard_owners):
        # non-wildcard owners should get all the notifications
        mailto_owner_arg_list = non_wildcard_owners
    elif any(non_global_owners):
        mailto_owner_arg_list = non_global_owners
    else:
        # only the wildcards so they get the notifications
        mailto_owner_arg_list = owner_arg_list

    for owner, arg in mailto_owner_arg_list:
        mail_to += [u for t, u in owner.my_members() if t == 'User']

    subj = get_template_env().get_template('email/pending_permission_request_subj.tmpl').render(
        permission=permission.name, group=group.name
    )
    send_email(session, set(mail_to), subj,
            "pending_permission_request", settings, email_context)

    return request
Example #9
0
def fe_app(session, standard_graph):
    my_settings = {
            "db_session": lambda: session,
            "template_env": get_template_env(),
            }
    return Application(FE_HANDLERS, my_settings=my_settings)
Example #10
0
def fe_app(session, standard_graph):
    my_settings = {
        "db_session": lambda: session,
        "template_env": get_template_env(),
    }
    return Application(FE_HANDLERS, my_settings=my_settings)