コード例 #1
0
ファイル: tools.py プロジェクト: maniacs-oss/timetagger
def set_auth_info_from_token(token):
    """Set the authentication by providing a TimeTagger webtoken."""
    payload_base64 = token.split(".")[1].replace("_", "/")
    auth_info = JSON.parse(
        window.decodeURIComponent(window.escape(window.atob(payload_base64))))
    auth_info.token = token
    localStorage.setItem("timetagger_auth_info", JSON.stringify(auth_info))
コード例 #2
0
ファイル: stores.py プロジェクト: liz3518/timetagger
    async def _push(self, kind, authtoken):

        # Build url
        url = location.protocol + "//" + location.hostname + ":" + location.port
        url = url.rstrip(":") + "/api/v1/" + kind

        # Take items, only proceed if nonempty
        items = self._to_push[kind]
        if len(items.keys()) == 0:
            return
        self._to_push[kind] = {}

        # Fetch and wait for response
        init = dict(
            method="PUT",
            body=JSON.stringify(items.values()),
            headers={"authtoken": authtoken},
        )
        try:
            res = await window.fetch(url, init)
        except Exception as err:
            res = dict(status=0, statusText=str(err), text=lambda: "")

        # Process response
        if res.status != 200:
            # The server was not able to process the request, maybe the
            # wifi is down or the server is restarting.
            # Put items back, but don't overwrite if the item was updated again.
            for key, item in items.items():
                self._to_push[kind].setdefault(key, item)
            self._set_state(
                "error")  # is usually less bad than the fail's below
            text = await res.text()
            console.warn(res.status + " (" + res.statusText + ") " + text)
            # Also notify the user for 402 errors
            if res.status == 402 and window.canvas:
                window.canvas.notify_once(text)

        else:
            # Success, but it can still mean that some records failed. In this
            # case these records are likely corrupt, so we delete them, and
            # will get the server's version back when we pull.
            d = JSON.parse(await res.text())
            # d.accepted -> list of ok keys
            for key in d.fail:
                self[kind]._drop(key)
            for err in d.errors:
                self._set_state("warning")
                console.warn(f"Server dropped a {kind}: {err}")
コード例 #3
0
ファイル: stores.py プロジェクト: maniacs-oss/timetagger
    async def _pull(self, authtoken):

        # Fetch and wait for response
        url = tools.build_api_url("updates?since=" + self._server_time)
        init = dict(method="GET", headers={"authtoken": authtoken})
        try:
            res = await window.fetch(url, init)
        except Exception as err:
            res = dict(status=0, statusText=str(err), text=lambda: "")
        self._pull_statuses.append(res.status)
        self._pull_statuses = self._pull_statuses[-5:]

        # Process response
        if res.status != 200:
            text = await res.text()
            console.warn(res.status + " (" + res.statusText + ") " + text)
            self._set_state("error")  # E.g. Wifi or server down, or 500
            if res.status == 401:
                # Our token is probably expired. There may be local
                # changes that have not yet been pushed, which would
                # be lost if we logout. On the other hand, this may be
                # a lost/stolen device and the user revoked the token.
                # We can distinguish between these cases by determining
                # that the 401 is due to a token seed mismatch (revoked).
                self._auth_cantuse = text
                if "revoked" in text:
                    window.location.href = "../logout"
        else:
            ob = JSON.parse(await res.text())
            if ob.server_time:
                self._log_load("server", ob)
                # Reset?
                if ob.reset:
                    await self._clear_cache()
                    self.reset()
                self._server_time = ob.server_time
                # The odds of something going wrong here are tiny ...
                # but if they happen, we're out of sync with the server :(
                try:
                    self.settings._put_received(*ob.settings)
                except Exception as err:
                    self._set_state("warning")
                    console.error(err)
                    window.alert(
                        "Sync error (settings), see dev console for details.")
                try:
                    self.records._put_received(*ob.records)
                except Exception as err:
                    self._set_state("warning")
                    console.error(err)
                    window.alert(
                        "Sync error (records), see dev console for details.")

                # Set state to ok if we got new items, and if there were no errors
                if ob.settings or ob.records:
                    if self.state != "warning":
                        self._set_state("ok")
コード例 #4
0
ファイル: stores.py プロジェクト: liz3518/timetagger
    async def _pull(self, authtoken):

        # Build url
        url = location.protocol + "//" + location.hostname + ":" + location.port
        url = url.rstrip(":") + "/api/v1/updates?since=" + self._server_time

        # Fetch and wait for response
        init = dict(method="GET", headers={"authtoken": authtoken})
        try:
            res = await window.fetch(url, init)
        except Exception as err:
            res = dict(status=0, statusText=str(err), text=lambda: "")
        self._pull_statuses.append(res.status)
        self._pull_statuses = self._pull_statuses[-5:]

        # Process response
        if res.status != 200:
            console.warn(res.status + " (" + res.statusText + ") " +
                         await res.text())
            self._set_state("error")  # E.g. Wifi or server down, or 500
            if res.status == 403:
                if self._pull_statuses[-2] != 403 and self._pull_statuses[
                        -3] != 403:
                    # We do have internet, but token is invalid: renew and sync sooner
                    await window.auth.renew_maybe()
                    self.sync_soon(4)
        else:
            ob = JSON.parse(await res.text())
            if ob.server_time:
                self._log_load("server", ob)
                # Reset?
                if ob.reset:
                    await self._clear_cache()
                    self.reset()
                self._server_time = ob.server_time
                # The odds of something going wrong here are tiny ...
                # but if they happen, we're out of sync with the server :(
                try:
                    self.settings._put_received(*ob.settings)
                except Exception as err:
                    self._set_state("warning")
                    console.error(err)
                    window.alert(
                        "Sync error (settings), see dev console for details.")
                try:
                    self.records._put_received(*ob.records)
                except Exception as err:
                    self._set_state("warning")
                    console.error(err)
                    window.alert(
                        "Sync error (records), see dev console for details.")

                # Set state to ok if we got new items, and if there were no errors
                if ob.settings or ob.records:
                    if self.state != "warning":
                        self._set_state("ok")
コード例 #5
0
ファイル: tools.py プロジェクト: maniacs-oss/timetagger
def get_auth_info():
    """Get the authentication info or None."""
    x = localStorage.getItem("timetagger_auth_info")
    if x:
        try:
            return JSON.parse(x)
        except Exception as err:
            console.warn("Cannot parse JSON auth info: " + str(err))
            return None
    else:
        return None
コード例 #6
0
ファイル: utils.py プロジェクト: maniacs-oss/timetagger
 def _load_settings(self):
     x = localStorage.getItem("timetagger_local_settings")
     if x:
         try:
             return JSON.parse(x)
         except Exception as err:
             window.console.warn("Cannot parse local settings JSON: " +
                                 str(err))
             return {}
     else:
         return {}
コード例 #7
0
ファイル: tools.py プロジェクト: maniacs-oss/timetagger
async def renew_webtoken(verbose=True, reset=False):
    """Renew the webtoken. Each webtoken expires after 14 days. But
    while valid, it can be exhcanged for a new one. By doing this while
    the app is active, users won't be logged out unless this device
    does not use the app for 14 days.

    If reset is True, the token seed is reset, causing all issued web
    tokens to become invalid. In other words: all sessions on other
    devices will be logged out.
    """
    # Get current auth info
    auth = get_auth_info()
    if not auth:
        if verbose:
            console.warn("Could not renew token - not logged in")
        return

    # Make request and wait for response
    url = build_api_url("webtoken")
    if reset:
        url += "?reset=1"
    init = dict(method="GET", headers={"authtoken": auth.token})
    res = await fetch(url, init)

    # Handle
    if res.status != 200:
        text = await res.text()
        console.warn("Could not renew token: " + text)
        if res.status == 401 and "revoked" in text:
            # When revoked, we logout to drop local changes.
            # See notes in stores.py where we do the same.
            if "/app/" in location.pathname:
                location.href = "../logout"
            else:
                location.href = "./logout"
        return

    # Are we still logged in. User may have logged out in the mean time.
    auth = get_auth_info()
    if not auth:
        return

    # Apply
    d = JSON.parse(await res.text())
    set_auth_info_from_token(d.token)
    if verbose:
        console.warn("webtoken renewed")
コード例 #8
0
    def __init__(self, app_name, id, ws_url=None):
        self.app = None  # the root component (can be a PyComponent)
        self.app_name = app_name
        self.id = id
        self.status = 1
        self.ws_url = ws_url
        self._component_counter = 0
        self._disposed_ob = {'_disposed': True}

        # Maybe this is JLab
        if not self.id:
            jconfig = window.document.getElementById('jupyter-config-data')
            if jconfig:
                try:
                    config = JSON.parse(jconfig.innerText)
                    self.id = config.flexx_session_id
                    self.app_name = config.flexx_app_name
                except Exception as err:
                    print(err)

        # Init internal variables
        self._init_time = time()
        self._pending_commands = []  # to pend raw commands during init
        self._asset_count = 0
        self._ws = None
        self.last_msg = None
        # self.classes = {}
        self.instances = {}
        self.instances_to_check_size = {}

        if not window.flexx.is_exported:
            self.init_socket()

        # Initiate service to track resize
        # Note that only toplevel widgets are tracked, and only once per sec
        window.addEventListener('resize', self._check_size_of_objects, False)
        window.setInterval(self._check_size_of_objects, 1000)
コード例 #9
0
ファイル: _clientcore.py プロジェクト: zoofIO/flexx
    def __init__(self, app_name, id, ws_url=None):
        self.app = None  # the root component (can be a PyComponent)
        self.app_name = app_name
        self.id = id
        self.status = 1
        self.ws_url = ws_url
        self._component_counter = 0
        self._disposed_ob = {'_disposed': True}

        # Maybe this is JLab
        if not self.id:
            jconfig = window.document.getElementById('jupyter-config-data')
            if jconfig:
                try:
                    config = JSON.parse(jconfig.innerText)
                    self.id = config.flexx_session_id
                    self.app_name = config.flexx_app_name
                except Exception as err:
                    print(err)

        # Init internal variables
        self._init_time = time()
        self._pending_commands = []  # to pend raw commands during init
        self._asset_count = 0
        self._ws = None
        self.last_msg = None
        # self.classes = {}
        self.instances = {}
        self.instances_to_check_size = {}

        if not window.flexx.is_exported:
            self.init_socket()

        # Initiate service to track resize
        # Note that only toplevel widgets are tracked, and only once per sec
        window.addEventListener('resize', self._check_size_of_objects, False)
        window.setInterval(self._check_size_of_objects, 1000)
コード例 #10
0
ファイル: _clientcore.py プロジェクト: okajun35/flexx
 def _receive_command(self, command):
     """ Process a command send from the server.
     """
     cmd = command[0]
     if cmd == 'PING':
         # Used for roundtrip stuff, do at least one iter loop here ...
         window.setTimeout(self.send_command, 10, 'PONG', command[1])
     elif cmd == 'INIT_DONE':
         window.flexx.spin(None)
         while len(self._pending_commands):
             self._receive_raw_command(self._pending_commands.pop(0))
         self._pending_commands = None
         # print('init took', time() - self._init_time)
     elif cmd == 'PRINT':
         (window.console.ori_log or window.console.log)(command[1])
     elif cmd == 'EXEC':
         eval(command[1])
     elif cmd == 'EVAL':
         x = None
         if len(command) == 2:
             x = eval(command[1])
         elif len(command) == 3:
             x = eval('this.instances.' + command[1] + '.' + command[2])
         console.log(str(x))  # print (and thus also sends back result)
     elif cmd == 'EVALANDRETURN':
         try:
             x = eval(command[1])
         except Exception as err:
             x = err
         try:
             sx = JSON.stringify(x)
         except Exception as err:
             sx = '"' + str(x) + '"'
         eval_id = command[2]  # to identify the result in Python
         self.send_command("EVALRESULT", sx, eval_id)
     elif cmd == 'INVOKE':
         id, name, args = command[1:]
         ob = self.instances.get(id, None)
         if ob is None:
             console.warn('Cannot invoke %s.%s; '
                          'session does not know it (anymore).' % (id, name))
         elif ob._disposed is True:
             pass  # deleted, but other end might not be aware when command was send
         else:
             ob[name](*args)
     elif cmd == 'INSTANTIATE':
         self.instantiate_component(*command[1:])  # module, cname, id, args, kwargs
     elif cmd == 'DISPOSE':
         id = command[1]
         c = self.instances.get(id, None)
         if c is not None and c._disposed is False:  # else: no need to warn
             c._dispose()
         self.send_command('DISPOSE_ACK', command[1])
         self.instances.pop(id, None)  # Drop local reference now
     elif cmd == 'DISPOSE_ACK':
         self.instances.pop(command[1], None)  # Drop reference
     elif cmd == 'DEFINE':
         #and command[1] == 'JS' or command[1] == 'DEFINE-JS-EVAL '):
         kind, name, code = command[1:]
         window.flexx.spin()
         address = window.location.protocol + '//' + self.ws_url.split('/')[2]
         code += '\n//# sourceURL=%s/flexx/assets/shared/%s\n' % (address, name)
         if kind == 'JS-EVAL':
             eval(code)
         elif kind == 'JS':
             # With this method, sourceURL does not work on Firefox,
             # but eval might not work for assets that don't "use strict"
             # (e.g. Bokeh). Note, btw, that creating links to assets does
             # not work because these won't be loaded on time.
             el = window.document.createElement("script")
             el.id = name
             el.innerHTML = code
             window.flexx.asset_node.appendChild(el)
         elif kind == 'CSS':
             el = window.document.createElement("style")
             el.type = "text/css"
             el.id = name
             el.innerHTML = code
             window.flexx.asset_node.appendChild(el)
         else:
             window.console.error('Dont know how to DEFINE ' +
                                  name + ' with "' + kind + '".')
     elif cmd == 'OPEN':
         window.win1 = window.open(command[1], 'new', 'chrome')
     else:
         window.console.error('Invalid command: "' + cmd + '"')
     return command
コード例 #11
0
ファイル: utils.py プロジェクト: maniacs-oss/timetagger
 def _save_settings(self):
     x = JSON.stringify(self._cache)
     localStorage.setItem("timetagger_local_settings", x)