# Copyright (c) 2019. Tobias Kurze """ This module provides functions related to authentication through the API. For example: listing of available auth providers or registration of users. Login through API does not start a new session, but instead returns JWT. """ from datetime import datetime from pprint import pprint from flask_jwt_extended import jwt_required from flask_restplus import fields, Resource, inputs from backend import db, app from backend.api import api_recorder from backend.models.recorder_model import Recorder, RecorderModel, RecorderCommand from backend.models.room_model import Room import backend.recorder_adapters as r_a recorder_model = api_recorder.model('Recorder', { 'id': fields.String(required=False, description='The recorder\'s identifier'), 'created_at': fields.DateTime(required=False, description='Creation date of the recorder'), 'last_time_modified': fields.DateTime(required=False, description='Creation date of the recorder'), 'name': fields.String(min_length=3, required=True, description='The recorder\'s name'), 'description': fields.String(required=False, description='The recorder\'s description'), 'locked': fields.Boolean(required=False, description='Indicates whether the recorder settings can be altered'), 'lock_message': fields.String(required=False, description='Optional: message explaining lock state'), 'offline': fields.Boolean(required=False, description='Should be set when recorder is disconnected for maintenance, etc.'), 'ip': fields.String(required=False, description='The recorder\'s IP address'), 'network_name': fields.String(required=False, description='The recorder\'s network name'), 'ssh_port': fields.Integer(required=True, default=22, description='The recorder\'s SSH port number'), 'telnet_port': fields.Integer(required=True, default=23, description='The recorder\'s telnet port number'), # 'use_telnet_instead_ssh': fields.Boolean(required=False, default=False, # description='If this is set, telnet will be used instead of ssh. ' # 'This might require specific commands.'), 'recorder_model': fields.Nested(api_recorder.model('recorder_model', {'id': fields.Integer(), 'name': fields.String(attribute="model_name", )}), required=False, allow_null=True, skip_none=False, description='Model of the recorder.'), 'room': fields.Nested(api_recorder.model('recorder_room', {'id': fields.Integer(), 'name': fields.String(), 'number': fields.String(), 'alternate_name': fields.String()}), r0equired=False, allow_null=True, skip_none=False, description='Room in which the recorder is located.'), 'virtual_commands': fields.List(fields.Nested(api_recorder.model('recorder_virtual_commands', {'id': fields.Integer(), 'name': fields.String()}))) }) recorder_command_model = api_recorder.model('Recorder Command', { 'id': fields.String(required=False, description='The recorder command\'s identifier'), 'name': fields.String(required=True, description='The recorder command\'s name'), 'alternative_name': fields.String(required=False, description='The recorder command\'s alternative name'), 'disabled': fields.Boolean(required=False, description='Indicates if the recorder command is disabled'), 'created_at': fields.DateTime(required=False, description='Creation date of the recorder'), 'last_time_modified': fields.DateTime(required=False), 'description': fields.String(required=False, description='The recorder command\'s description'), 'parameters': fields.Raw(required=True, description='The recorder parameters'), 'recorder_model': fields.Nested(api_recorder.model('recorder_command_models', {'id': fields.Integer(), 'name': fields.String(attribute="model_name", )})), }) recorder_model_model = api_recorder.model('Recorder Model', { 'id': fields.String(required=False, description='The recorder model\'s identifier'), 'name': fields.String(attribute="model_name", required=True, description='The recorder model\'s name'), 'created_at': fields.DateTime(required=False, description='Creation date of the recorder'), 'last_time_modified': fields.DateTime(required=False), 'notes': fields.String(required=False, description='The recorder model\'s notes'), 'requires_username': fields.Boolean(), 'requires_password': fields.Boolean(), 'recorders': fields.List(fields.Nested(api_recorder.model('recorder_model', {'id': fields.Integer(), 'name': fields.String(attribute="model_name", ), 'network_name': fields.String(), 'ip': fields.String()})), required=False, description='Model of the recorder.'), 'commands': fields.List(fields.Nested(recorder_command_model), attribute="recorder_commands") }) # == @api_recorder.route('/') @api_recorder.response(404, 'Recorder not found') @api_recorder.param('id', 'The recorder identifier') class RecorderResource(Resource): @jwt_required @api_recorder.doc('get_recorder') @api_recorder.marshal_with(recorder_model, skip_none=False) def get(self, id): """Fetch a recorder given its identifier""" recorder = Recorder.query.get(id) if recorder is not None: return recorder api_recorder.abort(404) @jwt_required @api_recorder.doc('delete_todo') @api_recorder.response(204, 'Todo deleted') def delete(self, id): """Delete a recorder given its identifier""" recorder = Recorder.query.get(id) if recorder is not None: db.session.delete(recorder) db.session.commit() return '', 204 api_recorder.abort(404) recorder_update_parser = api_recorder.parser() recorder_update_parser.add_argument('name', type=str, required=False, nullable=False, store_missing=False) recorder_update_parser.add_argument('network_name', type=inputs.regex(inputs.netloc_regex), required=False, store_missing=False) recorder_update_parser.add_argument('ip', type=inputs.ipv4, required=False, store_missing=False) recorder_update_parser.add_argument('ip6', type=inputs.ipv6, required=False, store_missing=False) recorder_update_parser.add_argument('ssh_port', type=inputs.int_range(0,65535), required=False, default=22, store_missing=False) recorder_update_parser.add_argument('telnet_port', type=inputs.int_range(0,65535), required=False, default=23, store_missing=False) recorder_update_parser.add_argument('room_id', type=int, required=False, store_missing=False) recorder_update_parser.add_argument('offline', type=inputs.boolean, required=False, default=False, store_missing=False) recorder_update_parser.add_argument('locked', type=inputs.boolean, required=False, default=False, store_missing=False) recorder_update_parser.add_argument('lock_message', type=str, required=False, nullable=True, default=None, store_missing=False) recorder_update_parser.add_argument('model_id', type=int, required=False, store_missing=False) recorder_update_parser.add_argument('description', type=str, required=False, nullable=True, default=None, store_missing=False) recorder_update_parser.add_argument('virtual_command_ids', action='split', nullable=True, default=[], required=False, store_missing=False) @jwt_required @api_recorder.doc('update_recorder') @api_recorder.expect(recorder_model) def put(self, id): """Update a recorder given its identifier""" args = self.recorder_update_parser.parse_args(strict=True) args['last_time_modified'] = datetime.utcnow() pprint(args) num_rows_matched = Recorder.query.filter_by(id=id).update(args) print(num_rows_matched) if num_rows_matched < 1: api_recorder.abort(404) db.session.commit() return "ok" @api_recorder.route('') class RecorderList(Resource): @jwt_required @api_recorder.doc('recorders') @api_recorder.marshal_list_with(recorder_model, skip_none=False) def get(self): """ List all recorders :return: recorders """ return Recorder.get_all() @jwt_required @api_recorder.doc('create_recorder') @api_recorder.expect(recorder_model) @api_recorder.marshal_with(recorder_model, skip_none=False, code=201) def post(self): if "room_id" in api_recorder.payload: if api_recorder.payload["room_id"] is None: api_recorder.payload["room"] = None else: room = Room.query.get(api_recorder.payload["room_id"]) if room is not None: api_recorder.payload["room"] = room else: return "specified room (id: {}) does not exist!".format(api_recorder.payload["room_id"]), 404 if "recorder_model_id" in api_recorder.payload: if api_recorder.payload["recorder_model_id"] is None: api_recorder.payload["recorder_model"] = None else: rec_model = RecorderModel.query.get(api_recorder.payload["recorder_model_id"]) if rec_model is not None: api_recorder.payload["recorder_model"] = rec_model else: return "specified recorder model (id: {}) does not exist!".format( api_recorder.payload["recorder_model_id"]), 404 recorder = Recorder(**api_recorder.payload) db.session.add(recorder) db.session.commit() return recorder @api_recorder.route('/model/') @api_recorder.response(404, 'Recorder Model not found') @api_recorder.param('id', 'The recorder model identifier') class RecorderModelResource(Resource): @jwt_required @api_recorder.doc('get_recorder_model') @api_recorder.marshal_with(recorder_model_model) def get(self, id): """Fetch a recorder model given its identifier""" recorder_model = RecorderModel.query.get(id) if recorder_model is not None: return recorder_model api_recorder.abort(404) recorder_model_parser = api_recorder.parser() recorder_model_parser.add_argument('notes', type=str, required=True) @jwt_required @api_recorder.doc('update_recorder_model') @api_recorder.expect(recorder_model_parser) @api_recorder.marshal_with(recorder_model_model) def put(self, id): """Update a recorder_model given its identifier""" num_rows_matched = RecorderModel.query.filter_by(id=id).update(api_recorder.payload) if num_rows_matched < 1: api_recorder.abort(404) db.session.commit() return "ok" @api_recorder.route('/model') class RecorderModelList(Resource): @jwt_required @api_recorder.doc('recorders') @api_recorder.marshal_list_with(recorder_model_model) def get(self): return RecorderModel.get_all() @api_recorder.route('/command/') @api_recorder.response(404, 'Recorder Command not found') @api_recorder.param('id', 'The recorder command identifier') class RecorderCommandResource(Resource): @jwt_required @api_recorder.doc('get_recorder_command') @api_recorder.marshal_with(recorder_command_model) def get(self, id): """Fetch a recorder command given its identifier""" recorder_command = RecorderCommand.query.get(id) if recorder_command is not None: return recorder_command api_recorder.abort(404) recorder_command_model_parser = api_recorder.parser() recorder_command_model_parser.add_argument('description', type=str, required=False) recorder_command_model_parser.add_argument('alternative_name', type=str, required=False) @jwt_required @api_recorder.doc('update_recorder_command') @api_recorder.expect(recorder_command_model_parser) @api_recorder.marshal_with(recorder_command_model) def put(self, id): """Update a recorder command given its identifier""" num_rows_matched = RecorderCommand.query.filter_by(id=id).update(api_recorder.payload) if num_rows_matched < 1: api_recorder.abort(404) db.session.commit() return "ok" @api_recorder.route('/command') class RecorderCommandList(Resource): @jwt_required @api_recorder.doc('recorder_commands') @api_recorder.marshal_list_with(recorder_command_model) def get(self): """ List all recorders commands :return: recorder commands """ return RecorderCommand.get_all()