Пример #1
0
class CSREndpoints(object):
    def __init__(self, core):
        self._core = weakref.ref(core)
        self._certs = Certs()
        self._auto_allow_csr = False

    @property
    def auto_allow_csr(self):
        return self._auto_allow_csr

    @auto_allow_csr.setter
    def auto_allow_csr(self, value):
        self._auto_allow_csr = value

    def get_routes(self):
        """
        Returns a list of tuples with the routes for authentication.

        Tuple should have the following:

            - regular expression for calling the endpoint
            - 'callable' keyword specifying that a method is being specified
            - the method that should be used to call when the regular expression matches

        code:

            return [
                (re.compile('^/csr/request_new$'), 'callable', self._csr_request_new)
            ]

        :return:
        """
        return [(re.compile('^/csr/request_new$'), 'callable',
                 self._csr_request_new)]

    def _csr_request_new(self, env, data):
        _log.debug("New csr request")
        if not isinstance(data, dict):
            try:
                request_data = jsonapi.loads(data)
            except:
                _log.error(
                    "Invalid data for csr request.  Must be json serializable")
                return Response()
        else:
            request_data = data.copy()

        csr = request_data.get('csr').encode("utf-8")
        identity = self._certs.get_csr_common_name(csr)

        # The identity must start with the current instances name or it is a failure.
        if not identity.startswith(get_platform_instance_name() + "."):
            json_response = dict(
                status="ERROR",
                message="CSR must start with instance name: {}".format(
                    get_platform_instance_name()))
            Response(jsonapi.dumps(json_response),
                     content_type='application/json',
                     headers={'Content-type': 'application/json'})

        csr_file = self._certs.save_pending_csr_request(
            env.get('REMOTE_ADDR'), identity, csr)

        if self._auto_allow_csr:
            _log.debug(
                "Creating cert and permissions for user: {}".format(identity))
            status = self._certs.get_csr_status(identity)
            json_response = dict(status=status)
            if status == 'APPROVED':
                try:
                    json_response['cert'] = self._certs.get_cert_from_csr(
                        identity)
                except Exception as e:
                    _log.error(f"Exception getting cert from csr {e}")

            else:
                try:
                    cert = self._certs.approve_csr(identity)
                    #permissions = self._core().rmq_mgmt.get_default_permissions(identity)
                    _log.debug(f"CREATING NEW RMQ USER: {identity}")
                    permissions = dict(configure=".*", read=".*", write=".*")
                    self._core().rmq_mgmt.create_user_with_permissions(
                        identity, permissions, True)
                    json_response = dict(status="SUCCESSFUL", cert=cert)
                except BaseException as e:
                    _log.error(
                        f"Exception in approving csr/creating user in auto_allow_csr mode: {e}"
                    )
        else:

            status = self._certs.get_csr_status(identity)
            cert = self._certs.get_cert_from_csr(identity)

            json_response = dict(status=status)
            if status == "APPROVED":
                json_response['cert'] = self._certs.get_cert_from_csr(identity)
            elif status == "PENDING":
                json_response[
                    'message'] = "The request is pending admininstrator approval."
            elif status == "DENIED":
                json_response[
                    'message'] = "The request has been denied by the administrator."
            elif status == "UNKNOWN":
                json_response[
                    'message'] = "An unknown common name was specified to the server {}".format(
                        identity)
            else:
                json_response[
                    'message'] = "An unkonwn error has occured during the respons phase"

        response = None
        try:
            if json_response.get('cert', None):
                json_response['cert'] = json_response['cert'].decode('utf-8')
            response = Response(jsonapi.dumps(json_response),
                                content_type='application/json',
                                headers={'Content-type': 'application/json'})
        except BaseException as e:
            _log.error(f"Exception creating Response {e}")
        return response
Пример #2
0
class AdminEndpoints(object):
    def __init__(self, rmq_mgmt, ssl_public_key):

        self._rmq_mgmt = rmq_mgmt
        self._ssl_public_key = ssl_public_key
        self._userdict = None
        self.reload_userdict()
        self._observer = Observer()
        self._observer.schedule(
            FileReloader("web-users.json", self.reload_userdict), get_home())
        self._observer.start()
        self._certs = Certs()

    def reload_userdict(self):
        webuserpath = os.path.join(get_home(), 'web-users.json')
        self._userdict = PersistentDict(webuserpath)

    def get_routes(self):
        """
        Returns a list of tuples with the routes for the adminstration endpoints
        available in it.

        :return:
        """
        return [(re.compile('^/admin.*'), 'callable', self.admin)]

    def admin(self, env, data):
        if len(self._userdict) == 0:
            if env.get('REQUEST_METHOD') == 'POST':
                decoded = dict((k, v if len(v) > 1 else v[0])
                               for k, v in urlparse.parse_qs(data).iteritems())
                username = decoded.get('username')
                pass1 = decoded.get('password1')
                pass2 = decoded.get('password2')
                if pass1 == pass2 and pass1 is not None:
                    _log.debug("Setting master password")
                    self.add_user(username, pass1, groups=['admin'])
                    return Response('',
                                    status='302',
                                    headers={'Location': '/admin/login.html'})

            template = template_env(env).get_template('first.html')
            return Response(template.render())

        if 'login.html' in env.get('PATH_INFO') or '/admin/' == env.get(
                'PATH_INFO'):
            template = template_env(env).get_template('login.html')
            return Response(template.render())

        return self.verify_and_dispatch(env, data)

    def verify_and_dispatch(self, env, data):
        """ Verify that the user is an admin and dispatch

        :param env: web environment
        :param data: data associated with a web form or json/xml request data
        :return: Response object.
        """
        from volttron.platform.web import get_user_claims, NotAuthorized
        try:
            claims = get_user_claims(env)
        except NotAuthorized:
            _log.error("Unauthorized user attempted to connect to {}".format(
                env.get('PATH_INFO')))
            return Response('<h1>Unauthorized User</h1>',
                            status="401 Unauthorized")

        # Make sure we have only admins for viewing this.
        if 'admin' not in claims.get('groups'):
            return Response('<h1>Unauthorized User</h1>',
                            status="401 Unauthorized")

        # Make sure we have only admins for viewing this.
        if 'admin' not in claims.get('groups'):
            return Response('<h1>Unauthorized User</h1>',
                            status="401 Unauthorized")

        path_info = env.get('PATH_INFO')
        if path_info.startswith('/admin/api/'):
            return self.__api_endpoint(path_info[len('/admin/api/'):], data)

        if path_info.endswith('html'):
            page = path_info.split('/')[-1]
            try:
                template = template_env(env).get_template(page)
            except TemplateNotFound:
                return Response("<h1>404 Not Found</h1>",
                                status="404 Not Found")

            if page == 'list_certs.html':
                html = template.render(
                    certs=self._certs.get_all_cert_subjects())
            elif page == 'pending_csrs.html':
                html = template.render(
                    csrs=self._certs.get_pending_csr_requests())
            else:
                # A template with no params.
                html = template.render()

            return Response(html)

        template = template_env(env).get_template('index.html')
        resp = template.render()
        return Response(resp)

    def __api_endpoint(self, endpoint, data):
        _log.debug("Doing admin endpoint {}".format(endpoint))
        if endpoint == 'certs':
            response = self.__cert_list_api()
        elif endpoint == 'pending_csrs':
            response = self.__pending_csrs_api()
        elif endpoint.startswith('approve_csr/'):
            response = self.__approve_csr_api(endpoint.split('/')[1])
        elif endpoint.startswith('deny_csr/'):
            response = self.__deny_csr_api(endpoint.split('/')[1])
        elif endpoint.startswith('delete_csr/'):
            response = self.__delete_csr_api(endpoint.split('/')[1])
        else:
            response = Response(
                '{"status": "Unknown endpoint {}"}'.format(endpoint),
                content_type="application/json")
        return response

    def __approve_csr_api(self, common_name):
        try:
            _log.debug("Creating cert and permissions for user: {}".format(
                common_name))
            self._certs.approve_csr(common_name)
            permissions = self._rmq_mgmt.get_default_permissions(common_name)
            self._rmq_mgmt.create_user_with_permissions(
                common_name, permissions, True)
            data = dict(status=self._certs.get_csr_status(common_name),
                        cert=self._certs.get_cert_from_csr(common_name))
        except ValueError as e:
            data = dict(status="ERROR", message=e.message)

        return Response(json.dumps(data), content_type="application/json")

    def __deny_csr_api(self, common_name):
        try:
            self._certs.deny_csr(common_name)
            data = dict(status="DENIED",
                        message="The administrator has denied the request")
        except ValueError as e:
            data = dict(status="ERROR", message=e.message)

        return Response(json.dumps(data), content_type="application/json")

    def __delete_csr_api(self, common_name):
        try:
            self._certs.delete_csr(common_name)
            data = dict(status="DELETED",
                        message="The administrator has denied the request")
        except ValueError as e:
            data = dict(status="ERROR", message=e.message)

        return Response(json.dumps(data), content_type="application/json")

    def __pending_csrs_api(self):
        csrs = [c for c in self._certs.get_pending_csr_requests()]
        return Response(json.dumps(csrs), content_type="application/json")

    def __cert_list_api(self):

        subjects = [
            dict(common_name=x.common_name)
            for x in self._certs.get_all_cert_subjects()
        ]
        return Response(json.dumps(subjects), content_type="application/json")

    def add_user(self, username, unencrypted_pw, groups=[], overwrite=False):
        if self._userdict.get(username):
            raise ValueError("Already exists!")
        if groups is None:
            groups = []
        hashed_pass = argon2.hash(unencrypted_pw)
        self._userdict[username] = dict(hashed_password=hashed_pass,
                                        groups=groups)
        self._userdict.async_sync()