Beispiel #1
0
def api_newapp():
    # Allow the use of a mock DB during testing
    if app.config['TESTING']:
        DB_CON = app.config['TEST_DB']
    else:
        DB_CON = None

    # Reject non-JSON payload
    if not request.json:
        raise error_handlers.InvalidAPIUsage("bad payload format",
                                             status_code=400)

    request_content = request.get_json()
    if 'appname' not in request_content:
        raise error_handlers.InvalidAPIUsage("bad payload", status_code=400)

    # Default to read-only apps
    app_attributes = {"writeEnabled": "False"}
    if 'writeenabled' in request_content:
        if request_content['writeenabled'] is True:
            app_attributes['writeEnabled'] = "True"

    app_name = html.escape(request_content['appname'])
    try:
        app_id, app_key, app_secret = jots.pyauth.app.create_app(
            app_name, attributes=app_attributes, db=DB_CON)
        return jsonify(
            {app_name: {
                "id": app_id,
                "key": app_key,
                "secret": app_secret
            }})
    except jots.pyauth.app.AppActionError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)
def api_passwordreset():
    # Allow the use of a mock DB during testing
    if app.config['TESTING']:
        DB_CON = app.config['TEST_DB']
    else:
        DB_CON = None

    # Reject non-JSON payload
    if not request.json:
        raise error_handlers.InvalidAPIUsage("bad payload format",
                                             status_code=400)

    request_content = request.get_json()
    if 'email' not in request_content:
        raise error_handlers.InvalidAPIUsage("bad payload", status_code=400)

    email = html.escape(request_content['email'])
    if len(email) < 5:
        raise error_handlers.InvalidAPIUsage("bad email address",
                                             status_code=400)

    try:
        user = jots.pyauth.user.user(email_address=email, db=DB_CON)
    except jots.pyauth.user.UserNotFound:
        raise error_handlers.InvalidAPIUsage("bad user", status_code=400)
    except jots.pyauth.user.InputError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)

    try:
        reset_code = user.reset_password(
            service_domain=app.config['DOMAIN_NAME'])
    except jots.pyauth.user.UserActionError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)
    except jots.pyauth.user.InputError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)

    # The next step is to email a link to the 'reset' page with a query string (q) containing reset code
    try:
        email_obj = mailer.personalised_email(
            recipient=email,
            template_name="reset",
            data={
                "site_name":
                app.config['DOMAIN_NAME'],
                "reset_url":
                "https://{}:{}/reset?q={}".format(app.config['DOMAIN_NAME'],
                                                  app.config['SERVER_PORT'],
                                                  reset_code)
            })
        if app.config['TESTING']:
            email_obj.send(mail_agent="file")
        else:
            email_obj.send()
    except mailer.InputError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)
    except mailer.MailActionError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)

    return jsonify({"status": "ok", "reset_code": reset_code})
    def app_req_rw_wrapper(*args, **kwargs):
        if g.app_obj is not None:
            if 'writeEnabled' not in g.app_obj.properties.attributes:
                raise error_handlers.InvalidAPIUsage("access denied",
                                                     status_code=403)

            if not bool(
                    strtobool(
                        g.app_obj.properties.attributes['writeEnabled'])):
                raise error_handlers.InvalidAPIUsage("access denied",
                                                     status_code=403)

        return func(*args, **kwargs)
    def id_wrapper(*args, **kwargs):
        # Allow the use of a mock DB during testing
        if app.config['TESTING']:
            DB_CON = app.config['TEST_DB']
        else:
            DB_CON = None

        g.app_obj = None
        g.user_obj = None

        # Extract requesters identidy and confirm it's a valid app
        requester_id = get_jwt_identity()

        # App errors passed as we need to check that a user isn't authing before raising an error
        try:
            g.app_obj = jots.pyauth.app.app(app_name=requester_id, db=DB_CON)
        except jots.pyauth.app.AppNotFound:
            pass
        except jots.pyauth.user.InputError:
            pass

        try:
            g.user_obj = jots.pyauth.user.user(email_address=requester_id,
                                               db=DB_CON)
        except jots.pyauth.user.UserNotFound:
            pass
        except jots.pyauth.user.InputError:
            pass

        # Catch all - if neither object is populated, raise error
        if g.app_obj is None and g.user_obj is None:
            raise error_handlers.InvalidAPIUsage("access denied",
                                                 status_code=403)

        return func(*args, **kwargs)
Beispiel #5
0
def api_get_appdetails(app_id):
    # Allow the use of a mock DB during testing
    if app.config['TESTING']:
        DB_CON = app.config['TEST_DB']
    else:
        DB_CON = None

    app_id = html.escape(app_id)
    try:
        app_object = jots.pyauth.app.app(app_id=app_id, db=DB_CON)
    except jots.pyauth.app.InputError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)
    except jots.pyauth.app.AppNotFound as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)

    return app_object.properties.as_dict()
def api_user_details(user_id):
    # Allow the use of a mock DB during testing
    if app.config['TESTING']:
        DB_CON = app.config['TEST_DB']
    else:
        DB_CON = None

    user_id = html.escape(user_id)

    try:
        user = jots.pyauth.user.user(user_id=user_id, db=DB_CON)
        return jsonify(user.properties.as_dict())
    except jots.pyauth.user.UserNotFound:
        raise error_handlers.InvalidAPIUsage("invalid user", status=403)
    except jots.pyauth.user.InputError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)
Beispiel #7
0
def api_groupmembers(group_id):
    # Allow the use of a mock DB during testing
    if app.config['TESTING']:
        DB_CON = app.config['TEST_DB']
    else:
        DB_CON = None

    group_id = html.escape(group_id)

    try:
        group = jots.pyauth.group.group(group_id=group_id, db=DB_CON)
        group_members_with_email = group.get_members_detail(attribute="email")
        return jsonify(group_members_with_email)
    except jots.pyauth.group.GroupNotFound:
        return jsonify(dict())
    except jots.pyauth.group.GroupActionError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)
    except jots.pyauth.group.InputError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)
    def usr_adm_wrapper(*args, **kwargs):
        # Allow the use of a mock DB during testing
        if app.config['TESTING']:
            DB_CON = app.config['TEST_DB']
        else:
            DB_CON = None

        if g.user_obj is not None:
            # If user object was created, check user is admin
            try:
                group = jots.pyauth.group.group(group_name="admin", db=DB_CON)
            except jots.pyauth.group.GroupNotFound:
                raise error_handlers.InvalidAPIUsage("admin group not found",
                                                     status_code=500)

            if g.user_obj.properties.userId not in group.properties.members:
                raise error_handlers.InvalidAPIUsage("access denied",
                                                     status_code=403)

        return func(*args, **kwargs)
Beispiel #9
0
def api_findgroups():
    ''' Search for groups based on either group name or a user ID
      The two use different methods wihin the group moduel
      but should result in the same output
  '''
    # Allow the use of a mock DB during testing
    if app.config['TESTING']:
        DB_CON = app.config['TEST_DB']
    else:
        DB_CON = None

    # Reject non-JSON payload
    if not request.json:
        raise error_handlers.InvalidAPIUsage("bad payload format",
                                             status_code=400)

    request_content = request.get_json()
    # Search by group name
    if 'groupname' in request_content:
        groupname = html.escape(request_content['groupname'])

        try:
            response = jots.pyauth.group.find_groups_like(groupname, db=DB_CON)
            return jsonify(response)

        except jots.pyauth.group.GroupNotFound:
            return jsonify(dict())

    # Search for group by member user id
    elif 'userid' in request_content:
        userid = html.escape(request_content['userid'])

        try:
            response = jots.pyauth.group.find_user_in_group(userid, db=DB_CON)
            return jsonify(response)

        except jots.pyauth.group.GroupNotFound:
            return jsonify(dict())

    else:
        raise error_handlers.InvalidAPIUsage("bad payload", status_code=400)
Beispiel #10
0
def token_get():
    # Allow the use of a mock DB during testing
    if app.config['TESTING']:
        DB_CON = app.config['TEST_DB']
    else:
        DB_CON = None

    if 'Authorization' not in request.headers:
        raise error_handlers.InvalidAPIUsage("missing autorization header",
                                             status_code=400)

    auth_header = request.headers.get('Authorization')
    if "Basic " in auth_header:
        auth_header = auth_header.split("Basic ")[1]

    b64_auth_content = base64.b64decode(auth_header).decode('utf-8').strip()

    if ":" not in b64_auth_content:
        raise error_handlers.InvalidAPIUsage("bad autorization header",
                                             status_code=400)

    key, secret = b64_auth_content.split(":")
    try:
        app_obj = jots.pyauth.app.app(app_key=key, db=DB_CON)
    except jots.pyauth.app.AppNotFound:
        raise error_handlers.InvalidAPIUsage("forbidden - bad app",
                                             status_code=403)
    except jots.pyauth.app.InputError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)

    if app_obj.authenticate(secret):
        expires = datetime.timedelta(days=30)
        access_token = create_access_token(
            identity=app_obj.properties.appName,
            expires_delta=expires,
            user_claims={"appId": app_obj.properties.appId})
        return access_token
    else:
        raise error_handlers.InvalidAPIUsage("permission denied",
                                             status_code=403)
Beispiel #11
0
def api_newgroup():
    # Allow the use of a mock DB during testing
    if app.config['TESTING']:
        DB_CON = app.config['TEST_DB']
    else:
        DB_CON = None

    # Reject non-JSON payload
    if not request.json:
        raise error_handlers.InvalidAPIUsage("bad payload format",
                                             status_code=400)

    request_content = request.get_json()
    if 'groupname' not in request_content:
        raise error_handlers.InvalidAPIUsage("bad payload", status_code=400)

    group_name = html.escape(request_content['groupname'])
    try:
        new_group = jots.pyauth.group.create_group(group_name, db=DB_CON)
        return jsonify(new_group)
    except jots.pyauth.group.GroupActionError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)
Beispiel #12
0
def login_form():
    ''' Accepts login form data
      Get user object and check password
      Returns access and refresh tokens with CSRF tokens
  '''
    # Allow the use of a mock DB during testing
    if app.config['TESTING']:
        DB_CON = app.config['TEST_DB']
    else:
        DB_CON = None

    form_data = request.form

    username = html.escape(form_data['username'])
    password = form_data['password']

    try:
        user = jots.pyauth.user.user(email_address=username, db=DB_CON)

    except jots.pyauth.user.UserNotFound:
        raise error_handlers.InvalidAPIUsage("access denied", status_code=403)

    except jots.pyauth.user.InputError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)

    if user.properties.status != "active":
        raise error_handlers.InvalidAPIUsage("access denied", status_code=403)

    result = user.authenticate(password)
    if not result:
        raise error_handlers.InvalidAPIUsage("access denied", status_code=403)

    # Add user claims - groups, all except admin
    group_data = jots.pyauth.group.find_user_in_group(user.properties.userId,
                                                      db=DB_CON)
    group_names = list()
    for group in group_data.keys():
        if group != "admin":
            group_names.append(group)

    access_token = create_access_token(identity=username,
                                       user_claims={"groups": group_names})

    refresh_token = create_refresh_token(identity=username)
    refresh_jti = get_jti(refresh_token)

    try:
        user.set_refresh_jti(refresh_jti)
    except jots.pyauth.user.InputError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)
    except jots.pyauth.user.UserActionError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)

    response = make_response(redirect("/page"))
    set_access_cookies(response, access_token)
    set_refresh_cookies(response, refresh_token)

    return response
Beispiel #13
0
def api_user_delete():
    # Allow the use of a mock DB during testing
    if app.config['TESTING']:
        DB_CON = app.config['TEST_DB']
    else:
        DB_CON = None

    # Reject non-JSON payload
    if not request.json:
        raise error_handlers.InvalidAPIUsage("bad payload format",
                                             status_code=400)

    request_content = request.get_json()
    if "userid" not in request_content:
        raise error_handlers.InvalidAPIUsage("bad payload", status_code=400)

    user_id = html.escape(request_content['userid'])

    try:
        result = jots.pyauth.user.delete_user(user_id, db=DB_CON)
        return jsonify({"result": str(result)})
    except jots.pyauth.user.UserActionError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)
Beispiel #14
0
def reset_form():
    ''' Accepts password reset form data
      Validates reset code and sets password
      Returns to login if OK
  '''
    # Allow the use of a mock DB during testing
    if app.config['TESTING']:
        DB_CON = app.config['TEST_DB']
    else:
        DB_CON = None

    valid_reset_status = ['new', 'reset']

    form_data = request.form

    username = html.escape(form_data['username'])
    password = form_data['password']
    reset_code = html.escape(form_data['resetcode'])

    try:
        user = jots.pyauth.user.user(email_address=username, db=DB_CON)
    except jots.pyauth.user.UserNotFound:
        raise error_handlers.InvalidAPIUsage("access denied", status_code=403)
    except jots.pyauth.user.InputError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)

    # Check user status is suitable for a password reset
    if user.properties.status not in valid_reset_status:
        raise error_handlers.InvalidAPIUsage("password not resetable",
                                             status_code=400)

    # Check for matching reset code
    if user.properties.resetCode != reset_code:
        raise error_handlers.InvalidAPIUsage("invalid reset code",
                                             status_code=403)

    # Check reset code hasn't expired
    date_now = datetime.datetime.now()
    reset_expiry = datetime.datetime.strptime(user.properties.resetExpiry,
                                              "%d/%m/%YT%H:%M:%S.%f")
    if date_now > reset_expiry:
        raise error_handlers.InvalidAPIUsage("expired reset code",
                                             status_code=403)

    try:
        user.set_password(password)
        response = make_response(redirect("/"))
        return response
    except jots.pyauth.user.InputError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)
    except jots.pyauth.user.UserActionError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)
Beispiel #15
0
def api_findapps():
    # Allow the use of a mock DB during testing
    if app.config['TESTING']:
        DB_CON = app.config['TEST_DB']
    else:
        DB_CON = None

    # Reject non-JSON payload
    if not request.json:
        raise error_handlers.InvalidAPIUsage("bad payload format",
                                             status_code=400)

    request_content = request.get_json()
    if 'appname' not in request_content:
        raise error_handlers.InvalidAPIUsage("bad payload", status_code=400)

    app_name = html.escape(request_content['appname'])

    try:
        response = jots.pyauth.app.find_apps_like(app_name, db=DB_CON)
        return jsonify(response)

    except jots.pyauth.app.AppNotFound:
        return jsonify(dict())
Beispiel #16
0
def api_findusers():
    # Allow the use of a mock DB during testing
    if app.config['TESTING']:
        DB_CON = app.config['TEST_DB']
    else:
        DB_CON = None

    # Reject non-JSON payload
    if not request.json:
        raise error_handlers.InvalidAPIUsage("bad payload format",
                                             status_code=400)

    request_content = request.get_json()
    if 'email' not in request_content:
        raise error_handlers.InvalidAPIUsage("bad payload", status_code=400)

    email_address = html.escape(request_content['email'])

    try:
        response = jots.pyauth.user.find_users_like(email_address, db=DB_CON)
        return jsonify(response)

    except jots.pyauth.group.GroupNotFound:
        return jsonify(dict())
Beispiel #17
0
def api_groupmember_remove(group_id):
    # Allow the use of a mock DB during testing
    if app.config['TESTING']:
        DB_CON = app.config['TEST_DB']
    else:
        DB_CON = None

    group_id = html.escape(group_id)

    # Reject non-JSON payload
    if not request.json:
        raise error_handlers.InvalidAPIUsage("bad payload format",
                                             status_code=400)

    request_content = request.get_json()
    if 'userid' not in request_content:
        raise error_handlers.InvalidAPIUsage("bad payload", status_code=400)

    user_id = html.escape(request_content['userid'])

    try:
        group = jots.pyauth.group.group(group_id=group_id, db=DB_CON)
        new_member_list = group.remove_member(user_id=user_id, force=True)
    except jots.pyauth.group.GroupNotFound:
        raise error_handlers.InvalidAPIUsage("group not found",
                                             status_code=400)
    except jots.pyauth.group.InputError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)
    except jots.pyauth.group.GroupActionError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)

    try:
        new_member_list = group.get_members_detail(attribute="email")
        return jsonify(new_member_list)
    except jots.pyauth.group.GroupActionError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)
    except jots.pyauth.group.InputError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)
Beispiel #18
0
def api_set_user_attribute(user_id, user_attribute):
    # Allow the use of a mock DB during testing
    if app.config['TESTING']:
        DB_CON = app.config['TEST_DB']
    else:
        DB_CON = None

    # Reject non-JSON payload
    if not request.json:
        raise error_handlers.InvalidAPIUsage("bad payload format",
                                             status_code=400)

    request_content = request.get_json()
    if "value" not in request_content:
        raise error_handlers.InvalidAPIUsage("bad payload", status_code=400)

    user_id = html.escape(user_id)
    user_attribute = html.escape(user_attribute)
    attribute_value = html.escape(request_content['value'])

    try:
        user = jots.pyauth.user.user(user_id=user_id, db=DB_CON)
    except jots.pyauth.user.UserNotFound:
        raise error_handlers.InvalidAPIUsage("invalid user", status=403)
    except jots.pyauth.user.InputError as err:
        raise error_handlers.InvalidAPIUsage(err.message, status_code=400)

    # If changing status to reset, we should trigger a password reset
    # Else use attribute change method
    if user_attribute == "status" and attribute_value == "reset":
        try:
            reset_code = user.reset_password(
                service_domain=app.config['DOMAIN_NAME'])
        except jots.pyauth.user.UserActionError as err:
            raise error_handlers.InvalidAPIUsage(err.message, status_code=400)
        except jots.pyauth.user.InputError as err:
            raise error_handlers.InvalidAPIUsage(err.message, status_code=400)

        # Wmail a link to the 'reset' page with a query string (q) containing reset code
        try:
            email_obj = mailer.personalised_email(
                recipient=user.properties.email,
                template_name="reset",
                data={
                    "site_name":
                    app.config['DOMAIN_NAME'],
                    "reset_url":
                    "https://{}:{}/reset?q={}".format(
                        app.config['DOMAIN_NAME'], app.config['SERVER_PORT'],
                        reset_code)
                })
            if app.config['TESTING']:
                email_obj.send(mail_agent="file")
            else:
                email_obj.send()
        except mailer.InputError as err:
            raise error_handlers.InvalidAPIUsage(err.message, status_code=400)
        except mailer.MailActionError as err:
            raise error_handlers.InvalidAPIUsage(err.message, status_code=400)

        return jsonify(
            {"new_value": user.properties.as_dict()[user_attribute]})

    else:
        try:
            user.update_named_attribute(user_attribute, attribute_value)
            return jsonify(
                {"new_value": user.properties.as_dict()[user_attribute]})
        except jots.pyauth.user.InputError as err:
            raise error_handlers.InvalidAPIUsage(err.message, status_code=400)
        except jots.pyauth.user.UserActionError as err:
            raise error_handlers.InvalidAPIUsage(err.message, status_code=400)