From ecb0e698dfb835d9ecde8cfc76a3189f53ae1a81 Mon Sep 17 00:00:00 2001
From: Sulverus <sulverus@gmail.com>
Date: Mon, 22 Jun 2015 20:26:54 +0300
Subject: [PATCH] gh-844: Authentication trigger feature in iproto

- add a test case for authentication trigger
- pass user name to the trigger
- invoke trigger before the current user has been changed
- after review fixes
---
 src/box/authentication.cc |   3 +
 src/box/lua/session.cc    |  19 ++++++
 src/box/session.cc        |   8 +++
 src/box/session.h         |   5 ++
 src/lua/session.cc        |   9 +++
 src/session.cc            |   1 +
 test-run                  |   2 +-
 test/box/auth.result      | 133 ++++++++++++++++++++++++++++++++++++++
 test/box/auth.test.lua    |  54 ++++++++++++++++
 9 files changed, 233 insertions(+), 1 deletion(-)
 create mode 100644 test/box/auth.result
 create mode 100644 test/box/auth.test.lua

diff --git a/src/box/authentication.cc b/src/box/authentication.cc
index dfdf8aaf7b..5d5c9b1045 100644
--- a/src/box/authentication.cc
+++ b/src/box/authentication.cc
@@ -69,6 +69,9 @@ authenticate(const char *user_name, uint32_t len,
 	if (scramble_check(scramble, session->salt, user->hash2))
 		tnt_raise(ClientError, ER_PASSWORD_MISMATCH, user->name);
 
+	/* check and run auth triggers on success */
+	if (! rlist_empty(&session_on_auth))
+		session_run_on_auth_triggers(user->name);
 ok:
 	credentials_init(&session->credentials, user);
 }
diff --git a/src/box/lua/session.cc b/src/box/lua/session.cc
index 082c53a9a1..1021b22c61 100644
--- a/src/box/lua/session.cc
+++ b/src/box/lua/session.cc
@@ -211,6 +211,17 @@ lbox_session_run_trigger(struct trigger *trigger, void * /* event */)
 	lbox_call(L, 0, 0);
 }
 
+static void
+lbox_session_run_auth_trigger(struct trigger *trigger, void *event)
+{
+	lua_State *L = lua_newthread(tarantool_L);
+	LuarefGuard coro_guard(tarantool_L);
+	lua_rawgeti(L, LUA_REGISTRYINDEX, (intptr_t) trigger->data);
+	/* now only the user name comes in as an on_auth argument */
+	lua_pushstring(L, (const char *)event);
+	lbox_call(L, 1, 0);
+}
+
 static int
 lbox_session_on_connect(struct lua_State *L)
 {
@@ -225,6 +236,13 @@ lbox_session_on_disconnect(struct lua_State *L)
 				  lbox_session_run_trigger);
 }
 
+static int
+lbox_session_on_auth(struct lua_State *L)
+{
+	return lbox_trigger_reset(L, 2, &session_on_auth,
+				  lbox_session_run_auth_trigger);
+}
+
 void
 session_storage_cleanup(int sid)
 {
@@ -270,6 +288,7 @@ box_lua_session_init(struct lua_State *L)
 		{"peer", lbox_session_peer},
 		{"on_connect", lbox_session_on_connect},
 		{"on_disconnect", lbox_session_on_disconnect},
+		{"on_auth", lbox_session_on_auth},
 		{NULL, NULL}
 	};
 	luaL_register_module(L, sessionlib_name, sessionlib);
diff --git a/src/box/session.cc b/src/box/session.cc
index 607f0d7597..8f18cca194 100644
--- a/src/box/session.cc
+++ b/src/box/session.cc
@@ -42,6 +42,7 @@ struct mempool session_pool;
 
 RLIST_HEAD(session_on_connect);
 RLIST_HEAD(session_on_disconnect);
+RLIST_HEAD(session_on_auth);
 
 static inline  uint32_t
 sid_max()
@@ -137,6 +138,7 @@ session_run_on_disconnect_triggers(struct session *session)
 void
 session_run_on_connect_triggers(struct session *session)
 {
+	/* Run on_connect with admin credentals */
 	struct fiber *fiber = fiber();
 	fiber_set_session(fiber, session);
 	fiber_set_user(fiber, &admin_credentials);
@@ -144,6 +146,12 @@ session_run_on_connect_triggers(struct session *session)
 	/* Set session user to guest, until it is authenticated. */
 }
 
+void
+session_run_on_auth_triggers(const char *user_name)
+{
+	trigger_run(&session_on_auth, (void *)user_name);
+}
+
 void
 session_destroy(struct session *session)
 {
diff --git a/src/box/session.h b/src/box/session.h
index 09a9ea34a5..b78f566b7a 100644
--- a/src/box/session.h
+++ b/src/box/session.h
@@ -120,6 +120,11 @@ extern struct rlist session_on_disconnect;
 void
 session_run_on_disconnect_triggers(struct session *session);
 
+extern struct rlist session_on_auth;
+
+void
+session_run_on_auth_triggers(const char *user_name);
+
 void
 session_init();
 
diff --git a/src/lua/session.cc b/src/lua/session.cc
index 8d14d3c007..c544e8fb54 100644
--- a/src/lua/session.cc
+++ b/src/lua/session.cc
@@ -101,6 +101,8 @@ static struct lbox_session_trigger on_connect =
 	{ &session_on_connect, LUA_NOREF};
 static struct lbox_session_trigger on_disconnect =
 	{ &session_on_disconnect, LUA_NOREF};
+static struct lbox_session_trigger on_auth =
+	{ &session_on_auth, LUA_NOREF};
 
 static void
 lbox_session_run_trigger(void *param)
@@ -176,6 +178,12 @@ lbox_session_on_disconnect(struct lua_State *L)
 	return lbox_session_set_trigger(L, &on_disconnect);
 }
 
+static int
+lbox_session_on_auth(struct lua_State *L)
+{
+	return lbox_session_set_trigger(L, &on_auth);
+}
+
 void
 session_storage_cleanup(int sid)
 {
@@ -203,6 +211,7 @@ static const struct luaL_reg sessionlib[] = {
 	{"peer", lbox_session_peer},
 	{"on_connect", lbox_session_on_connect},
 	{"on_disconnect", lbox_session_on_disconnect},
+	{"on_auth", lbox_session_on_auth},
 	{NULL, NULL}
 };
 
diff --git a/src/session.cc b/src/session.cc
index 9abdc1acef..17a746c602 100644
--- a/src/session.cc
+++ b/src/session.cc
@@ -39,6 +39,7 @@ static struct mh_i32ptr_t *session_registry;
 
 struct session_trigger session_on_connect;
 struct session_trigger session_on_disconnect;
+struct session_trigger session_on_auth;
 
 uint32_t
 session_create(int fd, uint64_t cookie)
diff --git a/test-run b/test-run
index 7a9dffa16d..6b4dcd6712 160000
--- a/test-run
+++ b/test-run
@@ -1 +1 @@
-Subproject commit 7a9dffa16d0da05ec33fe0b131e92f6d86a87fd6
+Subproject commit 6b4dcd67126709a92e722a62c8a87a6ec90ceb0f
diff --git a/test/box/auth.result b/test/box/auth.result
new file mode 100644
index 0000000000..478ef191fd
--- /dev/null
+++ b/test/box/auth.result
@@ -0,0 +1,133 @@
+session = box.session
+---
+...
+fiber = require('fiber')
+---
+...
+space = box.schema.space.create('tweedledum')
+---
+...
+index = space:create_index('primary', { type = 'hash' })
+---
+...
+box.schema.user.create('test', {password='pass'})
+---
+...
+box.schema.user.grant('test', 'read,write,execute', 'universe')
+---
+...
+box.schema.user.grant('guest', 'read,write,execute', 'universe')
+---
+...
+-- check how authentication trigger work
+-- no args trigger
+function auth_trigger() counter = counter + 1 end
+---
+...
+-- get user name as argument
+function auth_trigger2(user_name) msg = 'user ' .. user_name .. ' is there' end
+---
+...
+net = { box = require('net.box') }
+---
+...
+-- set trigger
+handle = session.on_auth(auth_trigger)
+---
+...
+-- check handle
+type(handle)
+---
+- function
+...
+-- check triggers list
+#session.on_auth()
+---
+- 1
+...
+handle2 = session.on_auth(auth_trigger2)
+---
+...
+counter = 0
+---
+...
+msg = ''
+---
+...
+LISTEN = require('uri').parse(box.cfg.listen)
+---
+...
+-- check connection with authentication(counter must be incremented)
+c = net.box:new('test:pass@' .. LISTEN.host .. ':' .. LISTEN.service)
+---
+...
+while counter < 1 do fiber.sleep(0.001) end
+---
+...
+counter
+---
+- 1
+...
+msg
+---
+- user test is there
+...
+-- check guest connection without authentication(no increment)
+c1 = net.box:new(LISTEN.host, LISTEN.service)
+---
+...
+c1:ping()
+---
+- true
+...
+counter
+---
+- 1
+...
+-- cleanup
+c:close()
+---
+...
+c1:close()
+---
+...
+session.on_auth(nil, auth_trigger)
+---
+...
+session.on_auth(nil, auth_trigger2)
+---
+...
+session.on_auth()
+---
+- []
+...
+space:drop()
+---
+...
+session.uid()
+---
+- 1
+...
+session.user()
+---
+- admin
+...
+session.sync()
+---
+- 0
+...
+session = nil
+---
+...
+fiber = nil
+---
+...
+box.schema.user.revoke('guest', 'read,write,execute', 'universe')
+---
+...
+box.schema.user.revoke('test', 'read,write,execute', 'universe')
+---
+...
+box.schema.user.drop('test', { if_exists = true})
+---
+...
diff --git a/test/box/auth.test.lua b/test/box/auth.test.lua
new file mode 100644
index 0000000000..a8d65465df
--- /dev/null
+++ b/test/box/auth.test.lua
@@ -0,0 +1,54 @@
+session = box.session
+fiber = require('fiber')
+
+space = box.schema.space.create('tweedledum')
+index = space:create_index('primary', { type = 'hash' })
+box.schema.user.create('test', {password='pass'})
+box.schema.user.grant('test', 'read,write,execute', 'universe')
+box.schema.user.grant('guest', 'read,write,execute', 'universe')
+
+-- check how authentication trigger work
+-- no args trigger
+function auth_trigger() counter = counter + 1 end
+-- get user name as argument
+function auth_trigger2(user_name) msg = 'user ' .. user_name .. ' is there' end
+net = { box = require('net.box') }
+
+-- set trigger
+handle = session.on_auth(auth_trigger)
+-- check handle
+type(handle)
+-- check triggers list
+#session.on_auth()
+handle2 = session.on_auth(auth_trigger2)
+counter = 0
+msg = ''
+
+LISTEN = require('uri').parse(box.cfg.listen)
+
+-- check connection with authentication(counter must be incremented)
+c = net.box:new('test:pass@' .. LISTEN.host .. ':' .. LISTEN.service)
+while counter < 1 do fiber.sleep(0.001) end
+counter
+msg
+
+-- check guest connection without authentication(no increment)
+c1 = net.box:new(LISTEN.host, LISTEN.service)
+c1:ping()
+counter
+
+-- cleanup
+c:close()
+c1:close()
+session.on_auth(nil, auth_trigger)
+session.on_auth(nil, auth_trigger2)
+session.on_auth()
+space:drop()
+session.uid()
+session.user()
+session.sync()
+session = nil
+fiber = nil
+box.schema.user.revoke('guest', 'read,write,execute', 'universe')
+box.schema.user.revoke('test', 'read,write,execute', 'universe')
+box.schema.user.drop('test', { if_exists = true})
-- 
GitLab