예제 #1
0
def get_exec_from_dn(ldap_entry_dn):
    """
    Walk up the reporting structure until we get to someone who is in
    the Exec group. Return that someone.

    This function can fail to return a result if someone in the tree is
    in the process of leaving, i.e. they are recorded as a manager but
    are no longer an active account.
    """

    # Get the membership of the Exec group. Use the mailing list so that
    # we get the full DNs, thus making it easier to check.
    _, memb_result = shared_ldap.find_group("exec", ["uniqueMember"])
    members = memb_result[0].uniqueMember.values

    # Walk up the tree ...
    searching = True
    while searching:
        result = shared_ldap.get_object(ldap_entry_dn, ["manager"])
        if result is not None and result.manager.value is not None:
            ldap_entry_dn = result.manager.value
            if ldap_entry_dn in members:
                mgr_email = shared_ldap.get_object(result.manager.value,
                                                   ["mail"])
                return mgr_email.mail.values[0]
            # otherwise loop to that person
        else:
            # The intermediate manager is leaving.
            searching = False
    return None
예제 #2
0
def get_group_owners(group_cn):
    """ Consistently return a list of owners. """
    _, result = shared_ldap.find_group(group_cn, ["owner"])
    if len(result) == 1:
        owners = result[0].owner.values
    else:
        owners = []
    return owners
예제 #3
0
def transition(status_to, ticket_data):
    """
    If the status is "In Progress", trigger the membership change. This
    status can only be reached from Open or Needs Approval.
    """
    if status_to == "In Progress":
        cf_group_email_address = custom_fields.get(GROUP_EMAIL_ADDRESS)
        group_email_address = shared_sd.get_field(
            ticket_data, cf_group_email_address).strip().lower()
        group_email_address, result = shared_ldap.find_group(
            group_email_address, ['owner'])
        action_change(ticket_data, result[0])
예제 #4
0
def post_owners_of_group_as_comment(group_full_dn):
    """Emit a list of the owners of the group."""
    # Need to re-fetch the group ownership because we may have changed it
    # since last time we queried it.
    name = shared_ldap.extract_id_from_dn(group_full_dn)
    _, result = shared_ldap.find_group(name, ["owner"])
    if len(result) == 1 and result[0].owner.values != []:
        response = "Here are the owners for the group:\r\n"
        for owner in result[0].owner.values:
            response += "* [%s|mailto:%s]\r\n" % owner_and_display_name(owner)
    else:
        response = "There are no owners for the group."
    shared_sd.post_comment(response, True)
예제 #5
0
def process_public_comment(ticket_data, last_comment, keyword):
    """Logic to process a public comment."""
    shared_sd.assign_issue_to(shared.globals.CONFIGURATION["bot_name"])
    # If the original reporter IS a group owner, we will only accept comments
    # from the same person and those comments will be add/remove commands.
    #
    # Otherwise, deassign and let IT work on what was said.
    #
    # Get the definitive email address for the group and the owner(s).
    cf_group_email_address = custom_fields.get(GROUP_EMAIL_ADDRESS)
    group_email_address = shared_sd.get_field(
        ticket_data, cf_group_email_address).strip().lower()
    group_email_address, result = shared_ldap.find_group(
        group_email_address, ['owner'])
    # Make sure that the group still exists because this is all asynchronous
    # and anything could have happened!
    if len(result) == 0:
        shared_sd.post_comment(
            "Sorry but the group's email address can't be found in Linaro "
            "Login.", True)
        shared_sd.resolve_ticket(WONT_DO)
        return True
    if len(result) != 1:
        shared_sd.post_comment(
            "Sorry but, somehow, the group's email address appears more than "
            "once in Linaro Login.", True)
        shared_sd.resolve_ticket(WONT_DO)
        return True

    if (result[0].owner.values != [] and
            shared_ldap.reporter_is_group_owner(result[0].owner.values) and
            keyword in ("add", "remove")):
        grp_name = shared_ldap.extract_id_from_dn(result[0].entry_dn)
        changes = last_comment["body"].split("\n")
        batch_process_ownership_changes(grp_name, changes)
        post_owners_of_group_as_comment(result[0].entry_dn)
        return True

    return False
예제 #6
0
def get_group_details(ticket_data):
    """ Get the true email address and LDAP object for the specified group """
    cf_group_email_address = custom_fields.get("Group Email Address")
    group_email_address = shared_sd.get_field(
        ticket_data, cf_group_email_address).strip().lower()
    return shared_ldap.find_group(group_email_address, ['owner'])
예제 #7
0
def batch_process_membership_changes(email_address,
                                     batch,
                                     auto=False,
                                     change_to_make=None):
    """ Process a list of changes to the membership """
    # If auto is false, we're expecting "keyword emailaddress" and will stop on
    # the first line that doesn't match that syntax. If auto is true, we're
    # just expecting emailaddress and the change flag will indicate add or
    # remove.
    #
    # The formatting of the text varies from "\r\n" in the original request to
    # "\n" in comments, so the *caller* must pass batch as a list.

    change_made = False

    # We need a list of current members to sanity check the request.
    _, result = shared_ldap.find_group(email_address, ["uniqueMember"])
    if len(result) == 1 and "uniqueMember" in result[0]:
        members = result[0].uniqueMember.values
    else:
        members = []

    group_cn = shared_ldap.extract_id_from_dn(result[0].entry_dn)
    response = ""

    for change in batch:
        # When submitting a comment via the portal, blank lines get inserted
        # so we just ignore them.
        if change != "":
            email_address, keyword = evaluate_change(change, auto,
                                                     change_to_make)
            if keyword is None:
                response += (
                    "\r\nCouldn't find a command at the start of '%s'. "
                    "*Processing of this request will now stop.*\r\n" % change)
                break

            result = find_member_change(email_address)

            if keyword == "add":
                if result is None:
                    response += (
                        "Couldn't find an entry '%s' on Linaro Login. Please "
                        "use https://servicedesk.linaro.org/servicedesk/"
                        "customer/portal/3/create/120 to create a contact "
                        "(email only) or external account (if login required) "
                        "and then submit a new ticket to add them.\r\n" %
                        email_address)
                elif result == ("cn=%s,ou=mailing,ou=groups,dc=linaro,dc=org" %
                                group_cn):
                    response += (
                        "You cannot add the group as a member to itself.\r\n")
                elif result in members:
                    response += ("%s is already a member of the group.\r\n" %
                                 email_address)
                else:
                    response += "Adding %s\r\n" % email_address
                    shared_ldap.add_to_group(group_cn, result)
                    members.append(result)
                    change_made = True
            elif keyword == "remove":
                if result is None:
                    response += (
                        "Couldn't find an entry '%s' on Linaro Login. Did you "
                        "mistype?\r\n" % email_address)
                elif result in members:
                    response += "Removing %s\r\n" % email_address
                    shared_ldap.remove_from_group(group_cn, result)
                    members.remove(result)
                    change_made = True
                else:
                    response += (
                        "%s is not a member of the group so cannot be "
                        "removed as one.\r\n" % email_address)
            else:
                response += ("%s is not recognised as 'add' or 'remove'.\r\n" %
                             keyword)

    if change_made:
        linaro_shared.trigger_google_sync()
        response += (
            "Please note it can take up to 15 minutes for these changes to "
            "appear on Google.")

    if response != "":
        shared_sd.post_comment(response, True)
예제 #8
0
def create(ticket_data):
    """Triggered when the issue is created."""
    cf_group_email_address = custom_fields.get(GROUP_EMAIL_ADDRESS)
    group_email_address = shared_sd.get_field(
        ticket_data, cf_group_email_address).strip().lower()
    group_email_address, result = shared_ldap.find_group(
        group_email_address, ['owner'])

    shared_sd.set_summary(
        "View/Change group ownership for %s" % group_email_address)
    shared_sd.assign_issue_to(shared.globals.CONFIGURATION["bot_name"])

    if len(result) == 0:
        shared_sd.post_comment(
            "Sorry but the group's email address can't be found in Linaro "
            "Login.", True)
        shared_sd.resolve_ticket(WONT_DO)
        return
    if len(result) != 1:
        shared_sd.post_comment(
            "Sorry but, somehow, the group's email address appears more than "
            "once in Linaro Login.", True)
        shared_sd.resolve_ticket(WONT_DO)
        return
    # See if the bot owns this group
    owners = result[0].owner.values
    if (len(owners) == 1 and owners[0] == IT_BOT):
        shared_sd.post_comment(
            (
                "This group is maintained through automation. It is not "
                "possible to change the owners of this group or raise "
                "tickets to directly change the membership. If you want "
                "to understand how this group is maintained automatically, "
                "please raise a general IT Services support ticket."
            ),
            True
        )
        shared_sd.resolve_ticket()
        return

    # Do we have any changes to process? If not, post the current owners to
    # the ticket.
    cf_group_owners = custom_fields.get("Group Owners")
    ownerchanges = shared_sd.get_field(ticket_data, cf_group_owners)
    if ownerchanges is None:
        post_owners_of_group_as_comment(result[0].entry_dn)
        if shared_ldap.reporter_is_group_owner(result[0].owner.values):
            shared_sd.post_comment(
                ("As you are an owner of this group, you can make changes to "
                 "the ownership by posting new comments to this ticket with "
                 "the following format:\r\n"
                 "*add* <email address>\r\n"
                 "*remove* <email address>\r\n"
                 "One command per line but you can have multiple changes in a "
                 "single comment. If you do not get the syntax right, the "
                 "automation will not be able to understand your request and "
                 "processing will stop.\r\n"), True)
            shared_sd.transition_request_to("Waiting for customer")
        else:
            shared_sd.post_comment(
                "As you are not an owner of this group, if you want to make "
                "changes to the ownership, you will need to open a "
                "[new ticket|https://servicedesk.linaro.org/servicedesk/"
                "customer/portal/3/create/140].", True)
            shared_sd.resolve_ticket()
        return

    # There are changes ... but is the requester a group owner?
    cf_approvers = custom_fields.get("Approvers")
    if result[0].owner.values == []:
        # No owners at all. IT is always allowed to make changes
        if shared_ldap.is_user_in_group("its", shared.globals.REPORTER):
            shared_sd.transition_request_to("In progress")
        else:
            shared_sd.post_comment(
                "This group has no owners. Asking IT Services to review "
                "your request.", True)
            it_members = shared_ldap.get_group_membership(
                "cn=its,ou=mailing,ou=groups,dc=linaro,dc=org")
            shared_sd.assign_approvers(it_members, cf_approvers)
            shared_sd.transition_request_to("Needs approval")
    elif shared_ldap.reporter_is_group_owner(result[0].owner.values):
        shared_sd.transition_request_to("In progress")
    else:
        shared_sd.post_comment(
            "As you are not an owner of this group, the owners will be "
            "asked to approve or decline your request.", True)
        shared_sd.assign_approvers(result[0].owner.values, cf_approvers)
        shared_sd.transition_request_to("Needs approval")