<p>Andrey has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/6156">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_xmpp: Google OAuth 2.0 protocol support for XMPP / Motif<br><br>Add ability to use tokens instead of passwords according to Google OAuth 2.0<br>protocol.<br><br>ASTERISK-27169<br>Reported by: Andrey Egorov<br>Tested by: Andrey Egorov<br><br>Change-Id: I07f7052a502457ab55010a4d3686653b60f4c8db<br>---<br>M configs/samples/xmpp.conf.sample<br>M res/res_xmpp.c<br>2 files changed, 82 insertions(+), 13 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/56/6156/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/configs/samples/xmpp.conf.sample b/configs/samples/xmpp.conf.sample<br>index dad0f79..d87100d 100644<br>--- a/configs/samples/xmpp.conf.sample<br>+++ b/configs/samples/xmpp.conf.sample<br>@@ -18,6 +18,11 @@<br> ;pubsub_node=pubsub.astjab.org          ; Node to use for publishing events via PubSub<br> ;username=asterisk@astjab.org/asterisk ; Username with optional resource.<br> ;secret=blah                               ; Password<br>+;refresh_token=TOKEN_VALUE         ; Refresh token issued by Google OAuth 2.0 protocol.<br>+                                 ; `secret` must NOT be set if you use OAuth.<br>+;oauth_clientid=OAUTH_CLIENT_ID_VALUE    ; The application's client id to authorize using Google OAuth 2.0 protocol.<br>+;oauth_secret=OAUTH_SECRET_VALUE      ; The application's secret to authorize using Google OAuth 2.0 protocol.<br>+                                 ; Create new one on https://console.cloud.google.com/apis/credentials/oauthclient<br> ;priority=1                         ; Resource priority<br> ;port=5222                                ; Port to use defaults to 5222<br> ;usetls=yes                            ; Use tls or not<br>diff --git a/res/res_xmpp.c b/res/res_xmpp.c<br>index cc9d56f..4f23c72 100644<br>--- a/res/res_xmpp.c<br>+++ b/res/res_xmpp.c<br>@@ -59,6 +59,7 @@<br> #include "asterisk/manager.h"<br> #include "asterisk/cli.h"<br> #include "asterisk/config_options.h"<br>+#include "asterisk/json.h"<br> <br> /*** DOCUMENTATION<br>  <application name="JabberSend" language="en_US" module="res_xmpp"><br>@@ -321,6 +322,15 @@<br>                          <configOption name="secret"><br>                                  <synopsis>XMPP password</synopsis><br>                                </configOption><br>+                                <configOption name="refresh_token"><br>+                                  <synopsis>Google OAuth 2.0 refresh token</synopsis><br>+                              </configOption><br>+                                <configOption name="oauth_clientid"><br>+                                 <synopsis>Google OAuth 2.0 application's client id</synopsis><br>+                                </configOption><br>+                                <configOption name="oauth_secret"><br>+                                   <synopsis>Google OAuth 2.0 application's secret</synopsis><br>+                           </configOption><br>                                 <configOption name="serverhost"><br>                                      <synopsis>Route to server, e.g. talk.google.com</synopsis><br>                                </configOption><br>@@ -459,6 +469,9 @@<br>            AST_STRING_FIELD(name);        /*!< Name of the client connection */<br>               AST_STRING_FIELD(user);        /*!< Username to use for authentication */<br>          AST_STRING_FIELD(password);    /*!< Password to use for authentication */<br>+         AST_STRING_FIELD(refresh_token);   /*!< Refresh token to use for OAuth authentication */<br>+          AST_STRING_FIELD(oauth_clientid);  /*!< Client ID to use for OAuth authentication */<br>+              AST_STRING_FIELD(oauth_secret);    /*!< Secret to use for OAuth authentication */<br>          AST_STRING_FIELD(server);      /*!< Server hostname */<br>             AST_STRING_FIELD(statusmsg);   /*!< Status message for presence */<br>                 AST_STRING_FIELD(pubsubnode);  /*!< Pubsub node */<br>@@ -527,6 +540,7 @@<br> static ast_mutex_t messagelock;<br> <br> static int xmpp_client_config_post_apply(void *obj, void *arg, int flags);<br>+static void fetch_access_token(struct ast_xmpp_client_config *cfg);<br> <br> /*! \brief Destructor function for configuration */<br> static void ast_xmpp_client_config_destructor(void *obj)<br>@@ -759,11 +773,15 @@<br>         if (ast_strlen_zero(clientcfg->user)) {<br>            ast_log(LOG_ERROR, "No user specified on client '%s'\n", clientcfg->name);<br>               return -1;<br>-   } else if (ast_strlen_zero(clientcfg->password)) {<br>-                ast_log(LOG_ERROR, "No password specified on client '%s'\n", clientcfg->name);<br>+  } else if (ast_strlen_zero(clientcfg->password) == ast_strlen_zero(clientcfg->refresh_token)) {<br>+                ast_log(LOG_ERROR, "No password either refresh_token specified on client '%s'\n", clientcfg->name);<br>              return -1;<br>    } else if (ast_strlen_zero(clientcfg->server)) {<br>           ast_log(LOG_ERROR, "No server specified on client '%s'\n", clientcfg->name);<br>+            return -1;<br>+   } else if (!ast_strlen_zero(clientcfg->refresh_token) &&<br>+             (ast_strlen_zero(clientcfg->oauth_clientid) || ast_strlen_zero(clientcfg->oauth_secret))) {<br>+         ast_log(LOG_ERROR, "No oauth_clientid or oauth_secret specified, so client '%s' can't be used\n", clientcfg->name);<br>          return -1;<br>    }<br> <br>@@ -776,6 +794,9 @@<br>     /* If any configuration options are changing that would require reconnecting set the bit so we will do so if possible */<br>      if (strcmp(clientcfg->user, oldclientcfg->user) ||<br>          strcmp(clientcfg->password, oldclientcfg->password) ||<br>+         strcmp(clientcfg->refresh_token, oldclientcfg->refresh_token) ||<br>+       strcmp(clientcfg->oauth_clientid, oldclientcfg->oauth_clientid) ||<br>+     strcmp(clientcfg->oauth_secret, oldclientcfg->oauth_secret) ||<br>          strcmp(clientcfg->server, oldclientcfg->server) ||<br>      (clientcfg->port != oldclientcfg->port) ||<br>      (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT) != ast_test_flag(&oldclientcfg->flags, XMPP_COMPONENT)) ||<br>@@ -2784,7 +2805,13 @@<br>        }<br> <br>  iks_insert_attrib(auth, "xmlns", IKS_NS_XMPP_SASL);<br>-        iks_insert_attrib(auth, "mechanism", "PLAIN");<br>+   if (!ast_strlen_zero(cfg->refresh_token)) {<br>+               iks_insert_attrib(auth, "mechanism", "X-OAUTH2");<br>+                iks_insert_attrib(auth, "auth:service", "oauth2");<br>+               iks_insert_attrib(auth, "xmlns:auth", "http://www.google.com/talk/protocol/auth");<br>+       } else {<br>+             iks_insert_attrib(auth, "mechanism", "PLAIN");<br>+   }<br> <br>  if (strchr(client->jid->user, '/')) {<br>           char *user = ast_strdupa(client->jid->user);<br>@@ -3283,28 +3310,28 @@<br> {<br>       iks *iq, *ping;<br>       int res;<br>-     <br>+<br>   ast_debug(2, "JABBER: Sending Keep-Alive Ping for client '%s'\n", client->name);<br> <br>      if (!(iq = iks_new("iq")) || !(ping = iks_new("ping"))) {<br>                 iks_delete(iq);<br>               return -1;<br>    }<br>-    <br>+<br>   iks_insert_attrib(iq, "type", "get");<br>     iks_insert_attrib(iq, "to", to);<br>    iks_insert_attrib(iq, "from", from);<br>-       <br>+<br>   ast_xmpp_client_lock(client);<br>         iks_insert_attrib(iq, "id", client->mid);<br>        ast_xmpp_increment_mid(client->mid);<br>       ast_xmpp_client_unlock(client);<br>-      <br>+<br>   iks_insert_attrib(ping, "xmlns", "urn:xmpp:ping");<br>        iks_insert_node(iq, ping);<br>-   <br>+<br>   res = ast_xmpp_client_send(client, iq);<br>-      <br>+<br>   iks_delete(ping);<br>     iks_delete(iq);<br> <br>@@ -3625,6 +3652,11 @@<br>            return -1;<br>    }<br> <br>+ if (!ast_strlen_zero(clientcfg->refresh_token)) {<br>+         ast_log(LOG_NOTICE , "Connecting to client token: %s\n" , clientcfg->refresh_token);<br>+            fetch_access_token(clientcfg);<br>+       }<br>+<br>  ast_xmpp_client_disconnect(client);<br> <br>        client->timeout = 50;<br>@@ -3641,7 +3673,7 @@<br> <br>    /* Set socket timeout options */<br>      setsockopt(iks_fd(client->parser), SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval));<br>-      <br>+<br>   if (res == IKS_NET_NOCONN) {<br>          ast_log(LOG_ERROR, "No XMPP connection available when trying to connect client '%s'\n", client->name);<br>           return -1;<br>@@ -3726,7 +3758,7 @@<br>             /* Log the message here, because iksemel's logHook is<br>                unaccessible */<br>            xmpp_log_hook(client, buf, len, 1);<br>-          <br>+<br>           if(buf[0] == ' ') {<br>                   ast_debug(1, "JABBER: Detected Google Keep Alive. "<br>                                 "Sending out Ping request for client '%s'\n", client->name);<br>@@ -3865,6 +3897,35 @@<br> <br>  /* All buddies are unlinked from the configuration buddies container, always */<br>       return 1;<br>+}<br>+<br>+static void fetch_access_token(struct ast_xmpp_client_config *cfg) {<br>+      char cmd[4096];<br>+      char cBuf[4096];<br>+     const char *url = "https://www.googleapis.com/oauth2/v3/token";<br>+<br>+ memset(cmd, 0, sizeof(cmd));<br>+ snprintf(cmd, sizeof(cmd) - 1 , "CURL(%s,client_id=%s&client_secret=%s&refresh_token=%s&grant_type=refresh_token)",<br>+             url, cfg->oauth_clientid, cfg->oauth_secret, cfg->refresh_token);<br>+<br>+       ast_log(LOG_NOTICE, "Command %s\n" , cmd);<br>+<br>+      memset(cBuf, 0, sizeof(cBuf));<br>+       ast_func_read(NULL, cmd, cBuf, sizeof(cBuf) - 1);<br>+    ast_log(LOG_NOTICE, "Command status: %s\n", cBuf);<br>+<br>+      struct ast_json_error error;<br>+ struct ast_json *jobj = ast_json_load_string(cBuf, &error);<br>+      if (jobj != NULL) {<br>+          const char *token = ast_json_string_get(ast_json_object_get(jobj, "access_token"));<br>+                if (token != NULL) {<br>+                 ast_string_field_set(cfg, password, token);<br>+          }<br>+            ast_log(LOG_NOTICE, "Access Token: %s\n", token);<br>+  }<br>+    else {<br>+               ast_log(LOG_ERROR, "OAuth object is NULL\n");<br>+      }<br> }<br> <br> static int xmpp_client_config_post_apply(void *obj, void *arg, int flags)<br>@@ -4620,8 +4681,8 @@<br>  * Module loading including tests for configuration or dependencies.<br>  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,<br>  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails<br>- * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the <br>- * configuration file or other non-critical problem return <br>+ * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the<br>+ * configuration file or other non-critical problem return<br>  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.<br>  */<br> static int load_module(void)<br>@@ -4639,6 +4700,9 @@<br> <br>     aco_option_register(&cfg_info, "username", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, user));<br>      aco_option_register(&cfg_info, "secret", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, password));<br>+   aco_option_register(&cfg_info, "refresh_token", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, refresh_token));<br>+       aco_option_register(&cfg_info, "oauth_clientid", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, oauth_clientid));<br>+     aco_option_register(&cfg_info, "oauth_secret", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, oauth_secret));<br>  aco_option_register(&cfg_info, "serverhost", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, server));<br>  aco_option_register(&cfg_info, "statusmessage", ACO_EXACT, client_options, "Online and Available", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, statusmsg));<br>        aco_option_register(&cfg_info, "pubsub_node", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, pubsubnode));<br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/6156">change 6156</a>. To unsubscribe, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/6156"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 15 </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: I07f7052a502457ab55010a4d3686653b60f4c8db </div>
<div style="display:none"> Gerrit-Change-Number: 6156 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Andrey <andr06@gmail.com> </div>