def users_person_signup_new(): """ --- post: summary: Allows a person to sign up (first step) tags: - Users description: Creates a new person (unconfirmed, with no access rights and without access to any account) and sends a welcome e-mail. parameters: - name: "body" in: body description: "Person data" required: true schema: "$ref": '#/definitions/PersonSignupNewPOST' responses: 204: description: Request was accepted 400: description: Invalid parameters 403: description: Signup disabled """ if os.environ.get('ENABLE_SIGNUP', 'false').lower() not in ['true', 'yes', 'on', '1']: return "Signup disabled", 403 if os.environ.get('SIGNUP_DISALLOW_TOR', 'true').lower() in ['true', 'yes', 'on', '1']: # if user is coming from Tor exit node, disallow signup: client_ip = flask.request.environ.get('HTTP_X_FORWARDED_FOR', None) if not client_ip: return "Could not determine client IP", 403 if _is_tor_exit_node(client_ip): return "Sorry, Tor exit nodes are not allowed to signup due to abuse", 403 user_id, confirm_pin = Person.signup_new(flask.request.get_json()) person_data = Person.get(user_id) mail_subject = "Welcome to Grafolean!" # unless explicitly set otherwise, assume that backend and frontend have the same origin: frontend_origin = os.environ.get('FRONTEND_ORIGIN', flask.request.url_root).rstrip('/') mail_body_text = _generate_signup_mail_message(person_data['name'], person_data['email'], frontend_origin, user_id, confirm_pin) msg = Message(mail_subject, sender="*****@*****.**", recipients=[person_data['email']], body=mail_body_text) mail = Mail(flask.current_app) with mail.record_messages() as outbox: mail.send(msg) flask.g.outbox = outbox # make sent messages accessible to tests return "", 204
def users_persons(): """ --- get: summary: Get all persons tags: - Users description: Returns a list of all persons. The list is returned in a single array (no pagination). responses: 200: content: application/json: schema: type: object properties: list: type: array items: "$ref": '#/definitions/PersonGET' post: summary: Create a person account tags: - Users description: Creates a person account. By default (as any user) a person is without permissions, so they must be granted to it before it can do anything useful. parameters: - name: "body" in: body description: "Person data" required: true schema: "$ref": '#/definitions/PersonPOST' responses: 201: content: application/json: schema: type: object properties: id: type: integer """ if flask.request.method in ['GET', 'HEAD']: rec = Person.get_list() return json.dumps({'list': rec}), 200 elif flask.request.method == 'POST': person = Person.forge_from_input(flask.request.get_json()) user_id = person.insert() mqtt_publish_changed([ 'persons', ]) return json.dumps({ 'id': user_id, }), 201
def users_person_forgot_password(): """ --- post: summary: Sends an email with a reset password link tags: - Users description: If given an existing e-mail address, sends an e-mail message with a password reset link. parameters: - name: "body" in: body description: "E-mail address" required: true schema: "$ref": '#/definitions/ForgotPasswordPOST' responses: 204: description: Request was accepted 400: description: Invalid parameters 500: description: Mail sending not setup """ if not flask.current_app.config.get('MAIL_SERVER', None): return "Mail sending not setup", 500 user_id, confirm_pin = Person.forgot_password(flask.request.get_json()) if not user_id: return "Email does not correspond to any registered user", 400 person_data = Person.get(user_id) mail_subject = "Grafolean password reset link" # unless explicitly set otherwise, assume that backend and frontend have the same origin: frontend_origin = os.environ.get('FRONTEND_ORIGIN', flask.request.url_root).rstrip('/') mail_body_text = _generate_forgot_password_message(frontend_origin, user_id, confirm_pin) msg = Message(mail_subject, sender="*****@*****.**", recipients=[person_data['email']], body=mail_body_text) mail = Mail(flask.current_app) with mail.record_messages() as outbox: mail.send(msg) flask.g.outbox = outbox # make sent messages accessible to tests return "", 204
def users_person_forgot_password_reset(): """ --- post: summary: Resets a forgotten password tags: - Users description: Resets person's password if person exists, confirmation pin is correct and less than 1 hour old and person account is confirmed. parameters: - name: "body" in: body description: "E-mail address" required: true schema: "$ref": '#/definitions/ForgotPasswordResetPOST' responses: 204: description: Password was reset 400: description: Invalid parameters """ success = Person.forgot_password_reset(flask.request.get_json()) if success: return "", 204 else: return "Password could not be changed - expired / invalid pin or user does not exist", 400
def users_person_signup_complete(): """ --- post: summary: Complete the signup process tags: - Users description: Completes the user's signup process by supplying a valid confirmation pin and a new password. parameters: - name: "body" in: body description: "Pin and password" required: true schema: "$ref": '#/definitions/PersonSignupCompletePOST' responses: 204: description: Pin is valid 400: description: Invalid parameters 403: description: Signup disabled """ if os.environ.get('ENABLE_SIGNUP', 'false').lower() not in ['true', 'yes', 'on', '1']: return "Signup disabled", 403 status = Person.signup_complete(flask.request.get_json(), create_account=True) if status: return "", 204 else: return "Invalid pin, invalid user id, or signup already completed", 400
def users_person_signup_validatepin(): """ --- post: summary: Checks if confirmation pin is valid tags: - Users description: Checks if confirmation pin is valid (but doesn't complete the signup yet). This allows frontend to ask for a password before completing the signup process. parameters: - name: "body" in: body description: "Pin" required: true schema: "$ref": '#/definitions/PersonSignupValidatePinPOST' responses: 204: description: Pin is valid 400: description: Invalid parameters 403: description: Signup disabled """ if os.environ.get('ENABLE_SIGNUP', 'false').lower() not in ['true', 'yes', 'on', '1']: return "Signup disabled", 403 confirm_pin_valid = Person.signup_pin_valid(flask.request.get_json()) if confirm_pin_valid: return "", 204 else: return "Invalid pin, invalid user id, or signup already completed", 400
def admin_first_post(): """ --- post: summary: Create first admin user tags: - Admin description: This endpoint helps with setting up a new installation. It allows us to set up just one initial admin access (with name, email and password). Later requests to the same endpoint will fail. At the same time a systemwide (ICMP ping) bot is configured and its token shared via file with a grafolean-ping-bot Docker container (in default setup). parameters: - name: "body" in: body description: "First admin data and credentials" required: true schema: "$ref": '#/definitions/PersonPOST' responses: 201: content: application/json: schema: type: object properties: id: type: integer description: "User id of created admin" 401: description: System already initialized """ if Auth.first_user_exists(): return 'System already initialized', 401 admin = Person.forge_from_input(flask.request.get_json()) admin_id = admin.insert() # make it a superuser: permission = Permission(admin_id, None, None) permission.insert(None, skip_checks=True) # Help users by including a systemwide ping bot in the package by default: Bot.ensure_default_systemwide_bots_exist() mqtt_publish_changed(['status/info']) return json.dumps({ 'id': admin_id, }), 201
def profile(): user_id = flask.g.grafolean_data['user_id'] user_is_bot = flask.g.grafolean_data['user_is_bot'] if user_is_bot: tied_to_account = Bot.get_tied_to_account(user_id) return json.dumps({ 'user_id': user_id, 'user_type': 'bot', 'record': Bot.get(user_id, tied_to_account=tied_to_account), }), 200 else: return json.dumps({ 'user_id': user_id, 'user_type': 'person', 'record': Person.get(user_id), }), 200
__author__ = 'Justice Ndou' __website__ = 'http://jobcloud.freelancing-seo.com/' __email__ = '*****@*****.**' import os import webapp2 import jinja2 from ConstantsAndErrorCodes import MyConstants, ErrorCodes, isGoogleServer from google.appengine.ext import db from datatypes import Reference, Person from google.appengine.api import mail import logging from google.appengine.api import users User = Person() # Jinja Loader template_env = jinja2.Environment( loader=jinja2.FileSystemLoader(os.getcwd())) scripts = ''' <link href="http://freelancing-solutions.freelancing-seo.com/jQueryAssets/jquery.ui.core.min.css" rel="stylesheet" type="text/css"> <link href="http://freelancing-solutions.freelancing-seo.com/jQueryAssets/jquery.ui.theme.min.css" rel="stylesheet" type="text/css"> <link href="http://freelancing-solutions.freelancing-seo.com/jQueryAssets/jquery.ui.tabs.min.css" rel="stylesheet" type="text/css"> <script src="http://freelancing-solutions.freelancing-seo.com/js/jquery.min.js"></script> <script src="http://freelancing-solutions.freelancing-seo.com/js/config.js"></script> <script src="http://freelancing-solutions.freelancing-seo.com/js/skel.min.js"></script> <script src="http://freelancing-solutions.freelancing-seo.com/js/skel-panels.min.js"></script> <noscript> <link rel="stylesheet" href="http://freelancing-solutions.freelancing-seo.com/css/skel-noscript.css" /> <link rel="stylesheet" href="http://freelancing-solutions.freelancing-seo.com/css/style.css" />
def users_person_change_password(user_id): rowcount = Person.change_password(user_id, flask.request.get_json()) if not rowcount: return "Change failed", 400 # no need to publish to mqtt - nobody cares return "", 204
def users_person_crud(user_id): """ --- get: summary: Get person data tags: - Users description: Returns person data. parameters: - name: user_id in: path description: "User id" required: true schema: type: integer responses: 200: content: application/json: schema: "$ref": '#/definitions/PersonGETWithPermissions' 404: description: No such person put: summary: Update the bot tags: - Users description: Updates person data. parameters: - name: user_id in: path description: "User id" required: true schema: type: integer - name: "body" in: body description: "Person data" required: true schema: "$ref": '#/definitions/PersonPOST' responses: 204: description: Update successful 404: description: No such person delete: summary: Remove the person data tags: - Users description: Removes the person data. Also removes user's permissions, if any. parameters: - name: user_id in: path description: "User id" required: true schema: type: integer responses: 204: description: Person data removed successfully 403: description: Can't remove yourself 404: description: No such person """ if flask.request.method in ['GET', 'HEAD']: rec = Person.get(user_id) if not rec: return "No such person", 404 rec['permissions'] = Permission.get_list(user_id) return json.dumps(rec), 200 elif flask.request.method == 'PUT': person = Person.forge_from_input(flask.request.get_json(), force_id=user_id) rowcount = person.update() if not rowcount: return "No such person", 404 mqtt_publish_changed([ 'persons/{user_id}'.format(user_id=user_id), 'persons', ]) return "", 204 elif flask.request.method == 'DELETE': # user should not be able to delete himself, otherwise they could lock themselves out: if int(flask.g.grafolean_data['user_id']) == int(user_id): return "Can't delete yourself", 403 rowcount = Person.delete(user_id) if not rowcount: return "No such person", 404 mqtt_publish_changed([ 'persons/{user_id}'.format(user_id=user_id), 'persons', ]) return "", 204