Ejemplo n.º 1
0
def ui_host_manager_add(host_uuid):
    host_uuid = validate_uuid(host_uuid)
    host = models.ManagedHost.query.filter_by(uuid=host_uuid).first()
    if not host or current_user not in host.managers:
        abort(403, 'No host found with given id and managed by you')
    form = forms.HostAddManagerForm()
    if form.validate_on_submit():
        email = form.email.data.lower()
        managers = models.Manager.query.filter(
            models.Manager.email.ilike(email))
        if managers.first():
            for manager in managers:
                if host in manager.hosts:
                    abort(403, f"Host already managed by {html.escape(email)}")
                else:
                    manager.hosts.append(host)
        else:
            # create new manager
            manager = models.Manager(email=email)
            db.session.add(manager)
            manager.hosts.append(host)
        db.commit_and_sync()
        return redirect(url_for("ui_host", host_uuid=host_uuid))
    else:
        return render_template('host-manager-add.html',
                               title=f'Add manager for {host.hostname}',
                               host=host,
                               form=form)
Ejemplo n.º 2
0
def ui_host_add():
    try:
        form = forms.HostAddForm()
        if form.validate_on_submit():
            hostname = form.hostname.data
            if not re.match('^[\w\-\.\ ]+$', hostname):
                abort(403, "Bad symbols in hostname")
            for host in current_user.hosts:
                if hostname.lower() == host.hostname.lower():
                    abort(
                        403,
                        f"Host {html.escape(hostname)} already exists among your hosts"
                    )
            host = models.ManagedHost(hostname=hostname, uuid=new_host_uuid())
            db.session.add(host)
            current_user.hosts.append(host)
            host.pin_generate()
            db.commit_and_sync()
            return render_template('host.html', title=host.hostname, host=host)
        else:
            return render_template('host-add.html',
                                   title='Add host',
                                   form=form)
    except TimekprwException as e:
        debug("exception:", exc_info=True)
        abort(403, str(e))
Ejemplo n.º 3
0
def rest_host_getauthkey():
    """
    REST: add host with pin and return auth key to client
    Authentication is not required
    :param
    {
        pin: "nnnnnn"
    }
    :return:
    {
        success: "true"|"false",
        message: "Cause of problem if success=false" (optional),
        authkey: assigned authentication key for future authentication
        hostuuid: host uuid
    }
    """
    rv = {
        "success": False,
        "message": "Internal error: message is not defined"
    }
    data = request.get_json()
    if not data:
        return {**rv, "message": "json data expected"}
    if "pin" in data:
        # TODO: this implementation can be brute-forced
        #       what we can do is to require some proof of work from the client
        pin = str(data["pin"])
        hostqry = models.ManagedHost.query.filter_by(pin=pin)
        count = hostqry.count()
        if count == 0:
            return {
                **rv, "message":
                "Please provide correct pin, or obtain new pin from server"
            }
        if count > 1:
            return {
                **rv, "message":
                "Internal error, please get another pin and come back"
            }
        if count != 1:
            return {
                **rv, "message":
                f"Internal error on server, please report to developer: count={count} and it is not 0,1,>1"
            }
        host = hostqry.first()
        (authkey_plain, authkey_hash) = host.authkey_generate()
        host.authkey = authkey_hash
        host.authkey_trycount = 0
        host.pin = None
        db.commit_and_sync()
        return {
            "success": True,
            "authkey": authkey_plain,
            "hostuuid": host.uuid
        }
    else:  # "pin" is not in data
        return {**rv, "message": "pin expected"}
    return {**rv}
Ejemplo n.º 4
0
def ui_host_deactivate(host_uuid):
    """Clear authentication token and pin"""
    host_uuid = validate_uuid(host_uuid)
    host = models.ManagedHost.query.filter_by(uuid=host_uuid).first()
    if not host or current_user not in host.managers:
        abort(403, 'No host found with given id and managed by you')
    host.pin = None
    host.authkey = None
    db.commit_and_sync()
    return redirect(url_for('ui_host', host_uuid=host_uuid))
Ejemplo n.º 5
0
def ui_host_user_rm(host_uuid, user_id):
    host_uuid = validate_uuid(host_uuid)
    host = models.ManagedHost.query.filter_by(uuid=host_uuid).first()
    if not host or current_user not in host.managers:
        abort(403, 'No host found with given id and managed by you')
    user = models.ManagedUser.query.filter_by(id=user_id).first()
    if not user:
        abort(403, 'Did not find such user')
    if user.host != host:
        abort(403, 'This user does not belong to this host')
    host.users.remove(user)
    db.commit_and_sync()
    return redirect(url_for("ui_host", host_uuid=host_uuid))
Ejemplo n.º 6
0
def ui_host_manager_rm(host_uuid, manager_id):
    host_uuid = validate_uuid(host_uuid)
    host = models.ManagedHost.query.filter_by(uuid=host_uuid).first()
    if not host or current_user not in host.managers:
        abort(403, 'No host found with given id and managed by you')
    manager = models.Manager.query.filter_by(id=manager_id).first()
    if not manager:
        abort(403, 'Did not find such manager')
    if manager == current_user:
        abort(403, 'You cannot remove yourself from managers list')
    if manager not in host.managers:
        abort(403, 'This manager does not manage this host')
    host.managers.remove(manager)
    db.commit_and_sync()
    return redirect(url_for("ui_host", host_uuid=host_uuid))
Ejemplo n.º 7
0
def ui_host_rm(host_uuid):
    host_uuid = validate_uuid(host_uuid)
    host = models.ManagedHost.query.filter_by(uuid=host_uuid).first()
    if not host or current_user not in host.managers:
        abort(403, 'No host found with given id and managed by you')
    form = forms.HostRemoveForm()
    if form.validate_on_submit():
        db.session.delete(host)
        db.commit_and_sync()
        return redirect(url_for("webroot"))
    else:
        return render_template('host-rm.html',
                               title=f'Confirm remove {host.hostname}',
                               host=host,
                               form=form)
Ejemplo n.º 8
0
def ui_time(user_uuid):
    user_uuid = validate_uuid(user_uuid)
    user = models.ManagedUser.query.filter_by(uuid=user_uuid).first()
    if not user or current_user not in user.host.managers:
        abort(403, 'No user found with given id and managed by you')
    form = forms.TimeForm(useruuid=user_uuid, username=str(user))
    if form.validate_on_submit():
        amount = form.amount.data
        override = models.TimeOverride(amount=amount,
                                       status=models.TimeOverrideStatusQueued,
                                       user=user,
                                       owner=current_user)
        db.session.add(override)
        db.commit_and_sync()
        return render_template('time_added.html', user=user, amount=amount)
    else:
        return render_template('time.html',
                               title='Time Override',
                               form=form,
                               username=str(user))
Ejemplo n.º 9
0
def rest_overrides_ack(host_uuid):
    """
    REST: Acknowledge time overrides for host.
    Call after applying overrides on host.
    :param host_uuid:
    :return: { success: True }
    """
    rv = {
        "success": False,
        "message": "Internal error: message is not defined"
    }

    host_uuid = validate_uuid(host_uuid)
    host = models.ManagedHost.query.filter_by(uuid=host_uuid).first()
    if not host:
        return {**rv, "message": "No host found with given id"}

    data = request.get_json()
    if not data:
        return {**rv, "message": "json data expected"}
    auth_result = rest_check_auth(host, data)
    if not auth_result["success"]:
        return auth_result

    for user in host.users:
        # clean older overrides
        for override in [
                x for x in user.timeoverrides
                if x.status != models.TimeOverrideStatusQueued
        ]:
            db.session.delete(override)
        # change status of last override
        for override in [
                x for x in user.timeoverrides
                if x.status == models.TimeOverrideStatusQueued
        ]:
            override.status = models.TimeOverrideStatusApplied
    db.commit_and_sync()
    return {'success': True}
Ejemplo n.º 10
0
def ui_host_user_add(host_uuid):
    host_uuid = validate_uuid(host_uuid)
    host = models.ManagedHost.query.filter_by(uuid=host_uuid).first()
    if not host or current_user not in host.managers:
        abort(403, 'No host found with given id and managed by you')
    form = forms.HostAddUserForm()
    if form.validate_on_submit():
        login = form.login.data
        # TODO: don't know how to query for users of this host with such login name
        existing = [x for x in host.users if x.login.lower() == login.lower()]
        if len(existing):
            abort(403, f"User {login} already exists on host {host.hostname}")
        user = models.ManagedUser(uuid=new_user_uuid(), login=login)
        db.session.add(user)
        host.users.append(user)
        db.commit_and_sync()
        return redirect(url_for("ui_host", host_uuid=host_uuid))
    else:
        return render_template('host-user-add.html',
                               title=f'Add user on {host.hostname}',
                               host=host,
                               form=form)
Ejemplo n.º 11
0
 def authkey_check(self, authkey):
     """
     Check authkey, return True or False
     may raise TimekprwException
     Performs DB commit
     """
     try:
         if not self.authkey:
             return False
         rv = pbkdf2_sha512.verify(authkey, self.authkey)
         if not rv:
             if self.authkey_trycount is None:
                 self.authkey_trycount = 0
             self.authkey_trycount += 1
         else:
             self.auth_lastsuccess = datetime.datetime.utcnow()
         db.commit_and_sync(sync_priority=SyncPriorityDelayed)
         return rv
     except Exception as e:
         debug(f'got exception in authkey_check: {e}, trace follows:',
               exc_info=True)
         raise TimekprwException('Failed to check authkey')
Ejemplo n.º 12
0
def app_init():
    """Get DB from permanent storage if needed. Upgrade database if needed."""
    debug(f"{whoami()} called")
    if db.db_permstore_instance:
        db.db_permstore_instance.get_from_permstore()

    fresh_db = False
    try:
        # baaad hack, but flask_migrate.current() returns nothing, and I don't know how to test if DB is newly created (i.e. sqlite), or existed before
        if not db.engine.dialect.has_table(db.engine, 'alembic_version'):
            # the only reason i know for this is when database is newly created
            # and we definitely should not run db.create_all() before migration!
            fresh_db = True
    except Exception as e:
        error(
            f"got exception while trying to get db revision: {e} (enable debug for more)"
        )
        debug(f"exception follows:", exc_info=True)

    debug("running db.create_all()")
    db.create_all()
    debug("done db.create_all()")

    try:
        if fresh_db:
            info("This is a fresh database, marking it as latest revision")
            run_in_child_process(flask_migrate.stamp)
        else:
            debug("Trying to perform database migration")
            run_in_child_process(flask_migrate.upgrade)
    except Exception as e:
        error(
            f"got exception while trying to upgrade db revision: {e} (enable debug for more)"
        )
        debug(f"exception follows:", exc_info=True)

    if os.environ.get('APP_ENVIRONMENT', None) == "dev":

        if models.Manager.query.count():
            debug(
                "skipping creation of dev objects - database already has objects"
            )
        else:
            debug("creating dev objects")

            host1 = models.ManagedHost(uuid=new_host_uuid(),
                                       hostname='testhost.tld')
            db.session.add(host1)
            host2 = models.ManagedHost(uuid=new_host_uuid(),
                                       hostname='otherhost.dom')
            db.session.add(host2)
            user1 = models.ManagedUser(uuid=new_user_uuid(),
                                       login="******",
                                       host=host1)
            db.session.add(user1)
            user2 = models.ManagedUser(uuid=new_user_uuid(),
                                       login="******",
                                       host=host1)
            db.session.add(user2)
            manager1 = models.Manager()
            manager1.hosts.append(host1)
            manager1.hosts.append(host2)
            db.session.add(manager1)

            db.commit_and_sync()
            debug("done creating dev objects")

            # TODO: remove this in production
            debug("creating built-in objects")
            host = models.ManagedHost.query.filter_by(
                uuid='59e8368c-7dbc-11ea-923e-7cb0c2957d37').first()
            if not host:
                debug("creating host")
                host = models.ManagedHost(uuid=str(
                    uuid.UUID('59e8368c-7dbc-11ea-923e-7cb0c2957d37')),
                                          hostname='john')
                user = models.ManagedUser(uuid=new_user_uuid(),
                                          login='******',
                                          host=host)
                db.session.add(user)
                db.session.add(host)
            manager1 = models.Manager.query.filter_by(
                email='*****@*****.**').first()
            if not manager1:
                debug("creating manager [email protected]")
                manager1 = models.Manager(
                    ext_auth_type=models.ExtAuthTypeGoogleAuth,
                    ext_auth_id='118295366576899719337',
                    email='*****@*****.**')
                manager1.hosts.append(host)
                db.session.add(manager1)
            #manager2 = models.Manager.query.filter_by(email='*****@*****.**').first()
            #if not manager2:
            #    debug("creating manager [email protected]")
            #    manager2 = models.Manager(ext_auth_type=models.ExtAuthTypeGoogleAuth,
            #                              ext_auth_id='103494272264223262600', email='*****@*****.**')
            #    manager2.hosts.append(host)
            #    db.session.add(manager2)
            db.commit_and_sync()
            debug("done creating built-in objects")
            # debug(dumpdata())
    debug(f"{whoami()} finished")
Ejemplo n.º 13
0
def login_callback():
    """Callback for Google Auth"""
    # Get authorization code Google sent back to you
    code = request.args.get("code")

    # Find out what URL to hit to get tokens that allow you to ask for
    # things on behalf of a user
    google_provider_cfg = get_google_provider_cfg()
    token_endpoint = google_provider_cfg["token_endpoint"]

    # Prepare and send a request to get tokens! Yay tokens!
    token_url, headers, body = client.prepare_token_request(
        token_endpoint,
        authorization_response=request.url,
        redirect_url=request.base_url,
        code=code)
    token_response = requests.post(
        token_url,
        headers=headers,
        data=body,
        auth=(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET),
    )

    # Parse the tokens!
    client.parse_request_body_response(json.dumps(token_response.json()))

    # Now that you have tokens (yay) let's find and hit the URL
    # from Google that gives you the user's profile information,
    # including their Google profile image and email
    userinfo_endpoint = google_provider_cfg["userinfo_endpoint"]
    uri, headers, body = client.add_token(userinfo_endpoint)
    userinfo_response = requests.get(uri, headers=headers, data=body)

    # You want to make sure their email is verified.
    # The user authenticated with Google, authorized your
    # app, and now you've verified their email through Google!
    if userinfo_response.json().get("email_verified"):
        unique_id = userinfo_response.json()["sub"]
        users_email = userinfo_response.json()["email"].lower()
        # picture = userinfo_response.json()["picture"]
        users_name = userinfo_response.json()["given_name"]
    else:
        return "User email not available or not verified by Google.", 400

    # Create a user in your db with the information provided by Google
    user = models.Manager.query.filter_by(
        ext_auth_id=unique_id,
        ext_auth_type=models.ExtAuthTypeGoogleAuth).first()
    if not user:
        # no user with given id, try to search by email among google-hosted users
        user = models.Manager.query.filter_by(
            email=users_email,
            ext_auth_type=models.ExtAuthTypeGoogleAuth).first()
        if not user:
            # no user with this email among google-hosted users, try to search by email with no type set
            user = models.Manager.query.filter(
                db.and_(models.Manager.email == users_email,
                        models.Manager.ext_auth_type == None)).first()
    if user:
        # update user's data if we already have it
        if user.name != users_name: user.name = users_name
        if user.email != users_email: user.email = users_email
        if user.ext_auth_type != models.ExtAuthTypeGoogleAuth:
            user.ext_auth_type = models.ExtAuthTypeGoogleAuth
        # if user.picture != picture: user.picture = picture
    else:
        user = models.Manager(ext_auth_id=unique_id,
                              ext_auth_type=models.ExtAuthTypeGoogleAuth,
                              name=users_name,
                              email=users_email)
        db.session.add(user)
    db.commit_and_sync(sync_priority=SyncPriorityDelayed)

    login_user(user, remember=True)

    return redirect(url_for("webroot"))