[Libreoffice-commits] core.git: external/libcmis ucb/source uui/source uui/uiconfig

Szymon Kłos eszkadev at gmail.com
Mon May 16 07:24:43 UTC 2016


 external/libcmis/UnpackedTarball_cmis.mk                 |    1 
 external/libcmis/libcmis-google-2FA-implementation.patch |  156 +++++++++++++++
 ucb/source/ucp/cmis/auth_provider.cxx                    |   42 ++++
 ucb/source/ucp/cmis/auth_provider.hxx                    |    4 
 ucb/source/ucp/cmis/cmis_content.cxx                     |    3 
 ucb/source/ucp/cmis/cmis_repo_content.cxx                |    3 
 uui/source/authfallbackdlg.cxx                           |    5 
 uui/uiconfig/ui/authfallback.ui                          |    2 
 8 files changed, 214 insertions(+), 2 deletions(-)

New commits:
commit 511db54e95d02292417d7cd076e4bd6d50956d64
Author: Szymon Kłos <eszkadev at gmail.com>
Date:   Sun May 15 00:52:50 2016 +0200

    tdf#87938 libcmis: Google 2FA implementation
    
    2 Factor Authentication for Google Drive, changes:
    + parsing additional page with challenge - pin code verification
    + calling the interaction dialog requesting the code
    + dialog title changed to more general: Authentication Code
    + dialog shows url field only if not empty
    
    Change-Id: Idb3ebbad6a12849b9e50af87b46324bfbe966bab
    Reviewed-on: https://gerrit.libreoffice.org/25002
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: David Tardon <dtardon at redhat.com>

diff --git a/external/libcmis/UnpackedTarball_cmis.mk b/external/libcmis/UnpackedTarball_cmis.mk
index 435e7c5..bd66e2e 100644
--- a/external/libcmis/UnpackedTarball_cmis.mk
+++ b/external/libcmis/UnpackedTarball_cmis.mk
@@ -16,6 +16,7 @@ $(eval $(call gb_UnpackedTarball_set_patchlevel,cmis,1))
 $(eval $(call gb_UnpackedTarball_add_patches,cmis, \
 						external/libcmis/libcmis-libxml2_compatibility.patch \
 						external/libcmis/libcmis-fix-google-drive.patch \
+						external/libcmis/libcmis-google-2FA-implementation.patch \
 ))
 
 ifeq ($(OS)$(COM),WNTMSC)
diff --git a/external/libcmis/libcmis-google-2FA-implementation.patch b/external/libcmis/libcmis-google-2FA-implementation.patch
new file mode 100755
index 0000000..23f9775
--- /dev/null
+++ b/external/libcmis/libcmis-google-2FA-implementation.patch
@@ -0,0 +1,156 @@
+diff --git a/src/libcmis/oauth2-providers.cxx b/src/libcmis/oauth2-providers.cxx
+--- a/src/libcmis/oauth2-providers.cxx
++++ b/src/libcmis/oauth2-providers.cxx
+@@ -29,6 +29,7 @@
+ #include <libxml/HTMLparser.h>
+ #include <libxml/xmlreader.h>
+ 
++#include "session-factory.hxx"
+ #include "oauth2-providers.hxx"
+ #include "http-session.hxx"
+ 
+@@ -51,10 +52,23 @@ string OAuth2Providers::OAuth2Gdrive( HttpSession* session, const string& authUr
+      * 4) subsequent post to send a consent for the application
+      *    receive a single-use authorization code
+      *    this code is returned as a string
++     *
++     * Sequence with 2FA is:
++     * 1) a get to activate login page
++     *    receive first login page, html format
++     * 2) subsequent post to sent email
++     *    receive html page for password input
++     * 3) subsequent post to send password
++     *    receive html page for pin input
++     * 3b) subsequent post to send pin number
++     *    receive html page for application consent
++     * 4) subsequent post to send a consent for the application
++     *    receive a single-use authorization code
++     *    this code is returned as a string
+      */
+ 
+     static const string CONTENT_TYPE( "application/x-www-form-urlencoded" );
+-    // STEP 1: Log in
++    // STEP 1: get login page
+     string res;
+     try
+     {
+@@ -66,6 +80,8 @@ string OAuth2Providers::OAuth2Gdrive( HttpSession* session, const string& authUr
+         return string( );
+     }
+ 
++    // STEP 2: send email
++
+     string loginEmailPost, loginEmailLink;
+     if ( !parseResponse( res.c_str( ), loginEmailPost, loginEmailLink ) )
+         return string( );
+@@ -86,6 +102,8 @@ string OAuth2Providers::OAuth2Gdrive( HttpSession* session, const string& authUr
+         return string( );
+     }
+ 
++    // STEP 3: password page
++
+     string loginPasswdPost, loginPasswdLink;
+     if ( !parseResponse( loginEmailRes.c_str( ), loginPasswdPost, loginPasswdLink ) )
+         return string( );
+@@ -106,10 +124,53 @@ string OAuth2Providers::OAuth2Gdrive( HttpSession* session, const string& authUr
+         return string( );
+     }
+ 
+-    // STEP 2: allow libcmis to access google drive
+     string approvalPost, approvalLink;
+     if ( !parseResponse( loginPasswdRes. c_str( ), approvalPost, approvalLink) )
+         return string( );
++
++    // when 2FA is enabled, link doesn't start with 'http'
++    if ( approvalLink.compare(0, 4, "http") != 0 )
++    {
++        // STEP 3b: 2 Factor Authentication, pin code request
++
++        string loginChallengePost( approvalPost );
++        string loginChallengeLink( approvalLink );
++
++        libcmis::OAuth2AuthCodeProvider fallbackProvider = libcmis::SessionFactory::getOAuth2AuthCodeProvider( );
++        string pin( fallbackProvider( "", "", "" ) );
++
++        loginChallengeLink = "https://accounts.google.com" + loginChallengeLink;
++        loginChallengePost += "Pin=";
++        loginChallengePost += string( pin );
++
++        istringstream loginChallengeIs( loginChallengePost );
++        string loginChallengeRes;
++        try
++        {
++            // send a post with pin, receive the application consent page
++            loginChallengeRes = session->httpPostRequest ( loginChallengeLink, loginChallengeIs, CONTENT_TYPE )
++                            ->getStream( )->str( );
++        }
++        catch ( const CurlException& e )
++        {
++            return string( );
++        }
++
++        approvalPost = string();
++        approvalLink = string();
++
++        if ( !parseResponse( loginChallengeRes. c_str( ), approvalPost, approvalLink) )
++            return string( );
++    }
++    else if( approvalLink.compare( "https://accounts.google.com/ServiceLoginAuth" ) == 0 )
++    {
++        // wrong password,
++        // unset OAuth2AuthCode Provider to avoid showing pin request again in the HttpSession::oauth2Authenticate
++        libcmis::SessionFactory::setOAuth2AuthCodeProvider( NULL );
++        return string( );
++    }
++
++    // STEP 4: allow libcmis to access google drive
+     approvalPost += "submit_access=true";
+ 
+     istringstream approvalIs( approvalPost );
+@@ -125,7 +186,7 @@ string OAuth2Providers::OAuth2Gdrive( HttpSession* session, const string& authUr
+         throw e.getCmisException( );
+     }
+ 
+-    // STEP 3: Take the authentication code from the text bar
++    // Take the authentication code from the text bar
+     string code = parseCode( approvalRes.c_str( ) );
+ 
+     return code;
+@@ -216,6 +277,9 @@ int OAuth2Providers::parseResponse ( const char* response, string& post, string&
+     if ( doc == NULL ) return 0;
+     xmlTextReaderPtr reader =   xmlReaderWalker( doc );
+     if ( reader == NULL ) return 0;
++
++    bool readInputField = false;
++
+     while ( true )
+     {
+         // Go to the next node, quit if not found
+@@ -227,15 +291,24 @@ int OAuth2Providers::parseResponse ( const char* response, string& post, string&
+         {
+             xmlChar* action = xmlTextReaderGetAttribute( reader, 
+                                                          BAD_CAST( "action" ));
++
++            // GDrive pin code page contains 3 forms.
++            // First form resends pin code, we need to omit its input fields.
++            // Also we mustn't parse action field from the 'select challenge' form.
+             if ( action != NULL )
+             {
+-                if ( xmlStrlen(action) > 0)
++                if ( ( strcmp( (char*)action, "/signin/challenge" ) != 0 )
++                   && ( strcmp( (char*)action, "/signin/selectchallenge" ) != 0 )
++                   && xmlStrlen(action) > 0 )
++                {
+                     link = string ( (char*) action);
++                    readInputField = true;
++                }
+                 xmlFree (action);
+             }
+         }
+         // Find input values
+-        if ( !xmlStrcmp( nodeName, BAD_CAST( "input" ) ) )
++        if ( readInputField && !xmlStrcmp( nodeName, BAD_CAST( "input" ) ) )
+         {
+             xmlChar* name = xmlTextReaderGetAttribute( reader, 
+                                                        BAD_CAST( "name" ));
+
diff --git a/ucb/source/ucp/cmis/auth_provider.cxx b/ucb/source/ucp/cmis/auth_provider.cxx
index 64cb84e..0c3e4c0 100644
--- a/ucb/source/ucp/cmis/auth_provider.cxx
+++ b/ucb/source/ucp/cmis/auth_provider.cxx
@@ -110,6 +110,48 @@ namespace cmis
 
         return strdup( "" );
     }
+
+    char* AuthProvider::gdriveAuthCodeFallback( const char* /*url*/,
+            const char* /*username*/,
+            const char* /*password*/ )
+    {
+        OUString instructions = "PIN:";
+        OUString url_oustr( "" );
+        const css::uno::Reference<
+            css::ucb::XCommandEnvironment> xEnv = getXEnv( );
+
+        if ( xEnv.is() )
+        {
+            uno::Reference< task::XInteractionHandler > xIH
+                = xEnv->getInteractionHandler();
+
+            if ( xIH.is() )
+            {
+                rtl::Reference< ucbhelper::AuthenticationFallbackRequest > xRequest
+                    = new ucbhelper::AuthenticationFallbackRequest (
+                            instructions, url_oustr );
+
+                xIH->handle( xRequest.get() );
+
+                rtl::Reference< ucbhelper::InteractionContinuation > xSelection
+                    = xRequest->getSelection();
+
+                if ( xSelection.is() )
+                {
+                    // Handler handled the request.
+                    const rtl::Reference< ucbhelper::InteractionAuthFallback >&
+                        xAuthFallback = xRequest->getAuthFallbackInter( );
+                    if ( xAuthFallback.is() )
+                    {
+                        OUString code = xAuthFallback->getCode( );
+                        return strdup( OUSTR_TO_STDSTR( code ).c_str( ) );
+                    }
+                }
+            }
+        }
+
+        return strdup( "" );
+    }
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/ucb/source/ucp/cmis/auth_provider.hxx b/ucb/source/ucp/cmis/auth_provider.hxx
index 977254a..e633e95 100644
--- a/ucb/source/ucp/cmis/auth_provider.hxx
+++ b/ucb/source/ucp/cmis/auth_provider.hxx
@@ -34,6 +34,10 @@ namespace cmis
                     const char* /*username*/,
                     const char* /*password*/ );
 
+            static char* gdriveAuthCodeFallback( const char* /*url*/,
+                    const char* /*username*/,
+                    const char* /*password*/ );
+
             static void setXEnv( const css::uno::Reference< css::ucb::XCommandEnvironment>& xEnv ) { sm_xEnv = xEnv; }
 
             static const css::uno::Reference< css::ucb::XCommandEnvironment>& getXEnv( ) { return sm_xEnv; }
diff --git a/ucb/source/ucp/cmis/cmis_content.cxx b/ucb/source/ucp/cmis/cmis_content.cxx
index dea8ec7..b097a6f 100644
--- a/ucb/source/ucp/cmis/cmis_content.cxx
+++ b/ucb/source/ucp/cmis/cmis_content.cxx
@@ -376,10 +376,13 @@ namespace cmis
                     // Initiate a CMIS session and register it as we found nothing
                     libcmis::OAuth2DataPtr oauth2Data;
                     if ( m_aURL.getBindingUrl( ) == GDRIVE_BASE_URL )
+                    {
+                        libcmis::SessionFactory::setOAuth2AuthCodeProvider( authProvider.gdriveAuthCodeFallback );
                         oauth2Data.reset( new libcmis::OAuth2Data(
                             GDRIVE_AUTH_URL, GDRIVE_TOKEN_URL,
                             GDRIVE_SCOPE, GDRIVE_REDIRECT_URI,
                             GDRIVE_CLIENT_ID, GDRIVE_CLIENT_SECRET ) );
+                    }
                     if ( m_aURL.getBindingUrl().startsWith( ALFRESCO_CLOUD_BASE_URL ) )
                         oauth2Data.reset( new libcmis::OAuth2Data(
                             ALFRESCO_CLOUD_AUTH_URL, ALFRESCO_CLOUD_TOKEN_URL,
diff --git a/ucb/source/ucp/cmis/cmis_repo_content.cxx b/ucb/source/ucp/cmis/cmis_repo_content.cxx
index 75b3790..cc1df94 100644
--- a/ucb/source/ucp/cmis/cmis_repo_content.cxx
+++ b/ucb/source/ucp/cmis/cmis_repo_content.cxx
@@ -175,10 +175,13 @@ namespace cmis
                         // Create a session to get repositories
                         libcmis::OAuth2DataPtr oauth2Data;
                         if ( m_aURL.getBindingUrl( ) == GDRIVE_BASE_URL )
+                        {
+                            libcmis::SessionFactory::setOAuth2AuthCodeProvider( authProvider.gdriveAuthCodeFallback );
                             oauth2Data.reset( new libcmis::OAuth2Data(
                                 GDRIVE_AUTH_URL, GDRIVE_TOKEN_URL,
                                 GDRIVE_SCOPE, GDRIVE_REDIRECT_URI,
                                 GDRIVE_CLIENT_ID, GDRIVE_CLIENT_SECRET ) );
+                        }
                         if ( m_aURL.getBindingUrl().startsWith( ALFRESCO_CLOUD_BASE_URL ) )
                             oauth2Data.reset( new libcmis::OAuth2Data(
                                 ALFRESCO_CLOUD_AUTH_URL, ALFRESCO_CLOUD_TOKEN_URL,
diff --git a/uui/source/authfallbackdlg.cxx b/uui/source/authfallbackdlg.cxx
index 70ed1e7..a6383d1 100644
--- a/uui/source/authfallbackdlg.cxx
+++ b/uui/source/authfallbackdlg.cxx
@@ -29,7 +29,10 @@ AuthFallbackDlg::AuthFallbackDlg(Window* pParent, const OUString& instructions,
     m_pBTOk->Enable();
 
     m_pTVInstructions->SetText( instructions );
-    m_pEDUrl->SetText( url );
+    if( url.isEmpty() )
+        m_pEDUrl->Hide();
+    else
+        m_pEDUrl->SetText( url );
 }
 
 AuthFallbackDlg::~AuthFallbackDlg()
diff --git a/uui/uiconfig/ui/authfallback.ui b/uui/uiconfig/ui/authfallback.ui
index a947ad4..6c99edf 100644
--- a/uui/uiconfig/ui/authfallback.ui
+++ b/uui/uiconfig/ui/authfallback.ui
@@ -5,7 +5,7 @@
   <object class="GtkDialog" id="AuthFallbackDlg">
     <property name="can_focus">False</property>
     <property name="border_width">6</property>
-    <property name="title" translatable="yes">OneDrive Authentication Code</property>
+    <property name="title" translatable="yes">Authentication Code</property>
     <property name="default_width">400</property>
     <property name="default_height">270</property>
     <property name="type_hint">dialog</property>


More information about the Libreoffice-commits mailing list