Exemple #1
0
class _RLock(object):
    def __init__(self):
        self._lock = ReentrantLock()
        self.__owner = None

    def acquire(self, blocking=1):
        if blocking:
            self._lock.lock()
            self.__owner = currentThread()
            return True
        else:
            return self._lock.tryLock()

    def __enter__(self):
        self.acquire()
        return self

    def release(self):
        assert self._lock.isHeldByCurrentThread(), \
            "release() of un-acquire()d lock"
        self.__owner = None
        self._lock.unlock()

    def __exit__(self, t, v, tb):
        self.release()

    def locked(self):
        return self._lock.isLocked()

    def _is_owned(self):
        return self._lock.isHeldByCurrentThread()
Exemple #2
0
class Sf_Lock():
    def __init__(self):
        self.lock_=ReentrantLock()
    
    def lock(self):
        self.lock_.lock()
        
    def unlock(self):
        self.lock_.unlock()
        
    def __enter__(self):
        self.lock()
        return self
    
    def __exit__(self, type, value, traceback):
        self.unlock()
Exemple #3
0
class Sf_Lock():
    def __init__(self):
        self.lock_=ReentrantLock()
    
    def lock(self):
        self.lock_.lock()
        
    def unlock(self):
        self.lock_.unlock()
        
    def __enter__(self):
        self.lock()
        return self
    
    def __exit__(self, type, value, traceback):
        self.unlock()
Exemple #4
0
class Mpc:
    def __init__(self, mpcExec="mpc", host=None, port=None):
        self.mpcExec = mpcExec
        self.allTags = [
            "artist", "album", "title", "track", "name", "genre", "date",
            "composer", "performer", "comment", "disc", "file"
        ]
        self.tags = [
            "artist", "album", "title", "track", "genre", "date", "disc",
            "file"
        ]
        self.separator = "\t"
        self.host = host
        self.port = port
        self.eventLoopProcess = None
        self.lock = ReentrantLock(True)
        self.format = self.separator.join(
            list(map(lambda tag: "%{}%".format(tag), self.tags)))

    def __createProcess(self):
        args = []
        if self.host:
            args.append("-h")
            args.append(self.host)
        if self.port:
            args.append("-p")
            args.append(str(self.port))

        return sponge.process(self.mpcExec).arguments(args)

    def __execute(self, *argv):
        self.lock.lock()
        try:
            process = self.__createProcess().arguments(
                argv).outputAsString().errorAsException().run()

            return process.outputString
        finally:
            self.lock.unlock()

    def __num(self, name, value, raiseOnError):
        try:
            return int(value) if value else None
        except ValueError:
            if raiseOnError:
                raise Exception("Incorrect value '{}' for {}".format(
                    value, name))
            else:
                return None

    def validatePrerequisites(self):
        # Check if the mpc is installed.
        self.__createProcess().outputAsString().errorAsString(
        ).exceptionOnExitCode(False).run()

    # Data type operations.
    def createSongValue(self, songSpec):
        tagValues = songSpec.split(self.separator)
        if len(tagValues) != len(self.tags):
            return None
        song = {}
        for i in range(len(self.tags)):
            song[self.tags[i]] = tagValues[i].strip() if tagValues[i] else None
        return song

    # Admin operations.

    def getServerVersion(self):
        return self.__execute("version")

    def getStatus(self):
        return self.__execute("status")

    def isStatusOk(self, status):
        if status is None or len(
                status.strip()) == 0 or self.isStatusNotPlaying(status):
            return False
        return True

    def getStats(self):
        return self.__execute("stats")

    def isConnected(self):
        error = self.__createProcess().outputAsString().errorAsString(
        ).exceptionOnExitCode(False).run().errorString
        if error and (error.endswith("Cannot assign requested address")
                      or error.endswith("Connection refused")):
            return False
        return True

    # Playlist operations.

    def addFile(self, file):
        self.__execute("add", file)

    def addFileAsRecord(self, file):
        self.addFile(file["file"])

    def addFiles(self, files):
        self.__createProcess().arguments("add").inputAsString(
            "\n".join(files)).run()

    def addFilesAsRecords(self, files):
        self.__createProcess().arguments("add").inputAsString("\n".join(
            list(map(lambda file: file["file"], files)))).run()

    def addAndPlayFiles(self, files, autoPlay, replacePlaylist=False):
        if len(files) == 0:
            return
        if replacePlaylist:
            self.clearPlaylist()
        self.addFileAsRecord(files[0])
        if autoPlay:
            # Play immediately after inserting the first file
            self.play()
        if len(files) > 1:
            self.addFilesAsRecords(files[1:])

    def playPlaylistEntry(self, position):
        self.__execute("play", str(position))

    def clearPlaylist(self):
        self.__execute("clear")

    def getPlaylist(self):
        return self.__execute("playlist").splitlines()

    def getCurrentPlaylistPositionAndSize(self, status=None):
        if status is None:
            status = self.getStatus()
        lines = status.splitlines()
        if len(lines) == 3:
            matched = re.match(r"\[.*\]\s*#(\d+)/(\d+)\s*.*", lines[1])
            if matched is not None and len(matched.groups()) == 2:
                (position, size) = matched.groups()
                return (int(position) if position else None,
                        int(size) if size else None)
        return (None, None)

    def getCurrentPlaylistPosition(self, status=None):
        return self.getCurrentPlaylistPositionAndSize(status)[0]

    def getPlaylistSize(self, status=None):
        return self.getCurrentPlaylistPositionAndSize(status)[1]

    def moveUpPlaylistEntry(self, position):
        if position > 1:
            self.__execute("move", str(position), str(position - 1))

    def moveDownPlaylistEntry(self, position):
        self.__execute("move", str(position), str(position + 1))

    def removePlaylistEntry(self, position):
        self.__execute("del", str(position))

    # Library operations.
    def listAllFiles(self):
        return self.__execute("-f", "%file%", "search", "genre",
                              "").splitlines()

    def getFiles(self, parentDir=None):
        """ Returns list of tuples (file, isDir)
        """
        format = self.separator.join(
            list(map(lambda tag: "%{}%".format(tag), ["file", "title"])))
        lines = sorted((self.__execute("-f", format, "ls", parentDir)
                        if parentDir else self.__execute("ls")).splitlines())

        def createEntry(line):
            elements = line.split("\t")
            if len(elements) == 2:
                return (elements[0], False)
            else:
                return (elements[0], True)

        return list(map(createEntry, lines))

    def refreshDatabase(self):
        self.__execute("update")

    def searchFiles(self,
                    aArtist,
                    aAlbum,
                    aTitle,
                    aGenre,
                    aMinYear,
                    aMaxYear,
                    useSimpleRegexp=False):
        minYear = self.__num("minYear", aMinYear, True)
        maxYear = self.__num("maxYear", aMaxYear, True)
        selectedFiles = []
        fileEntries = self.__execute("-f", self.format, "search", "artist",
                                     aArtist if aArtist else "", "album",
                                     aAlbum if aAlbum else "", "title",
                                     aTitle if aTitle else "", "genre",
                                     aGenre if aGenre else "").splitlines()
        for fileEntry in fileEntries:
            tagValues = fileEntry.split(self.separator)
            file = {}
            for i in range(len(self.tags)):
                file[self.tags[i]] = tagValues[i]
            file["date"] = self.__num("date", file["date"],
                                      False) if ("date" in self.tags) else None

            if (minYear is None or file["date"] and file["date"] >= minYear
                ) and (maxYear is None
                       or file["date"] and file["date"] <= maxYear):
                selectedFiles.append(file)

        if "file" in self.tags:
            selectedFiles.sort(key=lambda file: file["file"])

        return selectedFiles

    # Player operations.

    def play(self, position=1, waitFor=False):
        process = self.__createProcess().arguments("play", str(position)).run()
        if waitFor:
            process.waitFor()

    def getCurrentSong(self):
        return self.createSongValue(
            self.__execute("current", "-f", self.format))

    def getSongLabel(self, song):
        label = None
        if song:
            if song["artist"] is not None and song["title"] is not None:
                label = u"{} - {}".format(song["artist"], song["title"])
            else:
                label = os.path.basename(song["file"])
        return label

    def seekByPercentage(self, value):
        return self.__execute("seek", str(value) + "%")

    # Returns a 3-element tuple.
    def getPositionTuple(self, status):
        lines = status.splitlines()
        if len(lines) == 3:
            matched = re.match(r".+ (.*)/(.*) \((.*)%\)", lines[1])
            if matched is not None and len(matched.groups()) == 3:
                return matched.groups()
        return None

    def getPositionByPercentage(self):
        return self.getPositionByPercentage(self.getStatus())

    def getPositionByPercentage(self, status):
        position = self.getPositionTuple(status)
        return int(position[2]) if position else None

    def getTimeStatus(self, status):
        position = self.getPositionTuple(status)
        return position[0] + "/" + position[1] if position else None

    def getPlay(self, status):
        lines = status.splitlines()
        return len(lines) == 3 and re.match(r"\[playing\] .*",
                                            lines[1]) is not None

    def togglePlay(self, play):
        return self.__execute("play" if play else "pause")

    def prev(self):
        status = self.__execute("prev")
        if self.isStatusNotPlaying(status):
            return None
        return status

    def next(self):
        status = self.__execute("next")
        if self.isStatusNotPlaying(status):
            return None
        return status

    def isStatusNotPlaying(self, status):
        lines = status.splitlines()
        return len(lines) == 1 and re.match(r".* Not playing .*",
                                            lines[0]) is not None

    def getPlayingState(self, status):
        lines = status.splitlines()
        if len(lines) == 3:
            matched = re.match(r"\[(.*)\]\s*#\d+/.*", lines[1])
            if matched is not None and len(matched.groups()) == 1:
                return matched.groups()[0]
        return None

    def isStatusPlayingOrPaused(self, status):
        playingState = self.getPlayingState(status)
        return playingState is not None and (playingState == "playing"
                                             or playingState == "paused")

    def getVolume(self):
        return self.getVolume(self.getStatus())

    def getVolume(self, status):
        lines = status.splitlines()
        if len(lines) > 0:
            matched = re.match(r"volume:\s*(.+)% .*", lines[-1])
            if matched is not None and len(matched.groups()) == 1:
                volume = matched.groups()[0]
                return int(volume) if volume else None
        return None

    def setVolume(self, volume):
        return self.__execute("volume", str(volume))

    # Events.

    def __onMpdEvent(self, event):
        sponge.event("mpdNotification").send()
        sponge.event("mpdNotification_" + event).send()

    def startEventLoop(self):
        self.eventLoopProcess = self.__createProcess().arguments(
            "idleloop").outputAsConsumer(
                PyConsumer(lambda line: self.__onMpdEvent(line))
            ).outputLoggingConsumerNone().errorAsConsumer().runAsync()

    def stopEventLoop(self):
        if self.eventLoopProcess:
            self.eventLoopProcess.destroy()
            self.eventLoopProcess = None
class PersonAuthentication(PersonAuthenticationType):

    STEP_SPLASH = 1
    STEP_CHOOSER = 2
    STEP_1FA = 3
    STEP_COLLECT = 4
    STEP_2FA = 5
    STEP_FIDO_AUTH = 6
    STEP_FIDO_REGISTER = 7
    STEP_FIDO_CONFIRM = 8

    def __init__(self, currentTimeMillis):
        self.currentTimeMillis = currentTimeMillis

    def init(self, customScript, configurationAttributes):

        if REMOTE_DEBUG:
            pydevd.settrace('localhost',
                            port=5678,
                            stdoutToServer=True,
                            stderrToServer=True)

        self.name = customScript.getName()

        print("%s: Initializing" % self.name)

        # Get the list of providers and parse into a set for quick membership tests
        providersParam = configurationAttributes.get("providers").getValue2()
        if providersParam is None:
            print("%s: Providers parameter is missing from config!" %
                  self.name)
            return False
        else:
            self.providers = set(
                [item.strip() for item in providersParam.split(",")])

        # Get the defaults for RP business rule & UI configuration
        defaultsParam = configurationAttributes.get("rp_defaults").getValue2()
        if defaultsParam is None:
            print("%s: RP defaults (rp_defaults) are missing from config!" %
                  self.name)
            return False
        else:
            try:
                self.rpDefaults = json.loads(defaultsParam)
            except ValueError:
                print("%s: failed to parse RP defaults!" % self.name)
                return False

        # Keep an in-memory cache of RP Configs
        self.rpConfigCache = {}

        self.passport = passport.Passport()
        self.passport.init(configurationAttributes, self.name)

        # Configure FIDO2
        if configurationAttributes.containsKey("fido2_server_uri"):
            print("%s: Enabling FIDO2 support" % self.name)
            self.fido2_server_uri = configurationAttributes.get(
                "fido2_server_uri").getValue2()
            self.fido2_domain = None
            if configurationAttributes.containsKey("fido2_domain"):
                self.fido2_domain = configurationAttributes.get(
                    "fido2_domain").getValue2()
            self.metaDataLoaderLock = ReentrantLock()
            self.fidoMetaDataConfiguration = None

        self.account = account.Account()

        self.telemetryClient = TelemetryClient()

        print("%s: Initialized" % self.name)
        return True

    def destroy(self, configurationAttributes):
        print("%s: Destroyed" % self.name)
        return True

    def getApiVersion(self):
        return 11

    def getAuthenticationMethodClaims(self, requestParameters):
        return None

    def isValidAuthenticationMethod(self, usageType, configurationAttributes):
        # Inject dependencies
        identity = CdiUtil.bean(Identity)

        client = self.getClient(identity.getSessionId())
        defaultAcrValues = client.getDefaultAcrValues()
        # If any default ACR values are explicitly configured on the client,
        # then don't allow any others
        if defaultAcrValues is None or self.name in defaultAcrValues:
            return True
        else:
            return False

    def getAlternativeAuthenticationMethod(self, usageType,
                                           configurationAttributes):
        return None

    def getExtraParametersForStep(self, configurationAttributes, step):
        return Arrays.asList(
            "stepCount",  # Used to complete the workflow
            "provider",  # The 1FA provider chosen by the user
            "abort",  # Used to trigger error abort back to the RP
            "forceAuthn",  # Used to force authentication when prompt=login
            "userId",  # Used to keep track of the user across multiple requests
            "mfaId",  # Used to bind a 2nd factor credential into the session
            "mfaFallback")  # Used to bypass Fido authnetication

    def prepareForStep(self, configurationAttributes, requestParameters, step):

        if REMOTE_DEBUG:
            pydevd.settrace('localhost',
                            port=5678,
                            stdoutToServer=True,
                            stderrToServer=True)

        # Inject dependencies
        identity = CdiUtil.bean(Identity)
        facesResources = CdiUtil.bean(FacesResources)
        facesService = CdiUtil.bean(FacesService)
        userService = CdiUtil.bean(UserService)

        session = identity.getSessionId()
        sessionAttributes = session.getSessionAttributes()
        externalContext = facesResources.getFacesContext().getExternalContext()
        uiLocales = sessionAttributes.get(AuthorizeRequestParam.UI_LOCALES)

        rpConfig = self.getRPConfig(session)
        clientUri = self.getClientUri(session)

        externalContext.addResponseHeader(
            "Content-Security-Policy",
            "default-src 'self' https://www.canada.ca; font-src 'self' https://fonts.gstatic.com https://use.fontawesome.com https://www.canada.ca; style-src 'self' 'unsafe-inline'; style-src-elem 'self' 'unsafe-inline' https://use.fontawesome.com https://fonts.googleapis.com https://www.canada.ca; script-src 'self' 'unsafe-inline' https://www.canada.ca https://ajax.googleapis.com; connect-src 'self' https://*.fjgc-gccf.gc.ca"
        )

        if step == 1:
            httpRequest = externalContext.getRequest()
            # Bookmark detection
            #if httpRequest.getHeader("referer") is None:
            #    if StringHelper.isNotEmpty(clientUri):
            #        facesService.redirectToExternalURL(clientUri)
            #        return True
            #    else:
            #        print("%s: prepareForStep. clientUri is missing for client %s" % (self.name, self.getClient(session).getClientName()))
            #        return False

            # forceAuthn workaround
            prompt2 = httpRequest.getParameter("prompt2")
            if prompt2 == "login":
                identity.setWorkingParameter("forceAuthn", True)

            # step could actually be 2, or 3
            if uiLocales is not None:
                if len(self.providers) > 1:
                    step = self.STEP_CHOOSER
                else:
                    step = self.STEP_1FA

        if identity.getWorkingParameter("abort"):  # Back button workaround
            # Obtain the client URI of the current client from the client configuration
            if len(self.providers
                   ) == 1:  # Pass through, so send them back to the client
                if StringHelper.isNotEmpty(clientUri):
                    facesService.redirectToExternalURL(clientUri)
                    return True
                else:
                    print(
                        "%s: prepareForStep. clientUri is missing for client %s"
                        % (self.name, self.getClient(session).getClientName()))
                    return False
            else:  # reset the chooser
                identity.setWorkingParameter("provider", None)

        if step == self.STEP_CHOOSER:
            # Prepare for chooser page customization.
            for param in ["layout", "chooser", "content"]:
                identity.setWorkingParameter(param, rpConfig[param])

        elif step in {self.STEP_1FA, self.STEP_COLLECT,
                      self.STEP_2FA}:  # Passport

            passportOptions = {
                "ui_locales": uiLocales,
                "exp": int(time.time()) + 60
            }

            if step in {self.STEP_1FA, self.STEP_COLLECT}:
                provider = identity.getWorkingParameter("provider")
                if provider is None and len(
                        self.providers
                ) == 1:  # Only one provider. Direct Pass-through
                    provider = next(iter(self.providers))
                    identity.setWorkingParameter("provider", provider)

            if step == self.STEP_1FA:
                # Coordinate single-sign-on (SSO)
                maxAge = (sessionAttributes.get(AuthorizeRequestParam.MAX_AGE)
                          or self.getClient(session).getDefaultMaxAge())
                if (identity.getWorkingParameter("forceAuthn") or
                    ("GCCF" in self.passport.getProvider(provider)["options"]
                     and maxAge < 1200)
                    ):  # 1200 is 20 minutes, the SSO timeout on GCKey and CBS
                    passportOptions["forceAuthn"] = "true"

            elif step == self.STEP_COLLECT:
                collect = rpConfig.get("collect")
                if collect is not None:
                    passportOptions["allowCreate"] = rpConfig.get(
                        "allowCreate") or "false"
                    passportOptions["spNameQualifier"] = collect
                else:  # This should never happen
                    print(
                        "%s. prepareForStep: collection entityID is missing" %
                        self.name)
                    return False

            elif step == self.STEP_2FA:
                provider = rpConfig.get("mfaProvider")
                if provider is None:
                    print("%s: prepareForStep. mfaProvider is missing!" %
                          self.name)
                    return False
                mfaId = identity.getWorkingParameter("mfaId")
                if mfaId is None:
                    print("%s: prepareForStep. mfaId is missing!" % self.name)
                    return False
                else:
                    passportOptions["login_hint"] = mfaId
                    # The following parameters are redundant, but currently required by the 2ndFaaS
                    passportOptions[
                        "redirect_uri"] = self.passport.getProvider(
                            provider)["callbackUrl"]
                    passportOptions["response_type"] = "code"
                    passportOptions["scope"] = "openid profile"

            # Set the abort flag to handle back button
            identity.setWorkingParameter("abort", True)
            # Send the request to passport
            passportRequest = self.passport.createRequest(
                provider, passportOptions)
            facesService.redirectToExternalURL(passportRequest)

        elif step in {self.STEP_FIDO_REGISTER, self.STEP_FIDO_AUTH}:
            userId = identity.getWorkingParameter("userId")
            metaDataConfiguration = self.getFidoMetaDataConfiguration()

            if step == self.STEP_FIDO_REGISTER:
                try:
                    attestationService = Fido2ClientFactory.instance(
                    ).createAttestationService(metaDataConfiguration)
                    attestationRequest = json.dumps(
                        {
                            'username': userId,
                            'displayName': userId,
                            'attestation': 'direct',
                            'timeout': 120000,
                            'userVerification': 'discouraged'
                        },
                        separators=(',', ':'))
                    attestationResponse = attestationService.register(
                        attestationRequest).readEntity(java.lang.String)
                except ClientErrorException as ex:
                    print(
                        "%s. Prepare for step. Failed to start FIDO2 attestation flow. Exception:"
                        % self.name,
                        sys.exc_info()[1])
                    return False
                identity.setWorkingParameter(
                    "fido2_attestation_request",
                    ServerUtil.asJson(attestationResponse))
                print(ServerUtil.asJson(attestationResponse))

            elif step == self.STEP_FIDO_AUTH:
                userId = identity.getWorkingParameter("userId")
                metaDataConfiguration = self.getFidoMetaDataConfiguration()
                fidoDeviceCount = userService.countFidoAndFido2Devices(
                    userId, self.fido2_domain)
                try:
                    assertionService = Fido2ClientFactory.instance(
                    ).createAssertionService(metaDataConfiguration)
                    assertionRequest = json.dumps(
                        {
                            'username': userId,
                            'timeout': 120000,
                            'userVerification': 'discouraged'
                        },
                        separators=(',', ':'))
                    assertionResponse = assertionService.authenticate(
                        assertionRequest).readEntity(java.lang.String)
                except ClientErrorException as ex:
                    print(
                        "%s. Prepare for step. Failed to start FIDO2 assertion flow. Exception:"
                        % self.name,
                        sys.exc_info()[1])
                    return False
                identity.setWorkingParameter(
                    "fido2_assertion_request",
                    ServerUtil.asJson(assertionResponse))

        return True

    def authenticate(self, configurationAttributes, requestParameters, step):

        if REMOTE_DEBUG:
            pydevd.settrace('localhost',
                            port=5678,
                            stdoutToServer=True,
                            stderrToServer=True)

        # Inject dependencies
        facesService = CdiUtil.bean(FacesService)
        identity = CdiUtil.bean(Identity)
        languageBean = CdiUtil.bean(LanguageBean)
        userService = CdiUtil.bean(UserService)
        authenticationService = CdiUtil.bean(AuthenticationService)

        session = identity.getSessionId()
        sessionAttributes = session.getSessionAttributes()

        # Clear the abort flag
        identity.setWorkingParameter("abort", False)

        if requestParameters.containsKey("user"):
            # Successful response from passport
            return self.authenticatePassportUser(configurationAttributes,
                                                 requestParameters, step)

        elif requestParameters.containsKey("failure"):
            # This means that passport returned an error
            if step <= self.STEP_1FA:  # User Cancelled during login
                if len(self.providers
                       ) == 1:  # One provider. Redirect back to the RP
                    facesService.redirectToExternalURL(
                        self.getClientUri(session))
                else:  # Clear the previous choice to re-display the chooser
                    # locale = ServerUtil.getFirstValue(requestParameters, "ui_locale") # TODO: Update passport to send language onerror
                    # sessionAttributes.put(AuthorizeRequestParam.UI_LOCALES, locale)
                    identity.setWorkingParameter("provider", None)
            elif (
                    step == self.STEP_COLLECT and ServerUtil.getFirstValue(
                        requestParameters, "failure") == "InvalidNameIDPolicy"
            ):  # PAI Collection failed. If it's a SAML SP, Create a new SIC PAI
                spNameQualifier = sessionAttributes.get("entityId")
                if spNameQualifier is not None:
                    user = userService.getUser(
                        identity.getWorkingParameter("userId"), "uid",
                        "persistentId")
                    user = self.account.addSamlSubject(user, spNameQualifier)
                    userService.updateUser(user)
                if self.getNextStep(configurationAttributes, requestParameters,
                                    step) < 0:
                    return authenticationService.authenticate(
                        identity.getWorkingParameter("userId"))

            elif step == self.STEP_2FA:  # 2FA Failed. Redirect back to the RP
                facesService.redirectToExternalURL(self.getClientUri(session))
                return False
            else:
                print("%s: Invalid passport failure in step %s." %
                      (self.name, step))
                return False

        elif requestParameters.containsKey("lang"):
            # Manually selected language
            locale = self.getFormButton(requestParameters)
            if locale in {"en-CA", "fr-CA"}:
                languageBean.setLocaleCode(locale)
                sessionAttributes.put(AuthorizeRequestParam.UI_LOCALES, locale)
            else:
                return False

        elif requestParameters.containsKey("chooser"):
            # Chooser page
            choice = self.getFormButton(requestParameters)
            if choice == "gckeyregister": choice = "gckey"  #Hack!
            if choice in self.providers:
                identity.setWorkingParameter("provider", choice)
            else:
                print("%s: Invalid provider choice: %s." % (self.name, choice))
                return False

        elif requestParameters.containsKey("fido2Registration"):
            return self.registerFido2(identity.getWorkingParameter("userId"),
                                      requestParameters)

        elif requestParameters.containsKey("fido2Authentication"):
            return self.authenticateFido2(
                identity.getWorkingParameter("userId"), requestParameters)

        elif requestParameters.containsKey("fido2Nav"):
            option = self.getFormButton(requestParameters)
            if option == "use2FA":
                identity.setWorkingParameter("mfaFallback", True)
            elif option == "recover":
                user = userService.getUser(
                    identity.getWorkingParameter("userId"), "inum", "uid")
                self.account.removeFido2Registrations(user)
            elif option in {"decline", "continue"}:
                return authenticationService.authenticate(
                    identity.getWorkingParameter("userId"))

        else:  # Invalid response
            print("%s: Invalid form submission: %s." %
                  (self.name, requestParameters.keySet().toString()))
            return False

        return True

    def authenticatePassportUser(self, configurationAttributes,
                                 requestParameters, step):

        if REMOTE_DEBUG:
            pydevd.settrace('localhost',
                            port=5678,
                            stdoutToServer=True,
                            stderrToServer=True)

        # Inject dependencies
        userService = CdiUtil.bean(UserService)
        authenticationService = CdiUtil.bean(AuthenticationService)
        identity = CdiUtil.bean(Identity)
        languageBean = CdiUtil.bean(LanguageBean)

        session = identity.getSessionId()
        sessionAttributes = session.getSessionAttributes()
        rpConfig = self.getRPConfig(session)

        externalProfile = self.passport.handleResponse(requestParameters)
        if externalProfile is None:
            return False
        provider = externalProfile["provider"]

        # Can't trust the step parameter
        if identity.getWorkingParameter("userId") is None:
            step = self.STEP_1FA
        elif provider == identity.getWorkingParameter("provider"):
            step = self.STEP_COLLECT
        elif provider == rpConfig.get("mfaProvider"):
            step = self.STEP_2FA

        if step == self.STEP_1FA:
            if provider not in self.providers:
                # Unauthorized provider!
                return False

            provider = externalProfile["provider"]
            if step == self.STEP_1FA and provider not in self.providers:
                # Unauthorized provider!
                return False
            else:
                providerInfo = self.passport.getProvider(provider)

            if providerInfo["GCCF"]:
                sessionAttributes.put("authnInstant",
                                      externalProfile["authnInstant"][0])
                sessionAttributes.put("persistentId",
                                      externalProfile["persistentId"][0])
                sessionAttributes.put("sessionIndex",
                                      externalProfile["sessionIndex"][0])

            # Find or create the user account
            user = self.account.find(externalProfile)
            if user is None:
                user = self.account.create(externalProfile)
                newUser = True
            else:
                newUser = False
                userChanged = False
            identity.setWorkingParameter("userId", user.getUserId())

            # Update the preferred language if it has changed
            locale = ServerUtil.getFirstValue(requestParameters, "locale")
            if locale:
                locale += "-CA"
                languageBean.setLocaleCode(locale)
                sessionAttributes.put(AuthorizeRequestParam.UI_LOCALES, locale)
            else:  # Language cookie was blocked
                locale = sessionAttributes.get(
                    AuthorizeRequestParam.UI_LOCALES)

            if locale != user.getAttribute("locale", True, False):
                user.setAttribute("locale", locale, False)
                userChanged = True

            # If it's a SAML RP without collection enabled, then create our own PAI
            spNameQualifier = sessionAttributes.get("entityId")
            if spNameQualifier is not None and "collect" not in rpConfig and self.account.getSamlSubject(
                    user, spNameQualifier) is None:
                user = self.account.addSamlSubject(user, spNameQualifier)
                userChanged = True

            # IF MFA is enabled, grab the mfaId, or create if needed
            if rpConfig.get("mfaProvider"):
                mfaId = self.account.getExternalUid(user, "mfa")
                if mfaId is None:
                    mfaId = self.account.addExternalUid(user, "mfa")
                    userChanged = True
                identity.setWorkingParameter("mfaId", mfaId)

            if newUser:
                userService.addUser(user, True)
            elif userChanged:
                userService.updateUser(user)

            if self.getNextStep(configurationAttributes, requestParameters,
                                step) < 0:
                return authenticationService.authenticate(
                    identity.getWorkingParameter("userId"))

        elif step == self.STEP_COLLECT:
            user = userService.getUser(identity.getWorkingParameter("userId"),
                                       "inum", "uid", "persistentId")
            # Validate the session first
            if externalProfile["sessionIndex"][0] != sessionAttributes.get(
                    "sessionIndex"):
                print(
                    "%s: IDP session missmatch during PAI collection for user %s."
                    % (self.name, identity.getWorkingParameter("userId")))
                return False

            collect = rpConfig.get("collect")
            if collect is None:  # This should never happen
                print("%s. authenticateUser: collection entityID is missing" %
                      (self.name))
                return False

            # Collect the SAML PAI
            spNameQualifier, nameQualifier, nameId = tuple(
                externalProfile["persistentId"][0].split("|"))
            if spNameQualifier == "undefined":
                spNameQualifier = collect
            if nameQualifier == "undefined":
                nameQualifier = externalProfile["issuer"][0]
            if not self.account.getSamlSubject(
                    user, spNameQualifier):  # unless one already exists
                user = self.account.addSamlSubject(user, spNameQualifier,
                                                   nameQualifier, nameId)
            userService.updateUser(user)

            # construct an OIDC pairwise subject using the SAML PAI
            client = self.getClient(session)
            if not self.account.getOpenIdSubject(
                    user, client):  # unless one already exists
                provider = identity.getWorkingParameter("provider")
                self.account.addOpenIdSubject(user, client, provider + nameId)

            if self.getNextStep(configurationAttributes, requestParameters,
                                step) < 0:
                return authenticationService.authenticate(
                    identity.getWorkingParameter("userId"))

        elif step == self.STEP_2FA:
            user = userService.getUser(identity.getWorkingParameter("userId"),
                                       "uid", "oxExternalUid", "locale")
            mfaExternalId = self.account.getExternalUid(user, "mfa")
            if externalProfile.get("externalUid").split(":",
                                                        1)[1] != mfaExternalId:
                # Got the wrong MFA PAI. Authentication failed!
                return False

            # Accept locale from the 2nd-factor CSP
            locale = externalProfile.get("locale")[0]
            if locale:
                languageBean.setLocaleCode(locale)
                if locale != user.getAttribute("locale", True, False):
                    user.setAttribute("locale", locale, False)
                    userService.updateUser(user)

            userId = identity.getWorkingParameter("userId")
            if self.getNextStep(configurationAttributes, requestParameters,
                                step) < 0:
                return authenticationService.authenticate(
                    identity.getWorkingParameter("userId"))

        return True

    def registerFido2(self, userId, requestParameters):

        if REMOTE_DEBUG:
            pydevd.settrace('localhost',
                            port=5678,
                            stdoutToServer=True,
                            stderrToServer=True)

        userService = CdiUtil.bean(UserService)
        authenticationService = CdiUtil.bean(AuthenticationService)
        identity = CdiUtil.bean(Identity)
        session = identity.getSessionId()

        tokenResponse = ServerUtil.getFirstValue(requestParameters,
                                                 "fido2Registration")
        print("%s. Authenticate. Got fido2 registration response: %s" %
              (self.name, tokenResponse))
        metaDataConfiguration = self.getFidoMetaDataConfiguration()
        attestationService = Fido2ClientFactory.instance(
        ).createAttestationService(metaDataConfiguration)
        attestationStatus = attestationService.verify(tokenResponse)

        if attestationStatus.getStatus() != Response.Status.OK.getStatusCode():
            print(
                "%s. Authenticate. Got invalid registration status from Fido2 server"
                % self.name)
            return False

        return authenticationService.authenticate(
            identity.getWorkingParameter("userId"))

    def authenticateFido2(self, userId, requestParameters):

        if REMOTE_DEBUG:
            pydevd.settrace('localhost',
                            port=5678,
                            stdoutToServer=True,
                            stderrToServer=True)

        userService = CdiUtil.bean(UserService)
        authenticationService = CdiUtil.bean(AuthenticationService)
        identity = CdiUtil.bean(Identity)
        session = identity.getSessionId()

        tokenResponse = ServerUtil.getFirstValue(requestParameters,
                                                 "fido2Authentication")
        print("%s. Authenticate. Got fido2 authentication response: %s" %
              (self.name, tokenResponse))
        metaDataConfiguration = self.getFidoMetaDataConfiguration()
        assertionService = Fido2ClientFactory.instance(
        ).createAssertionService(metaDataConfiguration)
        assertionStatus = assertionService.verify(tokenResponse)
        authenticationStatusEntity = assertionStatus.readEntity(
            java.lang.String)

        if assertionStatus.getStatus() != Response.Status.OK.getStatusCode():
            print(
                "%s. Authenticate. Got invalid authentication status from Fido2 server"
                % self.name)
            return False

        return authenticationService.authenticate(
            identity.getWorkingParameter("userId"))

    def getPageForStep(self, configurationAttributes, step):

        if REMOTE_DEBUG:
            pydevd.settrace('localhost',
                            port=5678,
                            stdoutToServer=True,
                            stderrToServer=True)

        # Inject dependencies
        identity = CdiUtil.bean(Identity)
        facesResources = CdiUtil.bean(FacesResources)

        session = identity.getSessionId()

        # Check for ui_locales
        uiLocales = None
        if session is None:  # No session yet
            facesContext = facesResources.getFacesContext()
            httpRequest = facesContext.getCurrentInstance().getExternalContext(
            ).getRequest()
            uiLocales = httpRequest.getParameter(
                AuthorizeRequestParam.UI_LOCALES)
        else:
            # Session exists.
            uiLocales = session.getSessionAttributes().get(
                AuthorizeRequestParam.UI_LOCALES)

        if uiLocales is not None:
            language = uiLocales[:2].lower()

        if step == 1 and uiLocales is not None:
            if len(self.providers) > 1:
                step = self.STEP_CHOOSER
            else:  # Direct pass-through
                step = self.STEP_1FA

        if step == self.STEP_SPLASH:
            return "/lang.xhtml"

        elif step == self.STEP_CHOOSER:  # Chooser page
            if language == "fr":
                return "/fr/choisir.xhtml"
            else:
                return "/en/select.xhtml"

        elif step in {self.STEP_1FA, self.STEP_COLLECT,
                      self.STEP_2FA}:  # Passport
            # identity.getWorkingParameters().remove("abort")
            return "/auth/passport/passportlogin.xhtml"

        elif step == self.STEP_FIDO_AUTH:  # FIDO Authentication
            if language == "fr":
                return "/fr/wa.xhtml"
            else:
                return "/en/wa.xhtml"

        elif step == self.STEP_FIDO_REGISTER:  # FIDO Reggistration
            if language == "fr":
                return "/fr/waregistrer.xhtml"
            else:
                return "/en/waregister.xhtml"

        elif step == self.STEP_FIDO_CONFIRM:  # FIDO Registration
            if language == "fr":
                return "/fr/wasucces.xhtml"
            else:
                return "/en/wasuccess.xhtml"

        else:
            print("%s. getPageForStep. Unexpected step # %s" %
                  (self.name, step))
            return "/error.xhtml"

    def getCountAuthenticationSteps(self, configurationAttributes):

        if REMOTE_DEBUG:
            pydevd.settrace('localhost',
                            port=5678,
                            stdoutToServer=True,
                            stderrToServer=True)

        identity = CdiUtil.bean(Identity)
        stepCount = identity.getWorkingParameter("stepCount")

        if stepCount is None:
            return 255  # not done yet
        else:
            return stepCount

    def gotoStep(self, step):
        identity = CdiUtil.bean(Identity)
        sessionAttributes = identity.getSessionId().getSessionAttributes()

        # Mark all previous steps as passed so the workflow can skip steps
        for i in range(1, step + 1):
            sessionAttributes.put("auth_step_passed_%s" % i, "true")
        return step

    def getNextStep(self, configurationAttributes, requestParameters, step):

        if REMOTE_DEBUG:
            pydevd.settrace('localhost',
                            port=5678,
                            stdoutToServer=True,
                            stderrToServer=True)

        # Inject dependencies
        userService = CdiUtil.bean(UserService)
        identity = CdiUtil.bean(Identity)
        session = identity.getSessionId()
        rpConfig = self.getRPConfig(session)
        provider = identity.getWorkingParameter("provider")
        if provider is not None:
            providerInfo = self.passport.getProvider(provider)

        originalStep = step
        if step == 1:
            # Determine if SPLASH, CHOOSER, or 1FA
            if requestParameters.containsKey("lang"):
                step = self.STEP_SPLASH
            elif requestParameters.containsKey("chooser"):
                step = self.STEP_CHOOSER
            elif requestParameters.containsKey(
                    "user") or requestParameters.containsKey("failure"):
                step = self.STEP_1FA

        if step == self.STEP_SPLASH:
            if len(self.providers) == 1:
                return self.gotoStep(self.STEP_1FA)
            else:
                return self.gotoStep(self.STEP_CHOOSER)

        if step == self.STEP_CHOOSER:
            if requestParameters.containsKey("lang"):
                return self.gotoStep(self.STEP_CHOOSER)  # Language toggle
            else:
                return self.gotoStep(self.STEP_1FA)

        userId = identity.getWorkingParameter("userId")
        if step == self.STEP_1FA:
            if requestParameters.containsKey("failure"):  # User cancelled
                return self.gotoStep(self.STEP_CHOOSER)
            elif requestParameters.containsKey(
                    "chooser"):  # User double-clicked
                return self.gotoStep(self.STEP_1FA)
            else:
                if providerInfo["GCCF"] and "collect" in rpConfig:
                    user = userService.getUser(userId, "persistentId")
                    if self.account.getSamlSubject(
                            user, rpConfig["collect"]
                    ) is None:  # SAML PAI collection
                        return self.gotoStep(self.STEP_COLLECT)

        if step in {self.STEP_1FA, self.STEP_COLLECT}:
            if rpConfig.get("fido") and userService.countFidoAndFido2Devices(
                    userId, self.fido2_domain) > 0:
                return self.gotoStep(self.STEP_FIDO_AUTH)
            if rpConfig.get("mfaProvider"):  # 2FA
                return self.gotoStep(self.STEP_2FA)

        if step == self.STEP_2FA:
            if rpConfig.get("fido") and userService.countFidoAndFido2Devices(
                    userId, self.fido2_domain) == 0:
                return self.gotoStep(self.STEP_FIDO_REGISTER)

        if step == self.STEP_FIDO_AUTH:
            if requestParameters.containsKey("lang"):
                return self.gotoStep(self.STEP_FIDO_AUTH)  # Language toggle
            elif not requestParameters.containsKey("fido2Authentication"):
                return self.gotoStep(
                    self.STEP_2FA
                )  # If the don't have thier authentiator then fallback to 2FA

        if step == self.STEP_FIDO_REGISTER:
            if requestParameters.containsKey("lang"):
                return self.gotoStep(
                    self.STEP_FIDO_REGISTER)  # Language toggle
            elif requestParameters.containsKey("fido2Registration"):
                return self.gotoStep(self.STEP_FIDO_CONFIRM)

        if step == self.STEP_FIDO_CONFIRM:
            if requestParameters.containsKey("lang"):
                return self.gotoStep(self.STEP_FIDO_CONFIRM)  # Language toggle

        # if we get this far we're done
        identity.setWorkingParameter("stepCount", originalStep)
        return -1

    ### Form response parsing
    def getFormButton(self, requestParameters):
        for parameter in requestParameters.keySet():
            start = parameter.find(":")
            if start > -1:
                return parameter[start + 1:]

    ### Client Config Utilities

    def getClient(self, session):
        sessionAttributes = session.getSessionAttributes()
        clientId = sessionAttributes.get(AuthorizeRequestParam.CLIENT_ID)
        return CdiUtil.bean(ClientService).getClient(clientId)

    def getClientUri(self, session):

        clientUri = self.getClient(session).getClientUri()
        if clientUri is None:
            sessionAttributes = session.getSessionAttributes()
            clientUri = sessionAttributes.get("entityId")  # Hack!

        return clientUri

    def getRPConfig(self, session):
        clientService = CdiUtil.bean(ClientService)
        client = self.getClient(session)

        # check the cache
        clientKey = "oidc:%s" % client.getClientId()
        if clientKey in self.rpConfigCache:
            return self.rpConfigCache[clientKey]

        descriptionAttr = clientService.getCustomAttribute(
            client, "description")

        rpConfig = None
        if descriptionAttr is not None:
            description = descriptionAttr.getValue()
            start = description.find("{")
            if (start > -1):
                decoder = json.JSONDecoder()
                try:
                    rpConfig, _ = decoder.raw_decode(description[start:])
                except ValueError:
                    print(
                        "%s. getRPConfig: Failed to parse JSON config for client %s"
                        % (self.name, client.getClientName()))
                    print("Exception: ", sys.exc_info()[1])
                    pass

        if rpConfig is None:
            rpConfig = self.rpDefaults
        else:  # Populate missing settings with defaults
            for setting, value in self.rpDefaults.items():
                if not setting in rpConfig:
                    rpConfig[setting] = value

        # Add it to the cache
        self.rpConfigCache[clientKey] = rpConfig
        return rpConfig

    # FIDO2 Metadata loading
    # This is deferred so that the FIDO2 service has time to start
    def getFidoMetaDataConfiguration(self):
        if self.fidoMetaDataConfiguration != None:
            return self.fidoMetaDataConfiguration

        self.metaDataLoaderLock.lock()
        # Make sure that another thread not loaded configuration already
        if self.fidoMetaDataConfiguration != None:
            return self.fidoMetaDataConfiguration

        try:
            print("%s. Initialization. Downloading Fido2 metadata" % self.name)
            self.fido2_server_metadata_uri = self.fido2_server_uri + "/.well-known/fido2-configuration"

            metaDataConfigurationService = Fido2ClientFactory.instance(
            ).createMetaDataConfigurationService(
                self.fido2_server_metadata_uri)

            max_attempts = 10
            for attempt in range(1, max_attempts + 1):
                try:
                    self.fidoMetaDataConfiguration = metaDataConfigurationService.getMetadataConfiguration(
                    ).readEntity(java.lang.String)
                    return self.fidoMetaDataConfiguration
                except ClientErrorException as ex:
                    # Detect if last try or we still get Service Unavailable HTTP error
                    if (attempt == max_attempts) or (
                            ex.getResponse().getResponseStatus() !=
                            Response.Status.SERVICE_UNAVAILABLE):
                        raise ex

                    java.lang.Thread.sleep(3000)
                    print("Attempting to load metadata: %d" % attempt)
        finally:
            self.metaDataLoaderLock.unlock()