Ejemplo n.º 1
0
 def _getRoleAndUsers():
     checkSuspended = None
     role = ROLE_MEMBER
     expireTime = None
     i = 5
     if sys.argv[i].lower() in GROUP_ROLES_MAP:
         role = GROUP_ROLES_MAP[sys.argv[i].lower()]
         i += 1
     if sys.argv[i].lower() in ['suspended', 'notsuspended']:
         checkSuspended = sys.argv[i].lower() == 'suspended'
         i += 1
     if sys.argv[i].lower() in ['expire', 'expires']:
         if role != ROLE_MEMBER:
             controlflow.invalid_argument_exit(sys.argv[i], f'role {role}')
         expireTime = utils.get_time_or_delta_from_now(sys.argv[i + 1])
         i += 2
     if sys.argv[i].lower() in usergroup_types:
         users_email = gam.getUsersToModify(entity_type=sys.argv[i].lower(),
                                            entity=sys.argv[i + 1],
                                            checkSuspended=checkSuspended,
                                            groupUserMembersOnly=False)
     else:
         users_email = [
             gam.normalizeEmailAddressOrUID(sys.argv[i],
                                            checkForCustomerId=True)
         ]
     return (role, expireTime, users_email)
Ejemplo n.º 2
0
 def _getRoleAndUsers():
     checkSuspended = None
     role = None
     expireTime = None
     i = 5
     if sys.argv[i].lower() in GROUP_ROLES_MAP:
         role = GROUP_ROLES_MAP[sys.argv[i].lower()]
         i += 1
     if sys.argv[i].lower() in ['suspended', 'notsuspended']:
         checkSuspended = sys.argv[i].lower() == 'suspended'
         i += 1
     if sys.argv[i].lower() in ['expire', 'expires']:
         expireTime = sys.argv[i + 1]
         i += 2
     if sys.argv[i].lower() in usergroup_types:
         users_email = gam.getUsersToModify(entity_type=sys.argv[i].lower(),
                                            entity=sys.argv[i + 1],
                                            checkSuspended=checkSuspended,
                                            groupUserMembersOnly=False)
     else:
         users_email = [
             gam.normalizeEmailAddressOrUID(sys.argv[i],
                                            checkForCustomerId=True)
         ]
     return (role, expireTime, users_email)
Ejemplo n.º 3
0
def wait_for_mailbox(users):
    '''Wait until users mailbox is provisioned.'''
    cd = gapi_directory.build()
    i = 0
    count = len(users)
    for user in users:
        i += 1
        user = gam.normalizeEmailAddressOrUID(user)
        while True:
            try:
                result = gapi.call(
                    cd.users(),
                    'get',
                    'fields=isMailboxSetup',
                    userKey=user,
                    throw_reasons=[gapi_errors.ErrorReason.USER_NOT_FOUND])
            except gapi_errors.GapiUserNotFoundError:
                print(
                    f'{user} mailboxIsSetup: False (user does not exist yet)')
                sleep(3)
                continue
            mailbox_is_setup = result.get('isMailboxSetup')
            print(f'{user} mailboxIsSetup: {mailbox_is_setup}')
            if mailbox_is_setup:
                break
            sleep(3)
Ejemplo n.º 4
0
def info():
    ci = gapi_cloudidentity.build('cloudidentity_beta')
    group = gam.normalizeEmailAddressOrUID(sys.argv[3])
    getUsers = True
    showJoinDate = True
    showUpdateDate = False
    showMemberTree = False
    i = 4
    while i < len(sys.argv):
        myarg = sys.argv[i].lower().replace('_', '')
        if myarg == 'nousers':
            getUsers = False
            i += 1
        elif myarg == 'nojoindate':
            showJoinDate = False
            i += 1
        elif myarg == 'showupdatedate':
            showUpdateDate = True
            i += 1
        elif myarg == 'membertree':
            showMemberTree = True
            i += 1
        else:
            controlflow.invalid_argument_exit(myarg, 'gam info cigroup')
    name = group_email_to_id(ci, group)
    basic_info = gapi.call(ci.groups(), 'get', name=name)
    display.print_json(basic_info)
    if getUsers and not showMemberTree:
        if not showJoinDate and not showUpdateDate:
            view = 'BASIC'
            pageSize = 1000
        else:
            view = 'FULL'
            pageSize = 500
        members = gapi.get_all_pages(ci.groups().memberships(),
                                     'list',
                                     'memberships',
                                     parent=name,
                                     fields='*',
                                     pageSize=pageSize,
                                     view=view)
        print(' Members:')
        for member in members:
            role = get_single_role(member.get('roles', [])).lower()
            email = member.get('memberKey', {}).get('id')
            member_type = member.get('type', 'USER').lower()
            jc_string = ''
            if showJoinDate:
                joined = member.get('createTime', 'Unknown')
                jc_string += f'  joined {joined}'
            if showUpdateDate:
                updated = member.get('updateTime', 'Unknown')
                jc_string += f'  updated {updated}'
            print(f'  {role}: {email} ({member_type}){jc_string}')
        print(f'Total {len(members)} users in group')
    elif showMemberTree:
        print(' Membership Tree:')
        cached_group_members = {}
        print_member_tree(ci, name, cached_group_members, 2, True)
Ejemplo n.º 5
0
def info_member():
    ci = gapi_cloudidentity.build()
    member = gam.normalizeEmailAddressOrUID(sys.argv[3])
    group = gam.normalizeEmailAddressOrUID(sys.argv[4])
    group_name = gapi.call(ci.groups(),
                           'lookup',
                           groupKey_id=group,
                           fields='name').get('name')
    member_name = gapi.call(ci.groups().memberships(),
                            'lookup',
                            parent=group_name,
                            memberKey_id=member,
                            fields='name').get('name')
    member_details = gapi.call(ci.groups().memberships(),
                               'get',
                               name=member_name)
    display.print_json(member_details)
Ejemplo n.º 6
0
def signout(users):
    cd = gapi_directory.build()
    i = 0
    count = len(users)
    for user in users:
        i += 1
        user = gam.normalizeEmailAddressOrUID(user)
        print(f'Signing Out {user}{gam.currentCount(i, count)}')
        gapi.call(cd.users(), 'signOut', soft_errors=True, userKey=user)
Ejemplo n.º 7
0
def _getCalendarACLScope(i, body):
    body['scope'] = {}
    myarg = sys.argv[i].lower()
    body['scope']['type'] = myarg
    i += 1
    if myarg in ['user', 'group']:
        body['scope']['value'] = gam.normalizeEmailAddressOrUID(sys.argv[i],
                                                                noUid=True)
        i += 1
    elif myarg == 'domain':
        if i < len(sys.argv) and \
           sys.argv[i].lower().replace('_', '') != 'sendnotifications':
            body['scope']['value'] = sys.argv[i].lower()
            i += 1
        else:
            body['scope']['value'] = GC_Values[GC_DOMAIN]
    elif myarg != 'default':
        body['scope']['type'] = 'user'
        body['scope']['value'] = gam.normalizeEmailAddressOrUID(myarg,
                                                                noUid=True)
    return i
Ejemplo n.º 8
0
def delete():
    cd = gapi_directory.build()
    user_email = gam.normalizeEmailAddressOrUID(sys.argv[3])
    print(f'Deleting account for {user_email}')
    try:
        gapi.call(cd.users(),
                  'delete',
                  userKey=user_email,
                  throw_reasons=[gapi_errors.ErrorReason.CONDITION_NOT_MET])
    except gam.gapi.errors.GapiConditionNotMetError as err:
        display.print_error(
            f'{err} The user {user_email} may be (or have recently been) on Google Vault Hold and thus not eligible for deletion. You can check holds with "gam user <email> show vaultholds".'
        )
Ejemplo n.º 9
0
def create():
    ci = gapi_cloudidentity.build('cloudidentity_beta')
    initialGroupConfig = 'EMPTY'
    gapi_directory_customer.setTrueCustomerId()
    parent = f'customers/{GC_Values[GC_CUSTOMER_ID]}'
    body = {
        'groupKey': {
            'id': gam.normalizeEmailAddressOrUID(sys.argv[3], noUid=True)
        },
        'parent': parent,
        'labels': {
            'cloudidentity.googleapis.com/groups.discussion_forum': ''
        },
    }
    i = 4
    while i < len(sys.argv):
        myarg = sys.argv[i].lower().replace('_', '')
        if myarg == 'name':
            body['displayName'] = sys.argv[i + 1]
            i += 2
        elif myarg == 'description':
            body['description'] = sys.argv[i + 1]
            i += 2
        elif myarg in ['alias', 'aliases']:
            # As of 2020/06/25 this doesn't work (yet?)
            aliases = sys.argv[i + 1].split(' ')
            body['additionalGroupKeys'] = []
            for alias in aliases:
                body['additionalGroupKeys'].append({'id': alias})
            i += 2
        elif myarg in ['dynamic']:
            # As of 2020/06/25 this doesn't work (yet?)
            body['dynamicGroupMetadata'] = {
                'queries': [{
                    'query': sys.argv[i + 1],
                    'resourceType': 'USER'
                }]
            }
            i += 2
        elif myarg in ['makeowner']:
            initialGroupConfig = 'WITH_INITIAL_OWNER'
            i += 1
        else:
            print('should not get here')
            sys.exit(5)
    print(f'Creating group {body["groupKey"]["id"]}')
    gapi.call(ci.groups(),
              'create',
              initialGroupConfig=initialGroupConfig,
              body=body)
Ejemplo n.º 10
0
def turn_off_2sv(users):
    cd = gapi_directory.build()
    i = 0
    count = len(users)
    for user in users:
        i += 1
        user = gam.normalizeEmailAddressOrUID(user)
        print(
            f'Turning Off 2-Step Verification for {user}{gam.currentCount(i, count)}'
        )
        gapi.call(cd.twoStepVerification(),
                  'turnOff',
                  soft_errors=True,
                  userKey=user)
Ejemplo n.º 11
0
def group_email_to_id(ci, group, i=0, count=0):
    group = gam.normalizeEmailAddressOrUID(group)
    try:
        return gapi.call(ci.groups(),
                         'lookup',
                         throw_reasons=gapi_errors.GROUP_GET_THROW_REASONS,
                         retry_reasons=gapi_errors.GROUP_GET_RETRY_REASONS,
                         groupKey_id=group,
                         fields='name').get('name')
    except (gapi_errors.GapiGroupNotFoundError,
            gapi_errors.GapiDomainNotFoundError,
            gapi_errors.GapiDomainCannotUseApisError,
            gapi_errors.GapiForbiddenError, gapi_errors.GapiBadRequestError):
        entityUnknownWarning('Group', group, i, count)
        return None
Ejemplo n.º 12
0
def wait_for_mailbox(users):
    '''Wait until users mailbox is provisioned.'''
    cd = gapi_directory.build()
    i = 0
    count = len(users)
    for user in users:
        i += 1
        user = gam.normalizeEmailAddressOrUID(user)
        while True:
            result = gapi.call(cd.users(),
                               'get',
                               'fields=isMailboxSetup',
                               userKey=user)
            mailbox_is_setup = result.get('isMailboxSetup')
            print(f'{user} mailboxIsSetup: {mailbox_is_setup}')
            if mailbox_is_setup:
                break
            sleep(3)
Ejemplo n.º 13
0
def delete(users):
    condel = build()
    delegate = gam.normalizeEmailAddressOrUID(sys.argv[5])
    delegate = gapi_directory_users.get_primary(delegate)
    if not delegate:
        controlflow.system_error_exit(
            5, f'{sys.argv[5]} is not the primary address of a user.')
    i = 0
    count = len(users)
    for user in users:
        i += 1
        print(
            f'Deleting {delegate} contact delegate access to {user}{gam.currentCount(i, count)}'
        )
        gapi.call(condel.delegates(),
                  'delete',
                  soft_errors=True,
                  user=user,
                  delegate=delegate)
Ejemplo n.º 14
0
def create():
    cd = gapi_directory.build()
    user = gam.normalizeEmailAddressOrUID(sys.argv[3])
    body = {'assignedTo': gam.convertEmailAddressToUID(user, cd)}
    role = sys.argv[4]
    body['roleId'] = gapi_directory_roles.getRoleId(role)
    body['scopeType'] = sys.argv[5].upper()
    i = 6
    while i < len(sys.argv):
        myarg = sys.argv[i].lower()
        if myarg == 'condition':
            cd = gapi_directory.build_beta()
            body['condition'] = sys.argv[i + 1]
            if body['condition'] == 'securitygroup':
                body['condition'] = SECURITY_GROUP_CONDITION
            elif body['condition'] == 'nonsecuritygroup':
                body['condition'] = NONSECURITY_GROUP_CONDITION
            i += 2
        else:
            controlflow.invalid_argument_exit(sys.argv[i], 'gam create admin')
    if body['scopeType'] not in ['CUSTOMER', 'ORG_UNIT']:
        controlflow.expected_argument_exit('scope type',
                                           ', '.join(['customer', 'org_unit']),
                                           body['scopeType'])
    if body['scopeType'] == 'ORG_UNIT':
        orgUnit, orgUnitId = gapi_directory_orgunits.getOrgUnitId(
            sys.argv[6], cd)
        body['orgUnitId'] = orgUnitId[3:]
        scope = f'ORG_UNIT {orgUnit}'
    else:
        scope = 'CUSTOMER'
    print(f'Giving {user} admin role {role} for {scope}')
    gapi.call(cd.roleAssignments(),
              'insert',
              customer=GC_Values[GC_CUSTOMER_ID],
              body=body)
Ejemplo n.º 15
0
def print_members():
    ci = gapi_cloudidentity.build()
    todrive = False
    gapi_directory_customer.setTrueCustomerId()
    parent = f'customers/{GC_Values[GC_CUSTOMER_ID]}'
    roles = []
    titles = ['group']
    csvRows = []
    groups_to_get = []
    i = 3
    while i < len(sys.argv):
        myarg = sys.argv[i].lower().replace('_', '')
        if myarg == 'todrive':
            todrive = True
            i += 1
        elif myarg in ['role', 'roles']:
            for role in sys.argv[i + 1].lower().replace(',', ' ').split():
                if role in GROUP_ROLES_MAP:
                    roles.append(GROUP_ROLES_MAP[role])
                else:
                    controlflow.system_error_exit(
                        2,
                        f'{role} is not a valid role for "gam print group-members {myarg}"'
                    )
            i += 2
        elif myarg in ['cigroup', 'cigroups']:
            group_email = gam.normalizeEmailAddressOrUID(sys.argv[i + 1])
            groups_to_get = [group_email]
            i += 2
        else:
            controlflow.invalid_argument_exit(sys.argv[i],
                                              'gam print cigroup-members')
    if not groups_to_get:
        gam.printGettingAllItems('Groups', None)
        page_message = gapi.got_total_items_first_last_msg('Groups')
        groups_to_get = gapi.get_all_pages(
            ci.groups(),
            'list',
            'groups',
            message_attribute=['groupKey', 'id'],
            page_message=page_message,
            parent=parent,
            view='BASIC',
            pageSize=1000,
            fields='nextPageToken,groups(groupKey(id))')
        groups_to_get = [group['groupKey']['id'] for group in groups_to_get]
    i = 0
    count = len(groups_to_get)
    for group_email in groups_to_get:
        i += 1

        sys.stderr.write(
            f'Getting members for {group_email}{gam.currentCountNL(i, count)}')
        group_id = group_email_to_id(ci, group_email)
        print(f'Getting members of cigroup {group_email}...')
        page_message = f' {gapi.got_total_items_first_last_msg("Members")}'
        group_members = gapi.get_all_pages(
            ci.groups().memberships(),
            'list',
            'memberships',
            soft_errors=True,
            parent=group_id,
            view='FULL',
            pageSize=500,
            page_message=page_message,
            message_attribute=['memberKey', 'id'])
        #fields='nextPageToken,memberships(memberKey,roles,createTime,updateTime)')
        if roles:
            group_members = filter_members_to_roles(group_members, roles)
        for member in group_members:
            # reduce role to a single value
            member['role'] = get_single_role(member.pop('roles'))
            member = utils.flatten_json(member)
            for title in member:
                if title not in titles:
                    titles.append(title)
            member['group'] = group_email
            csvRows.append(member)
    display.write_csv_file(csvRows, titles, 'Group Members', todrive)
Ejemplo n.º 16
0
def update():

    # Convert [email protected] to [email protected]; eliminate periods in name for [email protected]
    def _cleanConsumerAddress(emailAddress, mapCleanToOriginal):
        atLoc = emailAddress.find('@')
        if atLoc > 0:
            if emailAddress[atLoc + 1:] in ['gmail.com', 'googlemail.com']:
                cleanEmailAddress = emailAddress[:atLoc].replace(
                    '.', '') + '@gmail.com'
                if cleanEmailAddress != emailAddress:
                    mapCleanToOriginal[cleanEmailAddress] = emailAddress
                    return cleanEmailAddress
        return emailAddress

    def _getRoleAndUsers():
        checkSuspended = None
        role = None
        i = 5
        if sys.argv[i].lower() in GROUP_ROLES_MAP:
            role = GROUP_ROLES_MAP[sys.argv[i].lower()]
            i += 1
        if sys.argv[i].lower() in ['suspended', 'notsuspended']:
            checkSuspended = sys.argv[i].lower() == 'suspended'
            i += 1
        if sys.argv[i].lower() in usergroup_types:
            users_email = gam.getUsersToModify(entity_type=sys.argv[i].lower(),
                                               entity=sys.argv[i + 1],
                                               checkSuspended=checkSuspended,
                                               groupUserMembersOnly=False)
        else:
            users_email = [
                gam.normalizeEmailAddressOrUID(sys.argv[i],
                                               checkForCustomerId=True)
            ]
        return (role, users_email)

    ci = gapi_cloudidentity.build()
    group = sys.argv[3]
    myarg = sys.argv[4].lower()
    items = []
    if myarg in UPDATE_GROUP_SUBCMDS:
        group = gam.normalizeEmailAddressOrUID(group)
        if group.startswith('groups/'):
            parent = group
        else:
            parent = group_email_to_id(ci, group)
        if not parent:
            return
        if myarg == 'add':
            role, users_email = _getRoleAndUsers()
            if not role:
                role = ROLE_MEMBER
            if len(users_email) > 1:
                sys.stderr.write(
                    f'Group: {group}, Will add {len(users_email)} {role}s.\n')
                for user_email in users_email:
                    item = [
                        'gam', 'update', 'cigroup', f'id:{parent}', 'add',
                        role, user_email
                    ]
                    items.append(item)
            elif len(users_email) > 0:
                body = {
                    'memberKey': {
                        'id': users_email[0]
                    },
                    'roles': [{
                        'name': ROLE_MEMBER
                    }]
                }
                if role != ROLE_MEMBER:
                    body['roles'].append({'name': role})
                add_text = [f'as {role}']
                for i in range(2):
                    try:
                        gapi.call(
                            ci.groups().memberships(),
                            'create',
                            throw_reasons=[
                                gapi_errors.ErrorReason.FOUR_O_NINE,
                                gapi_errors.ErrorReason.MEMBER_NOT_FOUND,
                                gapi_errors.ErrorReason.RESOURCE_NOT_FOUND,
                                gapi_errors.ErrorReason.INVALID_MEMBER,
                                gapi_errors.ErrorReason.
                                CYCLIC_MEMBERSHIPS_NOT_ALLOWED
                            ],
                            parent=parent,
                            body=body)
                        print(
                            f' Group: {group}, {users_email[0]} Added {" ".join(add_text)}'
                        )
                        break
                    except (gapi_errors.GapiMemberNotFoundError,
                            gapi_errors.GapiResourceNotFoundError,
                            gapi_errors.GapiInvalidMemberError,
                            gapi_errors.GapiCyclicMembershipsNotAllowedError
                            ) as e:
                        print(
                            f' Group: {group}, {users_email[0]} Add {" ".join(add_text)} Failed: {str(e)}'
                        )
                        break
        elif myarg == 'sync':
            syncMembersSet = set()
            syncMembersMap = {}
            role, users_email = _getRoleAndUsers()
            for user_email in users_email:
                if user_email in ('*', GC_Values[GC_CUSTOMER_ID]):
                    syncMembersSet.add(GC_Values[GC_CUSTOMER_ID])
                else:
                    syncMembersSet.add(
                        _cleanConsumerAddress(user_email.lower(),
                                              syncMembersMap))
            currentMembersSet = set()
            currentMembersMap = {}
            for current_email in gam.getUsersToModify(
                    entity_type='cigroup',
                    entity=group,
                    member_type=role,
                    groupUserMembersOnly=False):
                if current_email == GC_Values[GC_CUSTOMER_ID]:
                    currentMembersSet.add(current_email)
                else:
                    currentMembersSet.add(
                        _cleanConsumerAddress(current_email.lower(),
                                              currentMembersMap))
            to_add = [
                syncMembersMap.get(emailAddress, emailAddress)
                for emailAddress in syncMembersSet - currentMembersSet
            ]
            to_remove = [
                currentMembersMap.get(emailAddress, emailAddress)
                for emailAddress in currentMembersSet - syncMembersSet
            ]
            sys.stderr.write(
                f'Group: {group}, Will add {len(to_add)} and remove {len(to_remove)} {role}s.\n'
            )
            for user in to_add:
                item = [
                    'gam', 'update', 'cigroup', f'id:{parent}', 'add', role,
                    user
                ]
                items.append(item)
            for user in to_remove:
                items.append([
                    'gam', 'update', 'cigroup', f'id:{parent}', 'remove', user
                ])
        elif myarg in ['delete', 'remove']:
            _, users_email = _getRoleAndUsers()
            if len(users_email) > 1:
                sys.stderr.write(
                    f'Group: {group}, Will remove {len(users_email)} emails.\n'
                )
                for user_email in users_email:
                    items.append([
                        'gam', 'update', 'cigroup', f'id:{parent}', 'remove',
                        user_email
                    ])
            elif len(users_email) == 1:
                name = membership_email_to_id(ci, parent, users_email[0])
                try:
                    gapi.call(ci.groups().memberships(),
                              'delete',
                              throw_reasons=[
                                  gapi_errors.ErrorReason.MEMBER_NOT_FOUND,
                                  gapi_errors.ErrorReason.INVALID_MEMBER
                              ],
                              name=name)
                    print(f' Group: {group}, {users_email[0]} Removed')
                except (gapi_errors.GapiMemberNotFoundError,
                        gapi_errors.GapiInvalidMemberError) as e:
                    print(
                        f' Group: {group}, {users_email[0]} Remove Failed: {str(e)}'
                    )
        elif myarg == 'update':
            role, users_email = _getRoleAndUsers()
            if not role:
                role = ROLE_MEMBER
            if len(users_email) > 1:
                sys.stderr.write(
                    f'Group: {group}, Will update {len(users_email)} {role}s.\n'
                )
                for user_email in users_email:
                    item = [
                        'gam', 'update', 'cigroup', f'id:{parent}', 'update',
                        role, user_email
                    ]
                    items.append(item)
            elif len(users_email) > 0:
                name = membership_email_to_id(ci, parent, users_email[0])
                addRoles = []
                removeRoles = []
                new_role = {'role': role}
                current_roles = gapi.call(ci.groups().memberships(),
                                          'get',
                                          name=name,
                                          fields='roles').get('roles', [])
                current_roles = [role['name'] for role in current_roles]
                for crole in current_roles:
                    if crole != ROLE_MEMBER and crole != role:
                        removeRoles.append(crole)
                if role not in current_roles:
                    addRoles.append({'name': role})
                bodys = []
                if addRoles:
                    bodys.append({'addRoles': addRoles})
                if removeRoles:
                    bodys.append({'removeRoles': removeRoles})
                for body in bodys:
                    try:
                        gapi.call(ci.groups().memberships(),
                                  'modifyMembershipRoles',
                                  throw_reasons=[
                                      gapi_errors.ErrorReason.MEMBER_NOT_FOUND,
                                      gapi_errors.ErrorReason.INVALID_MEMBER
                                  ],
                                  name=name,
                                  body=body)
                    except (gapi_errors.GapiMemberNotFoundError,
                            gapi_errors.GapiInvalidMemberError) as e:
                        print(
                            f' Group: {group}, {users_email[0]} Update to {role} Failed: {str(e)}'
                        )
                        break
                print(f' Group: {group}, {users_email[0]} Updated to {role}')

        else:  # clear
            roles = []
            i = 5
            while i < len(sys.argv):
                myarg = sys.argv[i].lower()
                if myarg.upper() in [ROLE_OWNER, ROLE_MANAGER, ROLE_MEMBER]:
                    roles.append(myarg.upper())
                    i += 1
                else:
                    controlflow.invalid_argument_exit(
                        sys.argv[i], 'gam update cigroup clear')
            if not roles:
                roles = [ROLE_MEMBER]
            group = gam.normalizeEmailAddressOrUID(group)
            member_type_message = f'{",".join(roles).lower()}s'
            sys.stderr.write(
                f'Getting {member_type_message} of {group} (may take some time for large groups)...\n'
            )
            page_message = gapi.got_total_items_msg(f'{member_type_message}',
                                                    '...')
            try:
                result = gapi.get_all_pages(
                    ci.groups().memberships(),
                    'list',
                    'memberships',
                    page_message=page_message,
                    throw_reasons=gapi_errors.MEMBERS_THROW_REASONS,
                    parent=parent,
                    fields='nextPageToken,memberships(memberKey,roles)')
                result = filter_members_to_roles(result, roles)
                if not result:
                    print('Group already has 0 members')
                    return
                users_email = [member['memberKey']['id'] for member in result]
                sys.stderr.write(
                    f'Group: {group}, Will remove {len(users_email)} {", ".join(roles).lower()}s.\n'
                )
                for user_email in users_email:
                    items.append([
                        'gam', 'update', 'cigroup', group, 'remove', user_email
                    ])
            except (gapi_errors.GapiGroupNotFoundError,
                    gapi_errors.GapiDomainNotFoundError,
                    gapi_errors.GapiInvalidError,
                    gapi_errors.GapiForbiddenError):
                gam.entityUnknownWarning('Group', group, 0, 0)
        if items:
            gam.run_batch(items)
    else:
        i = 4
        body = {}
        while i < len(sys.argv):
            myarg = sys.argv[i].lower().replace('_', '')
            if myarg == 'name':
                body['displayName'] = sys.argv[i + 1]
                i += 2
            elif myarg == 'description':
                body['description'] = sys.argv[i + 1]
                i += 2
            else:
                controlflow.invalid_argument_exit(sys.argv[i],
                                                  'gam update cigroup')
        updateMask = ','.join(body.keys())
        name = group_email_to_id(ci, group)
        print(f'Updating group {group}')
        gapi.call(ci.groups(),
                  'patch',
                  updateMask=updateMask,
                  name=name,
                  body=body)
Ejemplo n.º 17
0
def print_members():
    ci = gapi_cloudidentity.build(CIGROUP_API_BETA)
    todrive = False
    gapi_directory_customer.setTrueCustomerId()
    parent = f'customers/{GC_Values[GC_CUSTOMER_ID]}'
    usemember = None
    roles = []
    titles = ['group']
    csvRows = []
    groups_to_get = []
    i = 3
    while i < len(sys.argv):
        myarg = sys.argv[i].lower().replace('_', '')
        if myarg == 'todrive':
            todrive = True
            i += 1
        elif myarg in ['role', 'roles']:
            for role in sys.argv[i + 1].lower().replace(',', ' ').split():
                if role in GROUP_ROLES_MAP:
                    roles.append(GROUP_ROLES_MAP[role])
                else:
                    controlflow.system_error_exit(
                        2,
                        f'{role} is not a valid role for "gam print group-members {myarg}"'
                    )
            i += 2
        elif myarg == 'enterprisemember':
            member = gam.convertUIDtoEmailAddress(
                sys.argv[i + 1], email_types=['user', 'group'])
            usemember = f"member_key_id == '{member}' && 'cloudidentity.googleapis.com/groups.discussion_forum' in labels"
            i += 2
        elif myarg in ['cigroup', 'cigroups']:
            group_email = gam.normalizeEmailAddressOrUID(sys.argv[i + 1])
            groups_to_get = [group_email]
            i += 2
        else:
            controlflow.invalid_argument_exit(sys.argv[i],
                                              'gam print cigroup-members')
    if not groups_to_get:
        groups_to_get = _get_groups_list(ci, usemember, parent)
    i = 0
    count = len(groups_to_get)
    for group_email in groups_to_get:
        i += 1

        sys.stderr.write(
            f'Getting members for {group_email}{gam.currentCountNL(i, count)}')
        group_id = group_email_to_id(ci, group_email)
        print(f'Getting members of cigroup {group_email}...')
        page_message = f' {gapi.got_total_items_first_last_msg("Members")}'
        group_members = gapi.get_all_pages(
            ci.groups().memberships(),
            'list',
            'memberships',
            soft_errors=True,
            parent=group_id,
            view='FULL',
            pageSize=500,
            page_message=page_message,
            message_attribute=[CIGROUP_MEMBERKEY, 'id'])
        #fields=f'nextPageToken,memberships({CIGROUP_MEMBERKEY},roles,createTime,updateTime)')
        if roles:
            group_members = filter_members_to_roles(group_members, roles)
        for member in group_members:
            # reduce role to a single value
            member['role'] = get_single_role(member.pop('roles'))
            member = utils.flatten_json(member)
            for title in member:
                if title not in titles:
                    titles.append(title)
            member['group'] = group_email
            csvRows.append(member)
    display.write_csv_file(csvRows, titles, 'Group Members', todrive)
Ejemplo n.º 18
0
def update():

    # Convert [email protected] to [email protected]; eliminate periods in name for [email protected]
    def _cleanConsumerAddress(emailAddress, mapCleanToOriginal):
        atLoc = emailAddress.find('@')
        if atLoc > 0:
            if emailAddress[atLoc + 1:] in ['gmail.com', 'googlemail.com']:
                cleanEmailAddress = emailAddress[:atLoc].replace(
                    '.', '') + '@gmail.com'
                if cleanEmailAddress != emailAddress:
                    mapCleanToOriginal[cleanEmailAddress] = emailAddress
                    return cleanEmailAddress
        return emailAddress

    def _getRoleAndUsers():
        checkSuspended = None
        role = ROLE_MEMBER
        expireTime = None
        i = 5
        if sys.argv[i].lower() in GROUP_ROLES_MAP:
            role = GROUP_ROLES_MAP[sys.argv[i].lower()]
            i += 1
        if sys.argv[i].lower() in ['suspended', 'notsuspended']:
            checkSuspended = sys.argv[i].lower() == 'suspended'
            i += 1
        if sys.argv[i].lower() in ['expire', 'expires']:
            if role != ROLE_MEMBER:
                controlflow.invalid_argument_exit(
                    sys.argv[i], f'role {role}')
            expireTime = utils.get_time_or_delta_from_now(sys.argv[i+1])
            i += 2
        if sys.argv[i].lower() in usergroup_types:
            users_email = gam.getUsersToModify(entity_type=sys.argv[i].lower(),
                                               entity=sys.argv[i + 1],
                                               checkSuspended=checkSuspended,
                                               groupUserMembersOnly=False)
        else:
            users_email = [
                gam.normalizeEmailAddressOrUID(sys.argv[i],
                                               checkForCustomerId=True)
            ]
        return (role, expireTime, users_email)

    ci = gapi_cloudidentity.build('cloudidentity_beta')
    group = sys.argv[3]
    myarg = sys.argv[4].lower()
    items = []
    if myarg in UPDATE_GROUP_SUBCMDS:
        group = gam.normalizeEmailAddressOrUID(group)
        if group.startswith('groups/'):
            parent = group
        else:
            parent = group_email_to_id(ci, group)
        if not parent:
            return
        if myarg == 'add':
            role, expireTime, users_email = _getRoleAndUsers()
            if len(users_email) > 1:
                sys.stderr.write(
                    f'Group: {group}, Will add {len(users_email)} {role}s.\n')
                for user_email in users_email:
                    item = [
                        'gam', 'update', 'cigroup', f'id:{parent}', 'add', role,
                    ]
                    if expireTime:
                        item.extend(['expires', expireTime])
                    item.append(user_email)
                    items.append(item)
            elif len(users_email) > 0:
                body = {
                    'preferredMemberKey': {
                        'id': users_email[0]
                    },
                    'roles': [{
                        'name': ROLE_MEMBER
                    }]
                }
                if role != ROLE_MEMBER:
                    body['roles'].append({'name': role})
                elif expireTime not in {None, NEVER_TIME}:
                    for role in body['roles']:
                        if role['name'] == ROLE_MEMBER:
                            role['expiryDetail'] = {'expireTime': expireTime}
                add_text = [f'as {role}']
                for i in range(2):
                    try:
                        gapi.call(
                            ci.groups().memberships(),
                            'create',
                            throw_reasons=[
                                gapi_errors.ErrorReason.FOUR_O_NINE,
                                gapi_errors.ErrorReason.MEMBER_NOT_FOUND,
                                gapi_errors.ErrorReason.RESOURCE_NOT_FOUND,
                                gapi_errors.ErrorReason.INVALID_MEMBER,
                                gapi_errors.ErrorReason.
                                CYCLIC_MEMBERSHIPS_NOT_ALLOWED
                            ],
                            parent=parent,
                            body=body)
                        print(
                            f' Group: {group}, {users_email[0]} Added {" ".join(add_text)}'
                        )
                        break
                    except (gapi_errors.GapiMemberNotFoundError,
                            gapi_errors.GapiResourceNotFoundError,
                            gapi_errors.GapiInvalidMemberError,
                            gapi_errors.GapiCyclicMembershipsNotAllowedError
                           ) as e:
                        print(
                            f' Group: {group}, {users_email[0]} Add {" ".join(add_text)} Failed: {str(e)}'
                        )
                        break
        elif myarg == 'sync':
            syncMembersSet = set()
            syncMembersMap = {}
            role, expireTime, users_email = _getRoleAndUsers()
            for user_email in users_email:
                if user_email in ('*', GC_Values[GC_CUSTOMER_ID]):
                    syncMembersSet.add(GC_Values[GC_CUSTOMER_ID])
                else:
                    syncMembersSet.add(
                        _cleanConsumerAddress(user_email.lower(),
                                              syncMembersMap))
            currentMembersSet = set()
            currentMembersMap = {}
            for current_email in gam.getUsersToModify(
                    entity_type='cigroup',
                    entity=group,
                    member_type=role,
                    groupUserMembersOnly=False):
                if current_email == GC_Values[GC_CUSTOMER_ID]:
                    currentMembersSet.add(current_email)
                else:
                    currentMembersSet.add(
                        _cleanConsumerAddress(current_email.lower(),
                                              currentMembersMap))
            to_add = [
                syncMembersMap.get(emailAddress, emailAddress)
                for emailAddress in syncMembersSet - currentMembersSet
            ]
            to_remove = [
                currentMembersMap.get(emailAddress, emailAddress)
                for emailAddress in currentMembersSet - syncMembersSet
            ]
            sys.stderr.write(
                f'Group: {group}, Will add {len(to_add)} and remove {len(to_remove)} {role}s.\n'
            )
            for user in to_add:
                item = ['gam', 'update', 'cigroup', f'id:{parent}', 'add',
                        role,]
                if role == ROLE_MEMBER and expireTime not in {None, NEVER_TIME}:
                    item.extend(['expires', expireTime])
                item.append(user)
                items.append(item)
            for user in to_remove:
                items.append([
                    'gam', 'update', 'cigroup', f'id:{parent}', 'remove', user
                ])
        elif myarg in ['delete', 'remove']:
            _, _, users_email = _getRoleAndUsers()
            if len(users_email) > 1:
                sys.stderr.write(
                    f'Group: {group}, Will remove {len(users_email)} emails.\n')
                for user_email in users_email:
                    items.append([
                        'gam', 'update', 'cigroup', f'id:{parent}', 'remove',
                        user_email
                    ])
            elif len(users_email) == 1:
                name = membership_email_to_id(ci, parent, users_email[0])
                try:
                    gapi.call(ci.groups().memberships(),
                              'delete',
                              throw_reasons=[
                                  gapi_errors.ErrorReason.MEMBER_NOT_FOUND,
                                  gapi_errors.ErrorReason.INVALID_MEMBER
                              ],
                              name=name)
                    print(f' Group: {group}, {users_email[0]} Removed')
                except (gapi_errors.GapiMemberNotFoundError,
                        gapi_errors.GapiInvalidMemberError) as e:
                    print(
                        f' Group: {group}, {users_email[0]} Remove Failed: {str(e)}'
                    )
        elif myarg == 'update':
            role, expireTime, users_email = _getRoleAndUsers()
            if len(users_email) > 1:
                sys.stderr.write(
                    f'Group: {group}, Will update {len(users_email)} {role}s.\n'
                )
                for user_email in users_email:
                    item = [
                        'gam', 'update', 'cigroup', f'id:{parent}', 'update',
                        role,]
                    if expireTime:
                        item.extend(['expires', expireTime])
                    item.append(user_email)
                    items.append(item)
            elif len(users_email) > 0:
                name = membership_email_to_id(ci, parent, users_email[0])
                preUpdateRoles = []
                addRoles = []
                removeRoles = []
                postUpdateRoles = []
                member_roles = gapi.call(ci.groups().memberships(),
                                         'get',
                                         name=name,
                                         fields='roles').get('roles', [{'name': ROLE_MEMBER}])
                current_roles = [crole['name'] for crole in member_roles]
                # When upgrading role, strip any expiryDetail from member before role changes
                if role != ROLE_MEMBER:
                    for crole in member_roles:
                        if 'expiryDetail' in crole:
                            preUpdateRoles.append(
                                {'fieldMask': 'expiryDetail.expireTime',
                                 'membershipRole': {'name': ROLE_MEMBER,
                                                    'expiryDetail': {'expireTime': None}}})
                            break
                # When downgrading role or simply updating member expireTime, update expiryDetail after role changes
                elif expireTime:
                    postUpdateRoles.append(
                        {'fieldMask': 'expiryDetail.expireTime',
                         'membershipRole': {'name': role,
                                            'expiryDetail': {'expireTime':  expireTime if expireTime != NEVER_TIME else None}}})
                for crole in current_roles:
                    if crole not in {ROLE_MEMBER, role}:
                        removeRoles.append(crole)
                if role not in current_roles:
                    new_role = {'name': role}
                    if role == ROLE_MEMBER and expireTime not in {None, NEVER_TIME}:
                        new_role['expiryDetail'] = {'expireTime': expireTime}
                        postUpdateRoles = []
                    addRoles.append(new_role)
                bodys = []
                if preUpdateRoles:
                    bodys.append({'updateRolesParams': preUpdateRoles})
                if addRoles:
                    bodys.append({'addRoles': addRoles})
                if removeRoles:
                    bodys.append({'removeRoles': removeRoles})
                if postUpdateRoles:
                    bodys.append({'updateRolesParams': postUpdateRoles})
                for body in bodys:
                    try:
                        gapi.call(ci.groups().memberships(),
                                  'modifyMembershipRoles',
                                  throw_reasons=[
                                      gapi_errors.ErrorReason.MEMBER_NOT_FOUND,
                                      gapi_errors.ErrorReason.INVALID_MEMBER
                                  ],
                                  name=name,
                                  body=body)
                    except (gapi_errors.GapiMemberNotFoundError,
                            gapi_errors.GapiInvalidMemberError) as e:
                        print(
                            f' Group: {group}, {users_email[0]} Update to {role} Failed: {str(e)}'
                        )
                        break
                print(
                  f' Group: {group}, {users_email[0]} Updated to {role}'
                )

        else:  # clear
            roles = []
            i = 5
            while i < len(sys.argv):
                myarg = sys.argv[i].lower()
                if myarg.upper() in [ROLE_OWNER, ROLE_MANAGER, ROLE_MEMBER]:
                    roles.append(myarg.upper())
                    i += 1
                else:
                    controlflow.invalid_argument_exit(
                        sys.argv[i], 'gam update cigroup clear')
            if not roles:
                roles = [ROLE_MEMBER]
            group = gam.normalizeEmailAddressOrUID(group)
            member_type_message = f'{",".join(roles).lower()}s'
            sys.stderr.write(
                f'Getting {member_type_message} of {group} (may take some time for large groups)...\n'
            )
            page_message = gapi.got_total_items_msg(f'{member_type_message}',
                                                    '...')
            try:
                result = gapi.get_all_pages(
                    ci.groups().memberships(),
                    'list',
                    'memberships',
                    page_message=page_message,
                    throw_reasons=gapi_errors.MEMBERS_THROW_REASONS,
                    parent=parent,
                    fields='nextPageToken,memberships(preferredMemberKey,roles)')
                result = filter_members_to_roles(result, roles)
                if not result:
                    print('Group already has 0 members')
                    return
                users_email = [member['preferredMemberKey']['id'] for member in result]
                sys.stderr.write(
                    f'Group: {group}, Will remove {len(users_email)} {", ".join(roles).lower()}s.\n'
                )
                for user_email in users_email:
                    items.append([
                        'gam', 'update', 'cigroup', group, 'remove', user_email
                    ])
            except (gapi_errors.GapiGroupNotFoundError,
                    gapi_errors.GapiDomainNotFoundError,
                    gapi_errors.GapiInvalidError,
                    gapi_errors.GapiForbiddenError):
                gam.entityUnknownWarning('Group', group, 0, 0)
        if items:
            gam.run_batch(items)
    else:
        i = 4
        body = {}
        sec_body = {}
        while i < len(sys.argv):
            myarg = sys.argv[i].lower().replace('_', '')
            if myarg == 'name':
                body['displayName'] = sys.argv[i + 1]
                i += 2
            elif myarg == 'description':
                body['description'] = sys.argv[i + 1]
                i += 2
            elif myarg == 'security':
                body['labels'] = {
                    'cloudidentity.googleapis.com/groups.security': '',
                    'cloudidentity.googleapis.com/groups.discussion_forum': ''
                }
                i += 1
            elif myarg in ['dynamic']:
                body['dynamicGroupMetadata'] = {
                    'queries': [{
                        'query': sys.argv[i + 1],
                        'resourceType': 'USER'
                    }]
                }
                i += 2
            elif myarg in ['memberrestriction', 'memberrestrictions']:
                query = sys.argv[i + 1]
                member_types = {
                  'USER': '******',
                  'SERVICE_ACCOUNT': '2',
                  'GROUP': '3',
                  }
                for key, val in member_types.items():
                    query = query.replace(key, val)
                sec_body['memberRestriction'] = {'query': query}
                i += 2
            else:
                controlflow.invalid_argument_exit(sys.argv[i],
                                                  'gam update cigroup')
        if body:
            updateMask = ','.join(body.keys())
            name = group_email_to_id(ci, group)
            print(f'Updating group {group}')
            gapi.call(ci.groups(),
                      'patch',
                      updateMask=updateMask,
                      name=name,
                      body=body)
        if sec_body:
            updateMask = 'member_restriction.query'
            # it seems like a bug that API requires /securitySettings
            # appended to name. We'll see if Google servers change this
            # at some point.
            name = f'{group_email_to_id(ci, group)}/securitySettings'
            print(f'Updating group {group} security settings')
            gapi.call(ci.groups(),
                    'updateSecuritySettings',
                    name=name,
                    updateMask=updateMask,
                    body=sec_body)
Ejemplo n.º 19
0
def print_members():
    ci = gapi_cloudidentity.build('cloudidentity_beta')
    todrive = False
    gapi_directory_customer.setTrueCustomerId()
    parent = f'customers/{GC_Values[GC_CUSTOMER_ID]}'
    usemember = None
    roles = []
    titles = ['group']
    csvRows = []
    groups_to_get = []
    i = 3
    while i < len(sys.argv):
        myarg = sys.argv[i].lower().replace('_', '')
        if myarg == 'todrive':
            todrive = True
            i += 1
        elif myarg in ['role', 'roles']:
            for role in sys.argv[i + 1].lower().replace(',', ' ').split():
                if role in GROUP_ROLES_MAP:
                    roles.append(GROUP_ROLES_MAP[role])
                else:
                    controlflow.system_error_exit(
                        2,
                        f'{role} is not a valid role for "gam print group-members {myarg}"'
                    )
            i += 2
        elif myarg == 'enterprisemember':
            member = gam.convertUIDtoEmailAddress(
                sys.argv[i + 1], email_types=['user', 'group'])
            usemember = f"member_key_id == '{member}' && 'cloudidentity.googleapis.com/groups.discussion_forum' in labels"
            i += 2
        elif myarg in ['cigroup', 'cigroups']:
            group_email = gam.normalizeEmailAddressOrUID(sys.argv[i + 1])
            groups_to_get = [group_email]
            i += 2
        else:
            controlflow.invalid_argument_exit(sys.argv[i],
                                              'gam print cigroup-members')
    if not groups_to_get:
        gam.printGettingAllItems('Groups', usemember)
        page_message = gapi.got_total_items_first_last_msg('Groups')
        if usemember:
            try:
                groups_to_get = gapi.get_all_pages(
                    ci.groups().memberships(),
                    'searchTransitiveGroups',
                    'memberships',
                    throw_reasons=[gapi_errors.ErrorReason.FOUR_O_O],
                    message_attribute=['groupKey', 'id'],
                    page_message=page_message,
                    parent='groups/-',
                    query=usemember,
                    pageSize=1000,
                    fields=
                    'nextPageToken,memberships(groupKey(id),relationType)')
            except googleapiclient.errors.HttpError:
                controlflow.system_error_exit(
                    2, f'enterprisemember requires Enterprise license')
            groups_to_get = [
                group['groupKey']['id'] for group in groups_to_get
                if group['relationType'] == 'DIRECT'
            ]
        else:
            groups_to_get = gapi.get_all_pages(
                ci.groups(),
                'list',
                'groups',
                message_attribute=['groupKey', 'id'],
                page_message=page_message,
                parent=parent,
                view='BASIC',
                pageSize=1000,
                fields='nextPageToken,groups(groupKey(id))')
            groups_to_get = [
                group['groupKey']['id'] for group in groups_to_get
            ]
    i = 0
    count = len(groups_to_get)
    for group_email in groups_to_get:
        i += 1

        sys.stderr.write(
            f'Getting members for {group_email}{gam.currentCountNL(i, count)}')
        group_id = group_email_to_id(ci, group_email)
        print(f'Getting members of cigroup {group_email}...')
        page_message = f' {gapi.got_total_items_first_last_msg("Members")}'
        group_members = gapi.get_all_pages(
            ci.groups().memberships(),
            'list',
            'memberships',
            soft_errors=True,
            parent=group_id,
            view='FULL',
            pageSize=500,
            page_message=page_message,
            message_attribute=['memberKey', 'id'])
        #fields='nextPageToken,memberships(memberKey,roles,createTime,updateTime)')
        if roles:
            group_members = filter_members_to_roles(group_members, roles)
        for member in group_members:
            # reduce role to a single value
            member['role'] = get_single_role(member.pop('roles'))
            member = utils.flatten_json(member)
            for title in member:
                if title not in titles:
                    titles.append(title)
            member['group'] = group_email
            csvRows.append(member)
    display.write_csv_file(csvRows, titles, 'Group Members', todrive)
Ejemplo n.º 20
0
def print_():
    cd = gapi_directory.build()
    roleId = None
    todrive = False
    kwargs = {}
    item_fields = [
        'roleAssignmentId', 'roleId', 'assignedTo', 'scopeType', 'orgUnitId'
    ]
    titles = [
        'roleAssignmentId', 'roleId', 'role', 'assignedTo', 'assignedToUser',
        'scopeType', 'orgUnitId', 'orgUnit'
    ]
    csvRows = []
    i = 3
    while i < len(sys.argv):
        myarg = sys.argv[i].lower()
        if myarg == 'user':
            kwargs['userKey'] = gam.normalizeEmailAddressOrUID(sys.argv[i + 1])
            i += 2
        elif myarg == 'role':
            roleId = gapi_directory_roles.getRoleId(sys.argv[i + 1])
            i += 2
        elif myarg == 'condition':
            cd = gapi_directory.build_beta()
            item_fields.append('condition')
            i += 1
        elif myarg == 'todrive':
            todrive = True
            i += 1
        else:
            controlflow.invalid_argument_exit(sys.argv[i], 'gam print admins')
    fields = f'nextPageToken,items({",".join(item_fields)})'
    if roleId and not kwargs:
        kwargs['roleId'] = roleId
        roleId = None
    admins = gapi.get_all_pages(cd.roleAssignments(),
                                'list',
                                'items',
                                customer=GC_Values[GC_CUSTOMER_ID],
                                fields=fields,
                                **kwargs)
    for admin in admins:
        if roleId and roleId != admin['roleId']:
            continue
        admin_attrib = {}
        for key, value in list(admin.items()):
            if key == 'assignedTo':
                admin_attrib['assignedToUser'] = gam.user_from_userid(value)
            elif key == 'roleId':
                admin_attrib['role'] = gapi_directory_roles.role_from_roleid(
                    value)
            elif key == 'orgUnitId':
                value = f'id:{value}'
                admin_attrib[
                    'orgUnit'] = gapi_directory_orgunits.orgunit_from_orgunitid(
                        value, cd)
            elif key == 'condition':
                if value == SECURITY_GROUP_CONDITION:
                    value = 'securitygroup'
                elif value == NONSECURITY_GROUP_CONDITION:
                    value = 'nonsecuritygroup'
            if key not in titles:
                titles.append(key)
            admin_attrib[key] = value
        csvRows.append(admin_attrib)
    display.write_csv_file(csvRows, titles, 'Admins', todrive)
Ejemplo n.º 21
0
def showReport():
    rep = buildGAPIObject()
    throw_reasons = [gapi.errors.ErrorReason.INVALID]
    report = sys.argv[2].lower()
    report = REPORT_CHOICE_MAP.get(report.replace('_', ''), report)
    if report == 'usage':
        showUsage()
        return
    if report == 'usageparameters':
        showUsageParameters()
        return
    valid_apps = gapi.get_enum_values_minus_unspecified(
        rep._rootDesc['resources']['activities']['methods']['list']
        ['parameters']['applicationName']['enum']) + ['customer', 'user']
    if report not in valid_apps:
        controlflow.expected_argument_exit('report',
                                           ', '.join(sorted(valid_apps)),
                                           report)
    customerId = GC_Values[GC_CUSTOMER_ID]
    if customerId == MY_CUSTOMER:
        customerId = None
    filters = parameters = actorIpAddress = startTime = endTime = eventName = orgUnitId = None
    tryDate = datetime.date.today().strftime(YYYYMMDD_FORMAT)
    to_drive = False
    userKey = 'all'
    fullDataRequired = None
    i = 3
    while i < len(sys.argv):
        myarg = sys.argv[i].lower()
        if myarg == 'date':
            tryDate = utils.get_yyyymmdd(sys.argv[i + 1])
            i += 2
        elif myarg in ['orgunit', 'org', 'ou']:
            _, orgUnitId = gam.getOrgUnitId(sys.argv[i + 1])
            i += 2
        elif myarg == 'fulldatarequired':
            fullDataRequired = []
            fdr = sys.argv[i + 1].lower()
            if fdr and fdr == 'all':
                fullDataRequired = 'all'
            else:
                fullDataRequired = fdr.replace(',', ' ').split()
            i += 2
        elif myarg == 'start':
            startTime = utils.get_time_or_delta_from_now(sys.argv[i + 1])
            i += 2
        elif myarg == 'end':
            endTime = utils.get_time_or_delta_from_now(sys.argv[i + 1])
            i += 2
        elif myarg == 'event':
            eventName = sys.argv[i + 1]
            i += 2
        elif myarg == 'user':
            userKey = sys.argv[i + 1].lower()
            if userKey != 'all':
                userKey = gam.normalizeEmailAddressOrUID(sys.argv[i + 1])
            i += 2
        elif myarg in ['filter', 'filters']:
            filters = sys.argv[i + 1]
            i += 2
        elif myarg in ['fields', 'parameters']:
            parameters = sys.argv[i + 1]
            i += 2
        elif myarg == 'ip':
            actorIpAddress = sys.argv[i + 1]
            i += 2
        elif myarg == 'todrive':
            to_drive = True
            i += 1
        else:
            controlflow.invalid_argument_exit(sys.argv[i], 'gam report')
    if report == 'user':
        while True:
            try:
                one_page = gapi.call(rep.userUsageReport(),
                                     'get',
                                     throw_reasons=throw_reasons,
                                     date=tryDate,
                                     userKey=userKey,
                                     customerId=customerId,
                                     orgUnitID=orgUnitId,
                                     fields='warnings,usageReports',
                                     maxResults=1)
                warnings = one_page.get('warnings', [])
                has_reports = bool(one_page.get('usageReports'))
                fullData, tryDate = _check_full_data_available(
                    warnings, tryDate, fullDataRequired, has_reports)
                if fullData < 0:
                    print('No user report available.')
                    sys.exit(1)
                if fullData == 0:
                    continue
                page_message = gapi.got_total_items_msg('Users', '...\n')
                usage = gapi.get_all_pages(rep.userUsageReport(),
                                           'get',
                                           'usageReports',
                                           page_message=page_message,
                                           throw_reasons=throw_reasons,
                                           date=tryDate,
                                           userKey=userKey,
                                           customerId=customerId,
                                           orgUnitID=orgUnitId,
                                           filters=filters,
                                           parameters=parameters)
                break
            except gapi.errors.GapiInvalidError as e:
                tryDate = _adjust_date(str(e))
        if not usage:
            print('No user report available.')
            sys.exit(1)
        titles = ['email', 'date']
        csvRows = []
        for user_report in usage:
            if 'entity' not in user_report:
                continue
            row = {'email': user_report['entity']['userEmail'], 'date': tryDate}
            for item in user_report.get('parameters', []):
                if 'name' not in item:
                    continue
                name = item['name']
                if not name in titles:
                    titles.append(name)
                for ptype in REPORTS_PARAMETERS_SIMPLE_TYPES:
                    if ptype in item:
                        row[name] = item[ptype]
                        break
                else:
                    row[name] = ''
            csvRows.append(row)
        display.write_csv_file(csvRows, titles, f'User Reports - {tryDate}',
                               to_drive)
    elif report == 'customer':
        while True:
            try:
                first_page = gapi.call(rep.customerUsageReports(),
                                       'get',
                                       throw_reasons=throw_reasons,
                                       customerId=customerId,
                                       date=tryDate,
                                       fields='warnings,usageReports')
                warnings = first_page.get('warnings', [])
                has_reports = bool(first_page.get('usageReports'))
                fullData, tryDate = _check_full_data_available(
                    warnings, tryDate, fullDataRequired, has_reports)
                if fullData < 0:
                    print('No customer report available.')
                    sys.exit(1)
                if fullData == 0:
                    continue
                usage = gapi.get_all_pages(rep.customerUsageReports(),
                                           'get',
                                           'usageReports',
                                           throw_reasons=throw_reasons,
                                           customerId=customerId,
                                           date=tryDate,
                                           parameters=parameters)
                break
            except gapi.errors.GapiInvalidError as e:
                tryDate = _adjust_date(str(e))
        if not usage:
            print('No customer report available.')
            sys.exit(1)
        titles = ['name', 'value', 'client_id']
        csvRows = []
        auth_apps = list()
        for item in usage[0]['parameters']:
            if 'name' not in item:
                continue
            name = item['name']
            if 'intValue' in item:
                value = item['intValue']
            elif 'msgValue' in item:
                if name == 'accounts:authorized_apps':
                    for subitem in item['msgValue']:
                        app = {}
                        for an_item in subitem:
                            if an_item == 'client_name':
                                app['name'] = 'App: ' + \
                                    subitem[an_item].replace('\n', '\\n')
                            elif an_item == 'num_users':
                                app['value'] = f'{subitem[an_item]} users'
                            elif an_item == 'client_id':
                                app['client_id'] = subitem[an_item]
                        auth_apps.append(app)
                    continue
                values = []
                for subitem in item['msgValue']:
                    if 'count' in subitem:
                        mycount = myvalue = None
                        for key, value in list(subitem.items()):
                            if key == 'count':
                                mycount = value
                            else:
                                myvalue = value
                            if mycount and myvalue:
                                values.append(f'{myvalue}:{mycount}')
                        value = ' '.join(values)
                    elif 'version_number' in subitem \
                         and 'num_devices' in subitem:
                        values.append(f'{subitem["version_number"]}:'
                                      f'{subitem["num_devices"]}')
                    else:
                        continue
                    value = ' '.join(sorted(values, reverse=True))
            csvRows.append({'name': name, 'value': value})
        for app in auth_apps:  # put apps at bottom
            csvRows.append(app)
        display.write_csv_file(csvRows,
                               titles,
                               f'Customer Report - {tryDate}',
                               todrive=to_drive)
    else:
        page_message = gapi.got_total_items_msg('Activities', '...\n')
        activities = gapi.get_all_pages(rep.activities(),
                                        'list',
                                        'items',
                                        page_message=page_message,
                                        applicationName=report,
                                        userKey=userKey,
                                        customerId=customerId,
                                        actorIpAddress=actorIpAddress,
                                        startTime=startTime,
                                        endTime=endTime,
                                        eventName=eventName,
                                        filters=filters,
                                        orgUnitID=orgUnitId)
        if activities:
            titles = ['name']
            csvRows = []
            for activity in activities:
                events = activity['events']
                del activity['events']
                activity_row = utils.flatten_json(activity)
                purge_parameters = True
                for event in events:
                    for item in event.get('parameters', []):
                        if set(item) == set(['value', 'name']):
                            event[item['name']] = item['value']
                        elif set(item) == set(['intValue', 'name']):
                            if item['name'] in ['start_time', 'end_time']:
                                val = item.get('intValue')
                                if val is not None:
                                    val = int(val)
                                    if val >= 62135683200:
                                        event[item['name']] = \
                                            datetime.datetime.fromtimestamp(
                                                val-62135683200).isoformat()
                            else:
                                event[item['name']] = item['intValue']
                        elif set(item) == set(['boolValue', 'name']):
                            event[item['name']] = item['boolValue']
                        elif set(item) == set(['multiValue', 'name']):
                            event[item['name']] = ' '.join(item['multiValue'])
                        elif item['name'] == 'scope_data':
                            parts = {}
                            for message in item['multiMessageValue']:
                                for mess in message['parameter']:
                                    value = mess.get(
                                        'value',
                                        ' '.join(mess.get('multiValue', [])))
                                    parts[mess['name']] = parts.get(
                                        mess['name'], []) + [value]
                            for part, v in parts.items():
                                if part == 'scope_name':
                                    part = 'scope'
                                event[part] = ' '.join(v)
                        else:
                            purge_parameters = False
                    if purge_parameters:
                        event.pop('parameters', None)
                    row = utils.flatten_json(event)
                    row.update(activity_row)
                    for item in row:
                        if item not in titles:
                            titles.append(item)
                    csvRows.append(row)
            display.sort_csv_titles([
                'name',
            ], titles)
            display.write_csv_file(csvRows, titles,
                                   f'{report.capitalize()} Activity Report',
                                   to_drive)