You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by gs...@apache.org on 2018/01/15 19:43:50 UTC

[1/2] qpid-dispatch git commit: DISPATCH-901: authorization from remote auth service

Repository: qpid-dispatch
Updated Branches:
  refs/heads/master e9f8502ea -> eed8bb654


DISPATCH-901: authorization from remote auth service

If the client specifies its desire for the ADDRESS-AUTHZ capacbility, the authorization service, if it supports this, will return a
set of permissions in the properties of the open frame. The properties will have an address-authz key, whose value is a map of address (or wildcard pattern) to an array of permissions.
The only permissions recognised at present by this patch are 'send' and 'recv'.


Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/eed8bb65
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/eed8bb65
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/eed8bb65

Branch: refs/heads/master
Commit: eed8bb65497b23e512072f5ea9fefd8ed73921ef
Parents: f8a1c9c
Author: Gordon Sim <gs...@redhat.com>
Authored: Fri Dec 15 10:29:21 2017 +0000
Committer: Gordon Sim <gs...@redhat.com>
Committed: Mon Jan 15 19:43:23 2018 +0000

----------------------------------------------------------------------
 src/policy.c                               |  12 +-
 src/remote_sasl.c                          | 234 +++++++++++++++++++++++-
 tests/CMakeLists.txt                       |   2 +
 tests/authservice.py                       |  81 ++++++++
 tests/system_tests_authz_service_plugin.py | 147 +++++++++++++++
 5 files changed, 469 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eed8bb65/src/policy.c
----------------------------------------------------------------------
diff --git a/src/policy.c b/src/policy.c
index 22cc79f..a712f35 100644
--- a/src/policy.c
+++ b/src/policy.c
@@ -321,8 +321,10 @@ bool qd_policy_open_lookup_user(
                     settings->allowAnonymousSender = qd_entity_opt_bool((qd_entity_t*)upolicy, "allowAnonymousSender", false);
                     settings->allowDynamicSource   = qd_entity_opt_bool((qd_entity_t*)upolicy, "allowDynamicSource", false);
                     settings->allowUserIdProxy     = qd_entity_opt_bool((qd_entity_t*)upolicy, "allowUserIdProxy", false);
-                    settings->sources              = qd_entity_get_string((qd_entity_t*)upolicy, "sources");
-                    settings->targets              = qd_entity_get_string((qd_entity_t*)upolicy, "targets");
+                    if (settings->sources == 0)
+                        settings->sources          = qd_entity_get_string((qd_entity_t*)upolicy, "sources");
+                    if (settings->targets == 0)
+                        settings->targets          = qd_entity_get_string((qd_entity_t*)upolicy, "targets");
                     settings->denialCounts         = (qd_policy_denial_counts_t*)
                                                     qd_entity_get_long((qd_entity_t*)upolicy, "denialCounts");
                     Py_XDECREF(result2);
@@ -689,8 +691,10 @@ void qd_policy_amqp_open(qd_connection_t *qd_conn) {
 #define SETTINGS_NAME_SIZE 256
         char settings_name[SETTINGS_NAME_SIZE];
         uint32_t conn_id = qd_conn->connection_id;
-        qd_conn->policy_settings = NEW(qd_policy_settings_t); // TODO: memory pool for settings
-        memset(qd_conn->policy_settings, 0, sizeof(qd_policy_settings_t));
+        if (!qd_conn->policy_settings) {
+            qd_conn->policy_settings = NEW(qd_policy_settings_t); // TODO: memory pool for settings
+            memset(qd_conn->policy_settings, 0, sizeof(qd_policy_settings_t));
+        }
 
         if (qd_policy_open_lookup_user(policy, qd_conn->user_id, hostip, vhost, conn_name,
                                        settings_name, SETTINGS_NAME_SIZE, conn_id,

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eed8bb65/src/remote_sasl.c
----------------------------------------------------------------------
diff --git a/src/remote_sasl.c b/src/remote_sasl.c
index e3c969b..93e6129 100644
--- a/src/remote_sasl.c
+++ b/src/remote_sasl.c
@@ -20,6 +20,7 @@
  */
 
 #include "remote_sasl.h"
+#include "server_private.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -29,6 +30,7 @@
 #include <proton/sasl.h>
 #include <proton/sasl-plugin.h>
 #include <qpid/dispatch/log.h>
+#include <qpid/dispatch/ctools.h>
 
 static qd_log_source_t* auth_service_log;
 
@@ -45,6 +47,45 @@ const int8_t DOWNSTREAM_CHALLENGE_RECEIVED = 4;
 const int8_t DOWNSTREAM_OUTCOME_RECEIVED = 5;
 const int8_t DOWNSTREAM_CLOSED = 6;
 
+typedef struct {
+    size_t used;
+    size_t capacity;
+    char *start;
+} buffer_t;
+
+
+static void allocate_buffer(buffer_t* buffer)
+{
+    buffer->start = malloc(buffer->capacity);
+    memset(buffer->start, 0, buffer->capacity);
+}
+
+static void free_buffer(buffer_t* buffer)
+{
+    free(buffer->start);
+    buffer->start = 0;
+    buffer->capacity = 0;
+    buffer->used = 0;
+}
+
+typedef struct {
+    buffer_t sources;
+    buffer_t targets;
+} permissions_t;
+
+static void init_buffer(buffer_t* buffer)
+{
+    buffer->used = 0;
+    buffer->capacity = 0;
+    buffer->start = 0;
+}
+
+static void init_permissions(permissions_t* permissions)
+{
+    init_buffer(&permissions->sources);
+    init_buffer(&permissions->targets);
+}
+
 typedef struct
 {
     char* authentication_service_address;
@@ -65,6 +106,7 @@ typedef struct
 
     bool complete;
     char* username;
+    permissions_t permissions;
     pn_sasl_outcome_t outcome;
 } qdr_sasl_relay_t;
 
@@ -101,6 +143,7 @@ static qdr_sasl_relay_t* new_qdr_sasl_relay_t(const char* address, const char* s
     instance->upstream = 0;
     instance->downstream = 0;
     instance->username = 0;
+    init_permissions(&instance->permissions);
     return instance;
 }
 
@@ -113,6 +156,8 @@ static void delete_qdr_sasl_relay_t(qdr_sasl_relay_t* instance)
     if (instance->response.start) free(instance->response.start);
     if (instance->challenge.start) free(instance->challenge.start);
     if (instance->username) free(instance->username);
+    free_buffer(&(instance->permissions.targets));
+    free_buffer(&(instance->permissions.sources));
     free(instance);
 }
 
@@ -160,8 +205,13 @@ static bool remote_sasl_init_server(pn_transport_t* transport)
         if (!proactor) return false;
         impl->downstream = pn_connection();
         pn_connection_set_hostname(impl->downstream, pn_connection_get_hostname(upstream));
-        pn_connection_set_user(impl->downstream, "dummy");//force sasl
         set_sasl_relay_context(impl->downstream, impl);
+        //request permissions in response if supported by peer:
+        pn_data_t* data = pn_connection_desired_capabilities(impl->downstream);
+        pn_data_put_array(data, false, PN_SYMBOL);
+        pn_data_enter(data);
+        pn_data_put_symbol(data, pn_bytes(13, "ADDRESS-AUTHZ"));
+        pn_data_exit(data);
 
         pn_proactor_connect(proactor, impl->downstream, impl->authentication_service_address);
         return true;
@@ -207,6 +257,25 @@ static void remote_sasl_free(pn_transport_t *transport)
     }
 }
 
+static void set_policy_settings(pn_connection_t* conn, permissions_t* permissions)
+{
+    if (permissions->targets.start || permissions->sources.start) {
+        qd_connection_t *qd_conn = (qd_connection_t*) pn_connection_get_context(conn);
+        qd_conn->policy_settings = NEW(qd_policy_settings_t);
+        ZERO(qd_conn->policy_settings);
+
+        qd_conn->policy_settings->denialCounts = NEW(qd_policy_denial_counts_t);
+        ZERO(qd_conn->policy_settings->denialCounts);
+
+        if (permissions->targets.start && permissions->targets.capacity) {
+            qd_conn->policy_settings->targets = strdup(permissions->targets.start);
+        }
+        if (permissions->sources.start && permissions->sources.capacity) {
+            qd_conn->policy_settings->sources = strdup(permissions->sources.start);
+        }
+    }
+}
+
 static void remote_sasl_prepare(pn_transport_t *transport)
 {
     qdr_sasl_relay_t* impl = (qdr_sasl_relay_t*) pnx_sasl_get_context(transport);
@@ -231,6 +300,7 @@ static void remote_sasl_prepare(pn_transport_t *transport)
         } else if (impl->upstream_state == DOWNSTREAM_OUTCOME_RECEIVED) {
             switch (impl->outcome) {
             case PN_SASL_OK:
+                set_policy_settings(impl->upstream, &impl->permissions);
                 pnx_sasl_succeed_authentication(transport, impl->username);
                 break;
             default:
@@ -304,9 +374,9 @@ static void remote_sasl_process_outcome(pn_transport_t *transport)
         pn_sasl_t* sasl = pn_sasl(transport);
         if (sasl) {
             impl->outcome = pn_sasl_outcome(sasl);
-            impl->username = strdup(pn_sasl_get_user(sasl));
             impl->complete = true;
-            if (!notify_upstream(impl, DOWNSTREAM_OUTCOME_RECEIVED)) {
+            //only consider complete if failed; if successful wait for the open frame
+            if (impl->outcome != PN_SASL_OK && !notify_upstream(impl, DOWNSTREAM_OUTCOME_RECEIVED)) {
                 pnx_sasl_set_desired_state(transport, SASL_ERROR);
             }
         }
@@ -398,11 +468,142 @@ void qdr_use_remote_authentication_service(pn_transport_t *transport, const char
     set_remote_impl(transport, context);
 }
 
+static bool append(buffer_t* buffer, pn_bytes_t data)
+{
+    if (buffer->capacity > data.size + buffer->used) {
+        if (buffer->used > 0) buffer->start[buffer->used++] = ',';
+        strncpy(buffer->start + buffer->used, data.start, data.size);
+        buffer->used += data.size;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+static size_t min(size_t a, size_t b)
+{
+    if (a > b) return b;
+    else return a;
+}
+
+typedef void* (*permission_handler)(pn_bytes_t, bool, bool, void*);
+
+static void* compute_required_size(pn_bytes_t address, bool send, bool recv, void* context)
+{
+    permissions_t* permissions = (permissions_t*) context;
+    if (send) permissions->targets.capacity += address.size + 1;
+    if (recv) permissions->sources.capacity += address.size + 1;
+    return context;
+}
+
+static void* collect_permissions(pn_bytes_t address, bool send, bool recv, void* context)
+{
+    permissions_t* permissions = (permissions_t*) context;
+    if (send) append(&(permissions->targets), address);
+    if (recv) append(&(permissions->sources), address);
+    return context;
+}
+
+static void* parse_permissions(pn_data_t* data, permission_handler handler, void* initial_context)
+{
+    void* context = initial_context;
+    size_t count = pn_data_get_map(data);
+    pn_data_enter(data);
+    for (size_t i = 0; i < count/2; i++) {
+        if (pn_data_next(data)) {
+            if (pn_data_type(data) == PN_STRING) {
+                pn_bytes_t address = pn_data_get_string(data);
+                if (pn_data_next(data)) {
+                    if (pn_data_type(data) == PN_ARRAY && pn_data_get_array_type(data) == PN_STRING) {
+                        size_t length = pn_data_get_array(data);
+                        pn_data_enter(data);
+                        for (size_t j = 0; j < length; j++) {
+                            if (pn_data_next(data)) {
+                                pn_bytes_t permission = pn_data_get_string(data);
+                                //printf("in permissions map %i of %i is %.*s for %.*s\n", (int) (j+1), (int) length, (int) permission.size, permission.start, (int) address.size, address.start);
+                                bool send = strncmp(permission.start, "send", min(permission.size, 4)) == 0;
+                                bool recv = strncmp(permission.start, "recv", min(permission.size, 4)) == 0;
+
+                                if (send || recv) {
+                                    context = handler(address, send, recv, context);
+                                }
+                            }
+                        }
+                        pn_data_exit(data);
+                    }
+                }
+            } else {
+                //key is not string, consume value to move onto next pair
+                pn_data_next(data);
+            }
+        }
+    }
+    pn_data_exit(data);
+    return context;
+}
+
+static void* parse_properties(pn_data_t* data, permission_handler handler, void* initial_context)
+{
+    void* context = 0;
+    size_t count = pn_data_get_map(data);
+    pn_data_enter(data);
+    for (size_t i = 0; !context && i < count/2; i++) {
+        if (pn_data_next(data)) {
+            if (pn_data_type(data) == PN_SYMBOL) {
+                pn_bytes_t key = pn_data_get_symbol(data);
+                if (key.size && key.start && strncmp(key.start, "address-authz", min(key.size, 13)) == 0) {
+                    pn_data_next(data);
+                    context = parse_permissions(data, handler, initial_context);
+                } else {
+                    //key didn't match, move to next pair
+                    pn_data_next(data);
+                }
+            } else {
+                //key was not symbol, move to next pair
+                pn_data_next(data);
+            }
+        }
+    }
+    pn_data_exit(data);
+    pn_data_rewind(data);
+    pn_data_next(data);
+    return context;
+}
+
+static pn_bytes_t extract_authenticated_identity(pn_data_t* data)
+{
+    pn_bytes_t result = pn_bytes_null;
+    size_t count = pn_data_get_map(data);
+    pn_data_enter(data);
+    for (size_t i = 0; !result.size && i < count/2; i++) {
+        if (pn_data_next(data)) {
+            if (pn_data_type(data) == PN_SYMBOL) {
+                pn_bytes_t key = pn_data_get_symbol(data);
+                if (key.size && key.start && strncmp(key.start, "authenticated-identity", min(key.size, 22)) == 0) {
+                    pn_data_next(data);
+                    result = pn_data_get_string(data);
+                } else {
+                    //key didn't match, move to next pair
+                    pn_data_next(data);
+                }
+            } else {
+                //key was not symbol, move to next pair
+                pn_data_next(data);
+            }
+        }
+    }
+    pn_data_exit(data);
+    pn_data_rewind(data);
+    pn_data_next(data);
+    return result;
+}
+
 void qdr_handle_authentication_service_connection_event(pn_event_t *e)
 {
     pn_connection_t *conn = pn_event_connection(e);
     pn_transport_t *transport = pn_event_transport(e);
     if (pn_event_type(e) == PN_CONNECTION_BOUND) {
+        pn_sasl(transport);
         qd_log(auth_service_log, QD_LOG_DEBUG, "Handling connection bound event for authentication service connection");
         qdr_sasl_relay_t* context = get_sasl_relay_context(conn);
         if (context->ssl_domain) {
@@ -416,6 +617,33 @@ void qdr_handle_authentication_service_connection_event(pn_event_t *e)
         set_remote_impl(pn_event_transport(e), context);
     } else if (pn_event_type(e) == PN_CONNECTION_REMOTE_OPEN) {
         qd_log(auth_service_log, QD_LOG_DEBUG, "authentication against service complete; closing connection");
+
+        qdr_sasl_relay_t* context = get_sasl_relay_context(conn);
+        //extract permissions as two comma separated lists (allowed sources and targets)
+        pn_data_t* properties = pn_connection_remote_properties(conn);
+        if (parse_properties(properties, compute_required_size, (void*) &(context->permissions))) {
+            if (!context->permissions.sources.capacity) {
+                context->permissions.sources.capacity = 1;
+            }
+            if (!context->permissions.targets.capacity) {
+                context->permissions.targets.capacity = 1;
+            }
+            allocate_buffer(&(context->permissions.targets));
+            allocate_buffer(&(context->permissions.sources));
+            parse_properties(properties, collect_permissions, (void*) &(context->permissions));
+            printf("allowed sources %s\n", context->permissions.sources.start);
+            printf("allowed targets %s\n", context->permissions.targets.start);
+        }
+        const pn_bytes_t authid = extract_authenticated_identity(properties);
+        if (authid.start && authid.size) {
+            context->username = strndup(authid.start, authid.size);
+        } else {
+            context->username = strdup("");
+        }
+        //notify upstream connection of successful authentication
+        notify_upstream(context, DOWNSTREAM_OUTCOME_RECEIVED);
+
+        //close downstream connection
         pn_connection_close(conn);
     } else if (pn_event_type(e) == PN_CONNECTION_REMOTE_CLOSE) {
         qd_log(auth_service_log, QD_LOG_DEBUG, "authentication service closed connection");

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eed8bb65/tests/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index a8aedd2..0105c29 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -100,6 +100,7 @@ foreach(py_test_module
     system_tests_failover_list
     system_tests_denied_unsettled_multicast
     system_tests_auth_service_plugin
+    system_tests_authz_service_plugin
     system_tests_delivery_abort
     system_tests_topology
     ${SYSTEM_TESTS_HTTP}
@@ -131,6 +132,7 @@ file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/policy-1/policy-safari.json      DESTINATI
 file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/policy-2/policy-photoserver-sasl.sasldb  DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/policy-2)
 file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/policy-3/test-sender-receiver-limits.json DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/policy-3)
 file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/policy-4/management-access.json  DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/policy-4/)
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/authservice.py  DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
 
 # following install() functions will be called only if you do a make "install"
 install(FILES ${SYSTEM_TEST_FILES}

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eed8bb65/tests/authservice.py
----------------------------------------------------------------------
diff --git a/tests/authservice.py b/tests/authservice.py
new file mode 100755
index 0000000..c46a9ae
--- /dev/null
+++ b/tests/authservice.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from __future__ import print_function, unicode_literals
+import signal, optparse
+from proton.handlers import MessagingHandler
+from proton.reactor import Container
+from proton import Array, Data, symbol, UNDESCRIBED
+from cproton import pn_sasl_config_path
+
+class AuthService(MessagingHandler):
+    def __init__(self, address):
+        super(AuthService, self).__init__()
+        self.address = address
+        self.permissions = {}
+        self.allow('admin', '*', ['send', 'recv'])
+        self.allow('guest', 'foo', ['send', 'recv'])
+        self.listener = None
+
+    def allow(self, user, address, permissions):
+        if not self.permissions.get(user):
+            self.permissions[user] = {}
+        self.permissions[user][address] = Array(UNDESCRIBED, Data.STRING, *permissions)
+
+    def on_start(self, event):
+        self.listener = event.container.listen(self.address)
+
+    def stop(self):
+        if self.listener:
+            self.listener.close()
+
+    def on_connection_opening(self, event):
+        if self.permissions.get(event.transport.user):
+            event.connection.properties = {
+                symbol('authenticated-identity'): "%s" % event.transport.user,
+                symbol('address-authz'): self.permissions[event.transport.user]
+            }
+        else:
+            event.connection.properties = {
+                symbol('authenticated-identity'): "%s" % event.transport.user,
+                symbol('address-authz'): {}
+            }
+
+parser = optparse.OptionParser(usage="usage: %prog [options]",
+                               description="test authentication and authorization service")
+parser.add_option("-a", "--address", default="localhost:55671",
+                  help="address to listen on (default %default)")
+parser.add_option("-c", "--config", help="sasl config path")
+opts, args = parser.parse_args()
+
+print('starting')
+if opts.config:
+    pn_sasl_config_path(None, opts.config)
+    print('set sasl config path to %s' % opts.config)
+
+handler = AuthService(opts.address)
+def sigterm_handler(_signo, _stack_frame):
+    #sys.exit(0)
+    handler.stop()
+
+signal.signal(signal.SIGTERM, sigterm_handler)
+
+Container(handler).run()
+

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eed8bb65/tests/system_tests_authz_service_plugin.py
----------------------------------------------------------------------
diff --git a/tests/system_tests_authz_service_plugin.py b/tests/system_tests_authz_service_plugin.py
new file mode 100644
index 0000000..af84bc0
--- /dev/null
+++ b/tests/system_tests_authz_service_plugin.py
@@ -0,0 +1,147 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+import unittest2 as unitest
+import os, json
+from subprocess import PIPE, Popen, STDOUT
+from system_test import TestCase, Qdrouterd, main_module, DIR, TIMEOUT, Process
+from proton import Array, Data, Message, SASL, symbol, UNDESCRIBED
+from proton.handlers import MessagingHandler
+from proton.reactor import Container
+
+class AuthServicePluginAuthzTest(TestCase):
+    @classmethod
+    def addUser(cls, user, password):
+        # Create a sasl database.
+        p = Popen(['saslpasswd2', '-c', '-p', '-f', 'users.sasldb', user],
+                  stdin=PIPE, stdout=PIPE, stderr=PIPE)
+        result = p.communicate(password)
+        assert p.returncode == 0, "saslpasswd2 exit status %s, output:\n%s" % (p.returncode, result)
+
+    @classmethod
+    def createSaslFiles(cls):
+        cls.addUser('guest', 'guest')
+        cls.addUser('admin', 'admin')
+        # Create a SASL configuration file.
+        with open('tests-mech-SCRAM.conf', 'w') as sasl_conf:
+            sasl_conf.write("""
+mech_list: SCRAM-SHA-1
+""")
+        with open('proton-server.conf', 'w') as sasl_conf:
+            sasl_conf.write("""
+pwcheck_method: auxprop
+auxprop_plugin: sasldb
+sasldb_path: users.sasldb
+mech_list: SCRAM-SHA-1
+""")
+
+
+    @classmethod
+    def setUpClass(cls):
+        """
+        Tests the delegation of sasl auth to an external auth service.
+        """
+        super(AuthServicePluginAuthzTest, cls).setUpClass()
+
+        if not SASL.extended():
+            return
+
+        cls.createSaslFiles()
+
+        cls.auth_service_port = cls.tester.get_port()
+        cls.tester.popen(['/usr/bin/env', 'python', os.path.join(os.path.dirname(os.path.abspath(__file__)), 'authservice.py'), '-a', '127.0.0.1:%d' % cls.auth_service_port, '-c', os.getcwd()], expect=Process.RUNNING)
+
+        cls.router_port = cls.tester.get_port()
+        cls.tester.qdrouterd('router', Qdrouterd.Config([
+                     ('authServicePlugin', {'name':'myauth', 'authService': '127.0.0.1:%d' % cls.auth_service_port}),
+                     ('listener', {'host': '0.0.0.0', 'port': cls.router_port, 'role': 'normal', 'saslPlugin':'myauth', 'saslMechanisms':'SCRAM-SHA-1'}),
+                     ('router', {'mode': 'standalone', 'id': 'router',
+                                 'saslConfigName': 'tests-mech-SCRAM',
+                                 'saslConfigPath': os.getcwd()})
+        ])).wait_ready()
+
+    def test_authorized(self):
+        if not SASL.extended():
+            self.skipTest("Cyrus library not available. skipping test")
+
+        container = Container()
+        client = ConnectionHandler('foo', 1)
+        container.connect("guest:guest@127.0.0.1:%d" % self.router_port, handler=client)
+        container.run()
+        self.assertEqual(1, client.sent)
+        self.assertEqual(1, client.received)
+        self.assertEqual(0, len(client.errors))
+
+    def test_unauthorized(self):
+        if not SASL.extended():
+            self.skipTest("Cyrus library not available. skipping test")
+
+        container = Container()
+        client = ConnectionHandler('bar', 1)
+        container.connect("guest:guest@127.0.0.1:%d" % self.router_port, handler=client)
+        container.run()
+        self.assertEqual(0, client.sent)
+        self.assertEqual(0, client.received)
+        self.assertEqual(2, len(client.errors))
+        self.assertEqual('amqp:unauthorized-access', client.errors[0])
+        self.assertEqual('amqp:unauthorized-access', client.errors[1])
+
+    def test_wildcard(self):
+        if not SASL.extended():
+            self.skipTest("Cyrus library not available. skipping test")
+
+        container = Container()
+        client = ConnectionHandler('whatever', 1)
+        container.connect("admin:admin@127.0.0.1:%d" % self.router_port, handler=client)
+        container.run()
+        self.assertEqual(1, client.sent)
+        self.assertEqual(1, client.received)
+        self.assertEqual(0, len(client.errors))
+
+
+class ConnectionHandler(MessagingHandler):
+    def __init__(self, address, count):
+        super(ConnectionHandler, self).__init__()
+        self.address = address
+        self.count = count
+        self.received = 0
+        self.sent = 0
+        self.errors = []
+
+    def on_message(self, event):
+        self.received += 1
+        if self.received == self.count:
+            event.connection.close()
+
+    def on_sendable(self, event):
+        if self.sent < self.count:
+            self.sent += 1
+            event.sender.send(Message(body='msg-%s' %self.sent))
+
+    def on_link_error(self, event):
+        self.errors.append(event.link.remote_condition.name)
+        event.connection.close()
+
+    def on_connection_opened(self, event):
+        event.container.create_receiver(event.connection, self.address)
+        event.container.create_sender(event.connection, self.address)
+
+if __name__ == '__main__':
+    unittest.main(main_module())
+


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[2/2] qpid-dispatch git commit: DISPATCH-912: move setting rhost from connection-bound to remote-open event to ensure proton has retrieved the necessary info

Posted by gs...@apache.org.
DISPATCH-912: move setting rhost from connection-bound to remote-open event to ensure proton has retrieved the necessary info


Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/f8a1c9c9
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/f8a1c9c9
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/f8a1c9c9

Branch: refs/heads/master
Commit: f8a1c9c975f3f8d859a4d57ce0d705fe4bd12e68
Parents: e9f8502
Author: Gordon Sim <gs...@redhat.com>
Authored: Mon Jan 15 15:47:23 2018 +0000
Committer: Gordon Sim <gs...@redhat.com>
Committed: Mon Jan 15 19:43:23 2018 +0000

----------------------------------------------------------------------
 src/server.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/f8a1c9c9/src/server.c
----------------------------------------------------------------------
diff --git a/src/server.c b/src/server.c
index 5cf967e..5005cb3 100644
--- a/src/server.c
+++ b/src/server.c
@@ -577,7 +577,9 @@ void connect_fail(qd_connection_t *ctx, const char *name, const char *descriptio
 
 
 /* Get the host IP address for the remote end */
-static void set_rhost_port(qd_connection_t *ctx) {
+static void set_rhost_port(pn_event_t *e) {
+    pn_connection_t *pn_conn = pn_event_connection(e);
+    qd_connection_t *ctx = pn_connection_get_context(pn_conn);
     pn_transport_t *tport  = pn_connection_transport(ctx->pn_conn);
     const struct sockaddr* sa = pn_netaddr_sockaddr(pn_netaddr_remote(tport));
     size_t salen = pn_netaddr_socklen(pn_netaddr_remote(tport));
@@ -592,7 +594,6 @@ static void set_rhost_port(qd_connection_t *ctx) {
     }
 }
 
-
 /* Configure the transport once it is bound to the connection */
 static void on_connection_bound(qd_server_t *server, pn_event_t *e) {
     pn_connection_t *pn_conn = pn_event_connection(e);
@@ -614,7 +615,6 @@ static void on_connection_bound(qd_server_t *server, pn_event_t *e) {
         config = &ctx->listener->config;
         const char *name = config->host_port;
         pn_transport_set_server(tport);
-        set_rhost_port(ctx);
 
         sys_mutex_lock(server->lock); /* Policy check is not thread safe */
         ctx->policy_counted = qd_policy_socket_accept(server->qd->policy, ctx->rhost);
@@ -874,6 +874,7 @@ static bool handle(qd_server_t *qd_server, pn_event_t *e) {
         break;
 
     case PN_CONNECTION_REMOTE_OPEN:
+        set_rhost_port(e);
         // If we are transitioning to the open state, notify the client via callback.
         if (ctx && ctx->timer) {
             qd_timer_free(ctx->timer);


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org