def findAffectedUsers(appInstanceId):
    """ Finds 'affected users': when subdomain part of UPN and linked domain are different. """

    api = openapi.OpenAPI()
    aps_api = apsapi.API(getApsApiUrl())
    appInstanceToken = getAppInstanceToken(appInstanceId, api)
    instanceUsersCount = countInstanceResources(appInstanceId,
                                                O365_APS_TYPE_USER)
    affectedUsers = []

    path = "aps/2/resources/?implementing(%s)" \
           ",and(eq(aps.status,aps:ready)" \
           ",select(aps.id,login,domain.domain_name,tenant.aps.id)" \
           ",sort(+aps.id)" \
           ",limit(0,%d))" \
           % (O365_APS_TYPE_USER, instanceUsersCount)
    allInstanceUsers = aps_api.GET(path, appInstanceToken)
    for user in allInstanceUsers:
        # VBE: strange condition  user['domain']['domain_name']!=0   ----- a glupost of mine, it should be len(user['domain']['domain_name'])!=0
        if (user['domain']['domain_name'].lower() in user['login'].lower()
                and len(user['login']) != 0
                and user['domain']['domain_name'] != 0):
            # It is not necessary to log users which are OK  ------ there was an idea to show good\bad users ratio.
            log(
                "Processing user " + user['login'] +
                ". He is OK: linked domain matches login.", logging.INFO, True)
        else:
            log(
                "Processing user " + user['login'] +
                ". He is NOT OK: linked to domain with name: " +
                user['domain']['domain_name'], logging.INFO, True)
            affectedUsers.append(user)
    return affectedUsers
def fixIncorrectDomainLink(userUIDToFix, correctDomainUIDToLink,
                           appInstanceId):
    """ Links an Office365/User resource to a given Office365/Domain. """

    api = openapi.OpenAPI()
    aps_api = apsapi.API(getApsApiUrl())
    appInstanceToken = getAppInstanceToken(appInstanceId, api)
    path = "aps/2/resources/%s/domain/" % userUIDToFix
    body = {
        "aps": {
            "id": correctDomainUIDToLink
        },
    }
    try:
        aps_api.POST(path, appInstanceToken, body)
    except Exception as ex:
        # VBE: it would be useful to log the error as well   ------ yes, I need to add smthg like log(str(ex),logging.INFO, True)
        log("Failed to update domain link of user: " + userUIDToFix,
            logging.INFO, True)
def createOffice365DomainResource(appInstanceId, domainName, coreDomainUID,
                                  tenantAPSUID):
    """ Creates new Office365/Domain resource in scope of certain OA subscription. Core domain UID should be specified to link with. """

    api = openapi.OpenAPI()
    aps_api = apsapi.API(getApsApiUrl())
    appInstanceToken = getAppInstanceToken(appInstanceId, api)

    # VBE: Unfortunately it doesn't work     --------  it does work in terms of poaupdater module: helps to add additioanl request headers. Yes, it doesn't work in scope of this task (
    appInstanceToken[
        "APS-Resource-ID"] = tenantAPSUID  # <-- add additional header with Office365/Tenant APS resource UID. Need for proper linking.

    path2 = "aps/2/applications/"
    allApplications = aps_api.GET(
        path2, appInstanceToken
    )  # <-- try to find Application UID by package name. RQL doesn't work on /applications/ node.
    for application in allApplications:
        if application.aps.package.name == 'Office 365':
            applicationUID = str(application.aps.id)
            # VBE: break   -------- a glupost of mine, forgot to add it

    # VBE: Need to validate the value of applicationUID
    path = "aps/2/applications/%s/office365domains/" % applicationUID
    # VBE: the body should be constructed from using the existing domain resource (including service_name, dns_records etc.)
    body = {
        "aps": {
            "type": O365_APS_TYPE_DOMAIN
        },
        "domain_name": domainName,
        "cloud_status": "Ready",
        "service_name": "rapidAuth,mobileDevice",
        "domain": {
            "aps": {
                "id": coreDomainUID
            }
        }
    }
    try:
        aps_api.POST(path, appInstanceToken, body)
    except Exception as ex:
        log("Failed to create new domain with name: " + domainName,
            logging.INFO, True)
def main():
    parser = OptionParser(
        version=VERSION,
        usage=
        "\nFind users whose UPN differs from domain name they are linked with. Fix by linking them to a correct domain in scope of the same subscription.\n\n  Usage: %prog --app-instance-id ID [--dry-run]"
    )
    parser.add_option("--app-instance-id",
                      dest="app_instance_id",
                      type="int",
                      help="Office 365 APS 2.0 Application Instance ID")

    parser.add_option(
        "--mode",
        dest="mode",
        type="string",
        default=None,
        help=
        "Script mode. Possible values: \n fixByDomainName - fix User <-> Domain links when login subdomain part does not match linked domain name; \n fixByTenantUID - fix User <-> Domain links when user and his domain are linked to different Tenant resources;"
    )

    parser.add_option(
        "--dry-run",
        dest="dry_run",
        action="store_true",
        help="Dry-run mode: count affected users and create a report only")
    (options, args) = parser.parse_args()

    if not options.app_instance_id:
        parser.print_help()
        raise Exception(
            "The required parameter 'app-instance-id' is not specified.")
    elif not options.mode:
        parser.print_help()
        raise Exception("Required parameter 'mode' is not specified.")

    else:
        # init globals
        date_for_file_name = time.strftime("%Y%m%d-%H%M%S")
        logfile_name = "./fixUserAndDomains_" + date_for_file_name + "_O365_instance_" + str(
            options.app_instance_id) + ".log"
        format_str = "%(asctime)s   %(levelname)s   %(message)s"
        logging.basicConfig(filename=logfile_name,
                            level=logging.DEBUG,
                            format=format_str)

        initEnv()
        api = openapi.OpenAPI()
        aps_api = apsapi.API(getApsApiUrl())
        appInstanceToken = getAppInstanceToken(options.app_instance_id, api)

        dtStart = datetime.datetime.now()

        instanceUsersCount = countInstanceResources(
            api, aps_api, appInstanceToken, options.app_instance_id,
            O365_APS_TYPE_USER
        )  # <-- count instance users, using the response headers
        print "Instance users total: ", instanceUsersCount

        # If we need to fix users by Office365/Tenant APS resource consistence:
        if options.mode == 'fixByTenantUID':
            affectedUsers = findAffectedUsersWrongDomainTenant(
                api, aps_api, appInstanceToken, options.app_instance_id)
            print affectedUsers

        # allDomainMap1 = findAffectedUsersWrongDomainTenant(api, aps_api, appInstanceToken, options.app_instance_id)
        # print allDomainMap1
        #findAffectedUsersWrongDomainTenant(api, aps_api, appInstanceToken, options.app_instance_id)
        # print affectedUsersMap1

        if options.mode == 'fixByDomainName' and (instanceUsersCount >= 0):
            affectedUsers = findAffectedUsers(
                options.app_instance_id
            )  #<-- find all affected users, where UPN does not match domain linked
Example #5
0
def main():
    parser = OptionParser(
        version=VERSION,
        usage=
        "\nFind users whose UPN differs from domain name they are linked with. Fix by linking them to a correct domain in scope of the same subscription.\n\n  Usage: %prog --app-instance-id ID [--dry-run]"
    )
    parser.add_option("--app-instance-id",
                      dest="app_instance_id",
                      type="int",
                      help="Office 365 APS 2.0 Application Instance ID")
    #parser.add_option("--mode", dest="mode", type="string", default=None,
    #                  help="Script mode. Possible values: \n fixByDomainName - fix User <-> Domain links when login subdomain part does not match linked domain name; \n fixByTenantUID - fix User <-> Domain links when user and his domain are linked to different Tenant resources;")
    parser.add_option(
        "--dry-run",
        dest="dry_run",
        action="store_true",
        help="Dry-run mode: count affected users and create a report only")

    (options, args) = parser.parse_args()

    if not options.app_instance_id:
        parser.print_help()
        raise Exception(
            "The required parameter 'app-instance-id' is not specified.")

    else:
        # init globals
        date_for_file_name = time.strftime("%Y%m%d-%H%M%S")
        logfile_name = "./fixUserDomainLinks_" + date_for_file_name + "_O365_instance_" + str(
            options.app_instance_id) + ".log"
        format_str = "%(asctime)s   %(levelname)s   %(message)s"
        logging.basicConfig(filename=logfile_name,
                            level=logging.DEBUG,
                            format=format_str)

        initEnv()
        api = openapi.OpenAPI()
        aps_api = apsapi.API(getApsApiUrl())
        appInstanceToken = getAppInstanceToken(options.app_instance_id, api)

        dtStart = datetime.datetime.now()

        instanceUsersCount = countInstanceResources(
            api, aps_api, appInstanceToken, options.app_instance_id,
            O365_APS_TYPE_USER
        )  # <-- count instance users, using the response headers

        log(
            " --- Processing application instance " +
            str(options.app_instance_id) + "---\n", logging.INFO, True)
        affectedUsers = findAllAffectedUsers(api, aps_api, appInstanceToken,
                                             options.app_instance_id)
        log(" \n", logging.INFO, True)
        log(
            " --- SUMMARY about application instance " +
            str(options.app_instance_id) + "---\n", logging.INFO, True)
        log(" Total instance users: " + str(instanceUsersCount), logging.INFO,
            True)
        log(" Total affected users: " + str(len(affectedUsers)), logging.INFO,
            True)
        log(" \n", logging.INFO, True)
        log(
            " Affected users are (User APS UID \ Correct Domain APS UID, if any): ",
            logging.INFO, True)
        for user in affectedUsers.items():
            log(user, logging.INFO, True)
        log(" \n", logging.INFO, True)
        if not options.dry_run:  # <----- dry_run option
            log(" --- Fixing affected users. ", logging.INFO, True)
            for key, value in affectedUsers.items():
                if "NO such" not in value:
                    #log("Trying to fix user: "******"Dry-run mode, links were not fixed actually. ", logging.INFO,
                True)

        totalExecutionTime = TimeProfiler.convertTimedelta2Milliseconds(
            datetime.datetime.now() - dtStart)
        log(" \n", logging.INFO, True)
        log(
            " Total execution time: " + str(totalExecutionTime) +
            " milliseconds.\n", logging.INFO, True)