From 861f2b860a9ccf55d2b7bc46f7761c7a11f92d93 Mon Sep 17 00:00:00 2001 From: Paul Libbrecht Date: Tue, 3 Dec 2019 18:58:17 +0100 Subject: [PATCH 01/17] Style comments on the PR. Paul --- .../CookieAuthenticationPersistence.java | 3 +++ .../apps/googleapps/GoogleAppsManager.java | 16 +++++++-------- .../googleapps/GoogleAppsScriptService.java | 15 ++++++++++---- .../CookieAuthenticationPersistenceImpl.java | 4 ++++ .../internal/GoogleAppsAuthServiceImpl.java | 4 +++- .../internal/GoogleAppsManagerImpl.java | 20 ++++++++++++++----- 6 files changed, 43 insertions(+), 19 deletions(-) diff --git a/api/src/main/java/org/xwiki/apps/googleapps/CookieAuthenticationPersistence.java b/api/src/main/java/org/xwiki/apps/googleapps/CookieAuthenticationPersistence.java index 754941b..93d46ea 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/CookieAuthenticationPersistence.java +++ b/api/src/main/java/org/xwiki/apps/googleapps/CookieAuthenticationPersistence.java @@ -35,6 +35,7 @@ public interface CookieAuthenticationPersistence { /** * Stores the user-id in an encryted fashion in the cookie. + * * @param userId the string to store * @since 3.0 */ @@ -42,6 +43,7 @@ public interface CookieAuthenticationPersistence /** * Reads the user-id from the cookie. + * * @return the decrypted user-id * @since 3.0 */ @@ -49,6 +51,7 @@ public interface CookieAuthenticationPersistence /** * Removes stored information from the cookie. + * * @since 3.0 */ void clear(); diff --git a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsManager.java b/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsManager.java index ed85369..16f9770 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsManager.java +++ b/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsManager.java @@ -45,8 +45,6 @@ @Role public interface GoogleAppsManager { - - /** * @return if the application is licensed and activated * @throws XWikiException in case a context cannot be read from thread. @@ -56,14 +54,12 @@ public interface GoogleAppsManager boolean isActive() throws XWikiException; /** - * * @return if the app is configured to use the Google Drive integration (default: yes). * @since 3.0 */ @Unstable boolean useDrive(); - /** * Reads the manifest to find when the JAR file was assembled by maven. * @return the build date. @@ -72,8 +68,8 @@ public interface GoogleAppsManager @Unstable Date getBuildTime(); - - /** Inspects the stored information to see if an authorization or a redirect needs to be pronounced. + /** + * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. * * @return found credential * @throws XWikiException if the interaction with xwiki failed @@ -83,8 +79,8 @@ public interface GoogleAppsManager @Unstable Credential authorize() throws XWikiException, IOException; - - /** Inspects the stored information to see if an authorization or a redirect needs to be pronounced. + /** + * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. * * @param redirect If a redirect can be done * @return found credential @@ -143,6 +139,7 @@ public interface GoogleAppsManager /** * Fetches the google-drive document's representation and stores it as attachment. + * * @param page attach to this page * @param name attach using this file name * @param id store object attached to this attachment using this id (for later sync) @@ -169,6 +166,7 @@ public interface GoogleAppsManager /** * Simple pojo for metadata about a google doc. + * * @since 3.0 */ @Unstable @@ -200,7 +198,6 @@ class GoogleDocMetadata @Unstable String[] getExportLink(String docName, String elink); - /** * Inserts the current information on the document to be embedded. * @@ -217,6 +214,7 @@ class GoogleDocMetadata /** * Saves the attachment stored in XWiki to the Google drive of the user attached to the current logged-in user. + * * @param page the XWiki page name * @param name the attachment name * @return a record with the keys fileName, exportLink, version, editLink, embedLink, diff --git a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsScriptService.java b/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsScriptService.java index fc74bae..fb29038 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsScriptService.java +++ b/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsScriptService.java @@ -81,6 +81,7 @@ public boolean useDrive() { /** * Reads the manifest to find when the JAR file was assembled by maven. + * * @return the build date. * @since 3.0 */ @@ -89,7 +90,8 @@ public Date getBuildTime() { return manager.getBuildTime(); } - /** Inspects the stored information to see if an authorization or a redirect needs to be pronounced. + /** + * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. * * @return found credential * @throws XWikiException if the interaction with xwiki failed @@ -102,7 +104,8 @@ public Credential authorize() throws XWikiException, IOException { } - /** Inspects the stored information to see if an authorization or a redirect needs to be pronounced. + /** + * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. * * @param redirect If a redirect can be done * @return found credential @@ -141,7 +144,8 @@ public List getDocumentList() throws XWikiException, IOException { return manager.getDocumentList(); } - /** Fetches a list of Google Drive document matching a substring query in the filename. + /** + * Fetches a list of Google Drive document matching a substring query in the filename. * * @param query the expected query (e.g. fullText contains winter ski) * @param nbResults max number of results @@ -155,7 +159,8 @@ public List listDriveDocumentsWithTypes(String query, int nbResults) throw return manager.listDriveDocumentsWithTypes(query, nbResults); } - /** Fetches a list of Google Drive document matching a given query. + /** + * Fetches a list of Google Drive document matching a given query. * * @param query the expected filename substring * @param nbResults max number of results @@ -190,6 +195,7 @@ public Object createOrUpdateEmbedObject(String docId, Document doc, Object obj, /** * Fetches the google-drive document's representation and stores it as attachment. + * * @param page attach to this page * @param name attach using this file name * @param id store object attached to this attachment using this id (for later sync) @@ -223,6 +229,7 @@ public GoogleAppsManager.GoogleDocMetadata getGoogleDocument(String pageName, St /** * Reads the extension and document name. + * * @param docName the raw docName * @param elink the link where to read the extension name * @return an array with extension and simplified document name diff --git a/api/src/main/java/org/xwiki/apps/googleapps/internal/CookieAuthenticationPersistenceImpl.java b/api/src/main/java/org/xwiki/apps/googleapps/internal/CookieAuthenticationPersistenceImpl.java index 56b3603..0b12582 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/internal/CookieAuthenticationPersistenceImpl.java +++ b/api/src/main/java/org/xwiki/apps/googleapps/internal/CookieAuthenticationPersistenceImpl.java @@ -95,6 +95,7 @@ public class CookieAuthenticationPersistenceImpl implements CookieAuthentication /** * Initialize the tool. + * * @param context XWiki Context * @param cookieMaxAge Time To Live of the created cookies in scd * @throws XWikiException in case of trouble @@ -129,6 +130,7 @@ public void initialize(XWikiContext context, long cookieMaxAge) throws XWikiExce /** * Erases the information stored. + * * @since 3.0 */ @Unstable @@ -140,6 +142,7 @@ public void clear() /** * Store the user-information within the cookie. + * * @param userUid the user-name (without xwiki. prefix) * @since 3.0 */ @@ -221,6 +224,7 @@ private String decryptText(String text) /** * Retrieve given cookie null-safe. + * * @param cookieName name of the cookie * @return the cookie * @since 3.0 diff --git a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsAuthServiceImpl.java b/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsAuthServiceImpl.java index 5de3410..8dac6f7 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsAuthServiceImpl.java +++ b/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsAuthServiceImpl.java @@ -52,6 +52,7 @@ /** * An authenticator that can include a negotiation with the Google Cloud (e.g. Google Drive) services. * This authenticator is created, configured and maintained by the GoogleAppsScriptService. + * * @since 3.0 * @version $Id$ */ @@ -101,7 +102,7 @@ public XWikiUser checkAuth(XWikiContext context) throws XWikiException { /** * Checks authentication. - * i + * * @param username the name of the user to verify against * @param password the password of the user to verify against * @param rememberme insert-cookies to remember the login @@ -176,6 +177,7 @@ public void showLogin(XWikiContext context) throws XWikiException { /** * Processes a password entry and creates the appropriate principal. + * * @param username the provided user-name * @param password the provided password * @param context the context describing the request diff --git a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsManagerImpl.java b/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsManagerImpl.java index 83b4afc..8bf7c3a 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsManagerImpl.java +++ b/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsManagerImpl.java @@ -102,6 +102,7 @@ /** * Set of methods accessible to the scripts using the GoogleApps functions. + * * @version $Id$ * @since 3.0 */ @@ -293,7 +294,8 @@ private BaseObject getConfigDoc(XWikiContext context) throws XWikiException // ----------------------------- APIs ------------------------------------------------- - /** Evaluates weather the application is active and licensed by looking at the stored documents. + /** + * Evaluates weather the application is active and licensed by looking at the stored documents. * Within a request, this method should always be the first to be called so that the config-object * is read and other properties are cached if need be. The context is extracted from the thead-local. * @@ -305,7 +307,8 @@ public boolean isActive() { return isActive(xwikiContextProvider.get()); } - /** Evaluates weather the application is active and licensed by looking at the stored documents. + /** + * Evaluates weather the application is active and licensed by looking at the stored documents. * Within a request, this method should always be the first to be called so that the config-object * is read and other properties are cached if need be. * @@ -401,6 +404,7 @@ private void readConfigDoc(XWikiContext context) { /** * Reads the manifest to find when the JAR file was assembled by maven. + * * @return the build date. * @since 3.0 */ @@ -459,7 +463,6 @@ private XWiki getXWiki() // ------------------------- public API --------------------------------------------- /** - * * @return if the app is configured to use the Google Drive integration (default: yes). * @since 3.0 */ @@ -530,6 +533,7 @@ private DocumentReference getConfigDocRef() { /** * Build flow and trigger user authorization request. + * * @return the configured flow * @throws IOException in case something can't be built */ @@ -1026,6 +1030,7 @@ private Drive getDriveService() throws XWikiException, IOException { /** * Build and return an authorized Drive client service. + * * @return an authorized Drive client service * @throws IOException if a communication error occurred */ @@ -1051,7 +1056,8 @@ public List getDocumentList() throws XWikiException, IOException { return result.getItems(); } - /** Fetches a list of Google Drive document matching a substring query in the filename. + /** + * Fetches a list of Google Drive document matching a substring query in the filename. * * @param query the expected query (e.g. fullText contains winter ski) * @param nbResults max number of results @@ -1071,7 +1077,8 @@ public List listDriveDocumentsWithTypes(String query, int nbResults) throw return result.getItems(); } - /** Fetches a list of Google Drive document matching a given query. + /** + * Fetches a list of Google Drive document matching a given query. * * @param query the expected filename substring * @param nbResults max number of results @@ -1090,6 +1097,7 @@ public FileList listDocuments(String query, int nbResults) throws XWikiException /** * Fetches the google-drive document's representation and stores it as attachment. + * * @param page attach to this page * @param name attach using this file name * @param id store object attached to this attachment using this id (for later sync) @@ -1256,6 +1264,7 @@ public BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, Bas /** * Reads the extension and document name. + * * @param docName the raw docName * @param elink the link where to read the extension name * @return an array with extension and simplified document name @@ -1296,6 +1305,7 @@ private String findExportLink(String name, File entry) { /** * Saves the attachment stored in XWiki to the Google drive of the user attached to the current logged-in user. + * * @param page the XWiki page name * @param name the attachment name * @return a record with the keys fileName, exportLink, version, editLink, embedLink, From 80ac30e9ec4ba726b74a4a4248aac1cc4b9945f3 Mon Sep 17 00:00:00 2001 From: Paul Libbrecht Date: Tue, 3 Dec 2019 21:47:55 +0100 Subject: [PATCH 02/17] Forgotten to address this request. Thanks for the reminder. scm and issuemanagement should only be on the root pom. Thanks @acotiuga. Paul --- api/pom.xml | 10 ---------- ui/pom.xml | 10 ---------- 2 files changed, 20 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index d4eb0b9..7e57c1a 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -58,16 +58,6 @@ v2-rev358-1.24.1 - - GitHub - https://github.com/xwikisas/application-googleapps/issues - - - scm:git:git://github.com/xwikisas/application-googleapps.git - scm:git:git@github.com:xwikisas/application-googleapps.git - https://github.com/xwikisas/application-googleapps - HEAD - diff --git a/ui/pom.xml b/ui/pom.xml index bd595fd..8c952fe 100644 --- a/ui/pom.xml +++ b/ui/pom.xml @@ -59,16 +59,6 @@ ${licensing.version} - - GitHub - https://github.com/xwikisas/application-googleapps/issues - - - scm:git:git://github.com/xwikisas/application-googleapps.git - scm:git:git@github.com:xwikisas/application-googleapps.git - https://github.com/xwikisas/application-googleapps - HEAD - From 38eeefa6da4b4328ecd08ab9ddf28462d5296bd4 Mon Sep 17 00:00:00 2001 From: Paul Libbrecht Date: Fri, 13 Dec 2019 09:17:22 +0100 Subject: [PATCH 03/17] Readability comments from @acotiuga and @mflorea. Thanks. --- api/pom.xml | 6 - .../CookieAuthenticationPersistence.java | 20 +- .../apps/googleapps/DriveDocMetadata.java | 46 + .../googleapps/GoogleAppsAuthService.java | 6 +- .../apps/googleapps/GoogleAppsManager.java | 102 +-- .../googleapps/GoogleAppsScriptService.java | 111 +-- .../CookieAuthenticationPersistenceImpl.java | 88 +- .../internal/GoogleAppsAuthServiceImpl.java | 70 +- .../internal/GoogleAppsEventListener.java | 82 ++ .../internal/GoogleAppsManagerImpl.java | 813 +++++++++--------- .../main/resources/GoogleApps/DriveMacro.xml | 2 +- .../resources/GoogleApps/EditInGoogleApps.xml | 2 +- .../main/resources/GoogleApps/JSExtension.xml | 2 +- 13 files changed, 735 insertions(+), 615 deletions(-) create mode 100644 api/src/main/java/org/xwiki/apps/googleapps/DriveDocMetadata.java create mode 100644 api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsEventListener.java diff --git a/api/pom.xml b/api/pom.xml index 7e57c1a..3bb2e00 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -66,12 +66,6 @@ false - - - checkstyle-validation - none - - diff --git a/api/src/main/java/org/xwiki/apps/googleapps/CookieAuthenticationPersistence.java b/api/src/main/java/org/xwiki/apps/googleapps/CookieAuthenticationPersistence.java index 93d46ea..4a951f0 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/CookieAuthenticationPersistence.java +++ b/api/src/main/java/org/xwiki/apps/googleapps/CookieAuthenticationPersistence.java @@ -21,14 +21,11 @@ import org.xwiki.component.annotation.Role; -import com.xpn.xwiki.XWikiContext; -import com.xpn.xwiki.XWikiException; - /** * Set of methods for the management of the cookies. * - * @since 3.0 * @version $Id$ + * @since 3.0 */ @Role public interface CookieAuthenticationPersistence @@ -39,7 +36,7 @@ public interface CookieAuthenticationPersistence * @param userId the string to store * @since 3.0 */ - void store(String userId); + void setUserId(String userId); /** * Reads the user-id from the cookie. @@ -47,7 +44,7 @@ public interface CookieAuthenticationPersistence * @return the decrypted user-id * @since 3.0 */ - String retrieve(); + String getUserId(); /** * Removes stored information from the cookie. @@ -55,15 +52,4 @@ public interface CookieAuthenticationPersistence * @since 3.0 */ void clear(); - - /** - * Initialize with the local parameters. - * - * @param context Context of the request (e.g. for cookies) - * @param cookieMaxAge Configure maximum age of the cookie. - * @throws XWikiException if anything goes wrong - * @since 3.0 - */ - void initialize(XWikiContext context, long cookieMaxAge) throws XWikiException; - } diff --git a/api/src/main/java/org/xwiki/apps/googleapps/DriveDocMetadata.java b/api/src/main/java/org/xwiki/apps/googleapps/DriveDocMetadata.java new file mode 100644 index 0000000..1f30143 --- /dev/null +++ b/api/src/main/java/org/xwiki/apps/googleapps/DriveDocMetadata.java @@ -0,0 +1,46 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.xwiki.apps.googleapps; + +import org.xwiki.stability.Unstable; + +/** + * Simple pojo for metadata about a doc in Google Drive. + * + * @since 3.0 + */ +@Unstable +public class DriveDocMetadata +{ + /** + * Google's internal id to find the document again. + */ + public String id; + + /** + * URL to direct the user to for editing. + */ + public String editLink; + + /** + * URL to pull from in order to fetch the document. + */ + public String exportLink; +} diff --git a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsAuthService.java b/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsAuthService.java index 8b78297..921c2dc 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsAuthService.java +++ b/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsAuthService.java @@ -20,14 +20,14 @@ package org.xwiki.apps.googleapps; import org.xwiki.component.annotation.Role; + import com.xpn.xwiki.user.api.XWikiAuthService; /** - * A badge interface to denote the role of the component that will replace the - * default authentication-service. + * A badge interface to denote the role of the component that will replace the default authentication-service. * - * @since 3.0 * @version $Id$ + * @since 3.0 */ @Role public interface GoogleAppsAuthService extends XWikiAuthService diff --git a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsManager.java b/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsManager.java index 16f9770..0b7775c 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsManager.java +++ b/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsManager.java @@ -35,9 +35,8 @@ import com.xpn.xwiki.objects.BaseObject; /** - * The specification of the methods that the manager of the GoogleApps application - * is doing. Methods of this interface are mostly called by the script-service (itself - * called by the views). + * The specification of the methods that the manager of the GoogleApps application is doing. Methods of this interface + * are mostly called by the script-service (itself called by the views). * * @version $Id$ * @since 3.0 @@ -58,10 +57,18 @@ public interface GoogleAppsManager * @since 3.0 */ @Unstable - boolean useDrive(); + boolean isDriveEnabled(); + + /** + * @return if the app is configured to use the Google Drive integration (default: yes). + * @since 3.0 + */ + @Unstable + int getConfigCookiesTTL(); /** * Reads the manifest to find when the JAR file was assembled by maven. + * * @return the build date. * @since 3.0 */ @@ -73,7 +80,7 @@ public interface GoogleAppsManager * * @return found credential * @throws XWikiException if the interaction with xwiki failed - * @throws IOException if a communication problem to Google services occured + * @throws IOException if a communication problem to Google services occured * @since 3.0 */ @Unstable @@ -85,53 +92,54 @@ public interface GoogleAppsManager * @param redirect If a redirect can be done * @return found credential * @throws XWikiException if the interaction with xwiki failed - * @throws IOException if a communication problem to Google services occured + * @throws IOException if a communication problem to Google services occured * @since 3.0 */ @Unstable Credential authorize(boolean redirect) throws XWikiException, IOException; /** - * Performs the necessary communication with Google-Services to fetch identity and - * update the XWiki-user object or possibly sends a redirect to a Google login screen. + * Performs the necessary communication with Google-Services to fetch identity and update the XWiki-user object or + * possibly sends a redirect to a Google login screen. * - * @return "failed login" if failed, "no user" (can be attempted to Google-OAuth), - * or "ok" if successful + * @return "failed login" if failed, "no user" (can be attempted to Google-OAuth), or "ok" if successful * @since 3.0 */ @Unstable String updateUser(); /** - * Get the list of all documents in the user's associated account. + * Get the list of all documents in the user's associated account. * * @return A list of max 10 documents. * @throws XWikiException if an authorization process failed. - * @throws IOException if a communication process to Google services occurred. + * @throws IOException if a communication process to Google services occurred. * @since 3.0 */ @Unstable List getDocumentList() throws XWikiException, IOException; - /** Fetches a list of Google Drive document matching a substring query in the filename. + /** + * Fetches a list of Google Drive document matching a substring query in the filename. * - * @param query the expected query (e.g. fullText contains winter ski) + * @param query the expected query (e.g. fullText contains winter ski) * @param nbResults max number of results * @return The list of files at Google Drive. * @throws XWikiException if an XWiki issue occurs - * @throws IOException if an error interacting with Google services occurred + * @throws IOException if an error interacting with Google services occurred * @since 3.0 */ @Unstable List listDriveDocumentsWithTypes(String query, int nbResults) throws XWikiException, IOException; - /** Fetches a list of Google Drive document matching a given query. + /** + * Fetches a list of Google Drive document matching a given query. * - * @param query the expected filename substring + * @param query the expected filename substring * @param nbResults max number of results * @return The list of files at Google Drive. * @throws XWikiException if an XWiki issue occurs - * @throws IOException if an error interacting with Google services occurred + * @throws IOException if an error interacting with Google services occurred * @since 3.0 */ @Unstable @@ -142,11 +150,11 @@ public interface GoogleAppsManager * * @param page attach to this page * @param name attach using this file name - * @param id store object attached to this attachment using this id (for later sync) - * @param url fetch from this URL + * @param id store object attached to this attachment using this id (for later sync) + * @param url fetch from this URL * @return true if successful * @throws XWikiException if an issue occurred in XWiki - * @throws IOException if an issue occurred in the communication with teh Google services + * @throws IOException if an issue occurred in the communication with teh Google services * @since 3.0 */ @Unstable @@ -162,36 +170,13 @@ public interface GoogleAppsManager * @since 3.0 */ @Unstable - GoogleAppsManager.GoogleDocMetadata getGoogleDocument(String pageName, String fileName) throws XWikiException; - - /** - * Simple pojo for metadata about a google doc. - * - * @since 3.0 - */ - @Unstable - class GoogleDocMetadata - { - /** - * Google's internal id to find the document again. - */ - public String id; - - /** - * URL to direct the user to for editing. - */ - public String editLink; - - /** - * URL to pull from in order to fetch the document. - */ - public String exportLink; - } + DriveDocMetadata getGoogleDocument(String pageName, String fileName) throws XWikiException; /** * Reads the extension and document name. + * * @param docName the raw docName - * @param elink the link where to read the extension name + * @param elink the link where to read the extension name * @return an array with extension and simplified document name * @since 3.0 */ @@ -202,25 +187,26 @@ class GoogleDocMetadata * Inserts the current information on the document to be embedded. * * @param docId the identifier of the Google Docs document to be embedded - * @param doc the XWiki document where to attach the embedding - * @param obj the XWiki object where this embedding is to be updated (or null if it is to be created) - * @param nb the number of the embedding across all the page's embeddings + * @param doc the XWiki document where to attach the embedding + * @param obj the XWiki object where this embedding is to be updated (or null if it is to be created) + * @param nb the number of the embedding across all the page's embeddings * @return the created or actualized document - * @throws IOException If the communication with Google went wrong + * @throws IOException If the communication with Google went wrong * @throws XWikiException If something at the XWiki side went wrong (e.g. saving) */ @Unstable - public BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, BaseObject obj, int nb) throws IOException, XWikiException; + public BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, BaseObject obj, int nb) + throws IOException, XWikiException; /** * Saves the attachment stored in XWiki to the Google drive of the user attached to the current logged-in user. * * @param page the XWiki page name * @param name the attachment name - * @return a record with the keys fileName, exportLink, version, editLink, embedLink, - * and google-user's email-address + * @return a record with the keys fileName, exportLink, version, editLink, embedLink, and google-user's + * email-address * @throws XWikiException if something went wrong at the XWiki side - * @throws IOException if something went wrong int he communication with Google drive. + * @throws IOException if something went wrong int he communication with Google drive. * @since 3.0 */ @Unstable @@ -229,12 +215,10 @@ class GoogleDocMetadata /** * Reads the google user-info attached to the current user as stored in the request. * - * @return the google user-info with keys displayName, emails (array of type,value pairs), - * etag, id, image (map with keys isDefault and url), kind, language, - * name (map with keys familyName and givenName). + * @return the google user-info with keys displayName, emails (array of type,value pairs), etag, id, image (map with + * keys isDefault and url), kind, language, name (map with keys familyName and givenName). * @since 3.0 */ @Unstable Map getGoogleUser(); - } diff --git a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsScriptService.java b/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsScriptService.java index fb29038..5d76125 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsScriptService.java +++ b/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsScriptService.java @@ -44,15 +44,14 @@ /** * Script service containing the methods used by the view files contained in the ui module. * - * @since 3.0 * @version $Id$ + * @since 3.0 */ @Component @Named("googleApps") @Singleton public class GoogleAppsScriptService implements ScriptService { - @Inject private GoogleAppsManager manager; @@ -65,7 +64,8 @@ public class GoogleAppsScriptService implements ScriptService * @since 3.0 */ @Unstable - public boolean isActive() throws XWikiException { + public boolean isActive() throws XWikiException + { return manager.isActive(); } @@ -74,11 +74,11 @@ public boolean isActive() throws XWikiException { * @since 3.0 */ @Unstable - public boolean useDrive() { - return manager.useDrive(); + public boolean isDriveEnabled() + { + return manager.isDriveEnabled(); } - /** * Reads the manifest to find when the JAR file was assembled by maven. * @@ -86,7 +86,8 @@ public boolean useDrive() { * @since 3.0 */ @Unstable - public Date getBuildTime() { + public Date getBuildTime() + { return manager.getBuildTime(); } @@ -95,99 +96,104 @@ public Date getBuildTime() { * * @return found credential * @throws XWikiException if the interaction with xwiki failed - * @throws IOException if a communication problem to Google services occured + * @throws IOException if a communication problem to Google services occured * @since 3.0 */ @Unstable - public Credential authorize() throws XWikiException, IOException { + public Credential authorize() throws XWikiException, IOException + { return manager.authorize(); } - /** * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. * * @param redirect If a redirect can be done * @return found credential * @throws XWikiException if the interaction with xwiki failed - * @throws IOException if a communication problem to Google services occured + * @throws IOException if a communication problem to Google services occured * @since 3.0 */ @Unstable - public Credential authorize(boolean redirect) throws XWikiException, IOException { + public Credential authorize(boolean redirect) throws XWikiException, IOException + { return manager.authorize(redirect); } /** - * Performs the necessary communication with Google-Services to fetch identity and - * update the XWiki-user object or possibly sends a redirect to a Google login screen. + * Performs the necessary communication with Google-Services to fetch identity and update the XWiki-user object or + * possibly sends a redirect to a Google login screen. * - * @return "failed login" if failed, "no user" (can be attempted to Google-OAuth), - * or "ok" if successful + * @return "failed login" if failed, "no user" (can be attempted to Google-OAuth), or "ok" if successful * @since 3.0 */ @Unstable - public String updateUser() { + public String updateUser() + { return manager.updateUser(); } /** - * Get the list of all documents in the user's associated account. + * Get the list of all documents in the user's associated account. * * @return A list of max 10 documents. * @throws XWikiException if an authorization process failed. - * @throws IOException if a communication process to Google services occurred. + * @throws IOException if a communication process to Google services occurred. * @since 3.0 */ @Unstable - public List getDocumentList() throws XWikiException, IOException { + public List getDocumentList() throws XWikiException, IOException + { return manager.getDocumentList(); } /** * Fetches a list of Google Drive document matching a substring query in the filename. * - * @param query the expected query (e.g. fullText contains winter ski) + * @param query the expected query (e.g. fullText contains winter ski) * @param nbResults max number of results * @return The list of files at Google Drive. * @throws XWikiException if an XWiki issue occurs - * @throws IOException if an error interacting with Google services occurred + * @throws IOException if an error interacting with Google services occurred * @since 3.0 */ @Unstable - public List listDriveDocumentsWithTypes(String query, int nbResults) throws XWikiException, IOException { + public List listDriveDocumentsWithTypes(String query, int nbResults) throws XWikiException, IOException + { return manager.listDriveDocumentsWithTypes(query, nbResults); } /** * Fetches a list of Google Drive document matching a given query. * - * @param query the expected filename substring + * @param query the expected filename substring * @param nbResults max number of results * @return The list of files at Google Drive. * @throws XWikiException if an XWiki issue occurs - * @throws IOException if an error interacting with Google services occurred + * @throws IOException if an error interacting with Google services occurred * @since 3.0 */ @Unstable - public FileList listDocuments(String query, int nbResults) throws XWikiException, IOException { + public FileList listDocuments(String query, int nbResults) throws XWikiException, IOException + { return manager.listDocuments(query, nbResults); } - /** * Inserts the current information on the document to be embedded. * * @param docId the identifier of the Google Docs document to be embedded - * @param doc the XWiki document where to attach the embedding - * @param obj the XWiki object where this embedding is to be updated (or null if it is to be created) - * @param nb the number of the embedding across all the page's embeddings + * @param doc the XWiki document where to attach the embedding + * @param obj the XWiki object where this embedding is to be updated (or null if it is to be created) + * @param nb the number of the embedding across all the page's embeddings * @return the created or actualized document - * @throws IOException If the communication with Google went wrong + * @throws IOException If the communication with Google went wrong * @throws XWikiException If something at the XWiki side went wrong (e.g. saving) */ @Unstable - public Object createOrUpdateEmbedObject(String docId, Document doc, Object obj, int nb) throws XWikiException, IOException { + public Object createOrUpdateEmbedObject(String docId, Document doc, Object obj, int nb) + throws XWikiException, IOException + { return new Object(manager.createOrUpdateEmbedObject(docId, doc.getDocument(), obj == null ? null : obj.getXWikiObject(), nb), contextProvider.get()); @@ -198,16 +204,17 @@ public Object createOrUpdateEmbedObject(String docId, Document doc, Object obj, * * @param page attach to this page * @param name attach using this file name - * @param id store object attached to this attachment using this id (for later sync) - * @param url fetch from this URL + * @param id store object attached to this attachment using this id (for later sync) + * @param url fetch from this URL * @return true if successful * @throws XWikiException if an issue occurred in XWiki - * @throws IOException if an issue occurred in the communication with teh Google services + * @throws IOException if an issue occurred in the communication with teh Google services * @since 3.0 */ @Unstable - public boolean retrieveFileFromGoogle(String page, String name, String id, String url) - throws XWikiException, IOException { + public boolean retrieveFileFromGoogle(String page, String name, String id, String url) + throws XWikiException, IOException + { return manager.retrieveFileFromGoogle(page, name, id, url); } @@ -221,22 +228,23 @@ public boolean retrieveFileFromGoogle(String page, String name, String id, Stri * @since 3.0 */ @Unstable - public GoogleAppsManager.GoogleDocMetadata getGoogleDocument(String pageName, String fileName) - throws XWikiException { + public DriveDocMetadata getGoogleDocument(String pageName, String fileName) + throws XWikiException + { return manager.getGoogleDocument(pageName, fileName); } - /** * Reads the extension and document name. * * @param docName the raw docName - * @param elink the link where to read the extension name + * @param elink the link where to read the extension name * @return an array with extension and simplified document name * @since 3.0 */ @Unstable - public String[] getExportLink(String docName, String elink) { + public String[] getExportLink(String docName, String elink) + { return manager.getExportLink(docName, elink); } @@ -245,30 +253,29 @@ public String[] getExportLink(String docName, String elink) { * * @param page the XWiki page name * @param name the attachment name - * @return a record with the keys fileName, exportLink, version, editLink, embedLink, - * and google-user's email-address + * @return a record with the keys fileName, exportLink, version, editLink, embedLink, and google-user's + * email-address * @throws XWikiException if something went wrong at the XWiki side - * @throws IOException if something went wrong int he communication with Google drive. + * @throws IOException if something went wrong int he communication with Google drive. * @since 3.0 */ @Unstable public Map saveAttachmentToGoogle(String page, String name) - throws XWikiException, IOException { + throws XWikiException, IOException + { return manager.saveAttachmentToGoogle(page, name); } /** * Reads the google user-info attached to the current user as stored in the request. * - * @return the google user-info with keys displayName, emails (array of type,value pairs), - * etag, id, image (map with keys isDefault and url), kind, language, - * name (map with keys familyName and givenName). + * @return the google user-info with keys displayName, emails (array of type,value pairs), etag, id, image (map with + * keys isDefault and url), kind, language, name (map with keys familyName and givenName). * @since 3.0 */ @Unstable - public Map getGoogleUser() { + public Map getGoogleUser() + { return manager.getGoogleUser(); } - - } diff --git a/api/src/main/java/org/xwiki/apps/googleapps/internal/CookieAuthenticationPersistenceImpl.java b/api/src/main/java/org/xwiki/apps/googleapps/internal/CookieAuthenticationPersistenceImpl.java index 0b12582..08ca7a5 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/internal/CookieAuthenticationPersistenceImpl.java +++ b/api/src/main/java/org/xwiki/apps/googleapps/internal/CookieAuthenticationPersistenceImpl.java @@ -27,42 +27,47 @@ import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.SecretKeySpec; import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; import javax.servlet.http.Cookie; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; - import org.slf4j.Logger; import org.xwiki.apps.googleapps.CookieAuthenticationPersistence; +import org.xwiki.apps.googleapps.GoogleAppsManager; import org.xwiki.component.annotation.Component; import org.xwiki.component.annotation.InstantiationStrategy; import org.xwiki.component.descriptor.ComponentInstantiationStrategy; -import org.xwiki.component.manager.ComponentManager; +import org.xwiki.component.phase.Initializable; +import org.xwiki.component.phase.InitializationException; +import org.xwiki.configuration.ConfigurationSource; import org.xwiki.stability.Unstable; import com.xpn.xwiki.XWikiContext; -import com.xpn.xwiki.XWikiException; /** - * Tools to help storing and retrieving enriched information within cookies such as the - * linked Google user profile. + * Tools to help storing and retrieving enriched information within cookies such as the linked Google user profile. + *

+ * Copied code from xwiki-authenticator-trusted https://github.com/xwiki-contrib/xwiki-authenticator-trusted/edit/master\ + * /xwiki-authenticator-trusted-api/src/main/java/org/xwiki/contrib/authentication\ + * /internal/CookieAuthenticationPersistenceStore.java. * - * Copied code from xwiki-authenticator-trusted - * https://github.com/xwiki-contrib/xwiki-authenticator-trusted/edit/master\ - * /xwiki-authenticator-trusted-api/src/main/java/org/xwiki/contrib/authentication\ - * /internal/CookieAuthenticationPersistenceStore.java. * @version $Id$ * @since 3.0 */ @Component @InstantiationStrategy(ComponentInstantiationStrategy.PER_LOOKUP) -public class CookieAuthenticationPersistenceImpl implements CookieAuthenticationPersistence +public class CookieAuthenticationPersistenceImpl implements CookieAuthenticationPersistence, Initializable { private static final String AUTHENTICATION_CONFIG_PREFIX = "xwiki.authentication"; private static final String COOKIE_PREFIX_PROPERTY = AUTHENTICATION_CONFIG_PREFIX + ".cookieprefix"; - private static final String COOKIE_PATH_PROPERTY = AUTHENTICATION_CONFIG_PREFIX + ".cookiepath"; + + private static final String COOKIE_PATH_PROPERTY = AUTHENTICATION_CONFIG_PREFIX + ".cookiepath"; + private static final String COOKIE_DOMAINS_PROPERTY = AUTHENTICATION_CONFIG_PREFIX + ".cookiedomains"; + private static final String ENCRYPTION_KEY_PROPERTY = AUTHENTICATION_CONFIG_PREFIX + ".encryptionKey"; private static final String CIPHER_ALGORITHM = "TripleDES"; @@ -75,40 +80,41 @@ public class CookieAuthenticationPersistenceImpl implements CookieAuthentication private static final String COOKIE_DOT_PFX = "."; private static final String EQUAL_SIGN = "="; + private static final String UNDERSCORE = "_"; @Inject private Logger logger; - private XWikiContext context; + @Inject + private Provider contextProvider; @Inject - private ComponentManager componentManager; + private GoogleAppsManager manager; private String cookiePfx; + private String cookiePath; + private String[] cookieDomains; - private long cookieMaxAge; + + private int cookieMaxAge; + private Cipher encryptionCipher; + private Cipher decryptionCipher; + @Inject + @Named("xwikicfg") + private Provider xwikicfgProvider; - /** - * Initialize the tool. - * - * @param context XWiki Context - * @param cookieMaxAge Time To Live of the created cookies in scd - * @throws XWikiException in case of trouble - * @since 3.0 - */ - @Unstable - public void initialize(XWikiContext context, long cookieMaxAge) throws XWikiException + public void initialize() throws InitializationException { - this.context = context; - cookiePfx = this.context.getWiki().Param(COOKIE_PREFIX_PROPERTY, ""); - cookiePath = this.context.getWiki().Param(COOKIE_PATH_PROPERTY, "/"); + cookiePfx = xwikicfgProvider.get().getProperty(COOKIE_PREFIX_PROPERTY, ""); + cookiePath = xwikicfgProvider.get().getProperty(COOKIE_PATH_PROPERTY, "/"); - String[] cdlist = StringUtils.split(this.context.getWiki().Param(COOKIE_DOMAINS_PROPERTY), ','); + String[] cdlist = StringUtils.split( + xwikicfgProvider.get().getProperty(COOKIE_DOMAINS_PROPERTY), ','); if (cdlist != null && cdlist.length > 0) { this.cookieDomains = new String[cdlist.length]; for (int i = 0; i < cdlist.length; ++i) { @@ -118,13 +124,13 @@ public void initialize(XWikiContext context, long cookieMaxAge) throws XWikiExce cookieDomains = null; } - this.cookieMaxAge = cookieMaxAge; + this.cookieMaxAge = manager.getConfigCookiesTTL(); try { encryptionCipher = getCipher(true); decryptionCipher = getCipher(false); } catch (Exception e) { - throw new XWikiException("Unable to initialize ciphers", e); + throw new InitializationException("Unable to initialize ciphers", e); } } @@ -137,7 +143,7 @@ public void initialize(XWikiContext context, long cookieMaxAge) throws XWikiExce public void clear() { cookieMaxAge = 0; - this.store(this.retrieve()); + this.setUserId(this.getUserId()); } /** @@ -147,19 +153,19 @@ public void clear() * @since 3.0 */ @Unstable - public void store(String userUid) + public void setUserId(String userUid) { Cookie cookie = new Cookie(cookiePfx + AUTHENTICATION_COOKIE, encryptText(userUid)); - cookie.setMaxAge((int) cookieMaxAge); + cookie.setMaxAge(cookieMaxAge); cookie.setPath(cookiePath); String cookieDomain = getCookieDomain(); if (cookieDomain != null) { cookie.setDomain(cookieDomain); } - if (context.getRequest().isSecure()) { + if (contextProvider.get().getRequest().isSecure()) { cookie.setSecure(true); } - context.getResponse().addCookie(cookie); + contextProvider.get().getResponse().addCookie(cookie); } /** @@ -169,7 +175,7 @@ public void store(String userUid) * @since 3.0 */ @Unstable - public String retrieve() + public String getUserId() { logger.info("retrieve cookie " + cookiePfx + AUTHENTICATION_COOKIE); String cookie = getCookieValue(cookiePfx + AUTHENTICATION_COOKIE); @@ -183,7 +189,7 @@ private Cipher getCipher(boolean encrypt) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException { Cipher cipher = null; - String secretKey = context.getWiki().Param(ENCRYPTION_KEY_PROPERTY); + String secretKey = xwikicfgProvider.get().getProperty(ENCRYPTION_KEY_PROPERTY); if (secretKey != null) { secretKey = secretKey.substring(0, 24); SecretKeySpec key = new SecretKeySpec(secretKey.getBytes(), CIPHER_ALGORITHM); @@ -213,7 +219,7 @@ private String decryptText(String text) logger.info("text to decrypt : " + text); String decryptedText = new String(decryptionCipher.doFinal( Base64.decodeBase64(text.replaceAll(UNDERSCORE, EQUAL_SIGN).getBytes( - StandardCharsets.ISO_8859_1)))); + StandardCharsets.ISO_8859_1)))); logger.info("decrypted text : " + decryptedText); return decryptedText; } catch (Exception e) { @@ -231,8 +237,8 @@ private String decryptText(String text) */ private String getCookieValue(String cookieName) { - if (context.getRequest() != null) { - Cookie cookie = context.getRequest().getCookie(cookieName); + if (contextProvider.get().getRequest() != null) { + Cookie cookie = contextProvider.get().getRequest().getCookie(cookieName); if (cookie != null) { logger.info("cookie : " + cookie); return cookie.getValue(); @@ -256,7 +262,7 @@ private String getCookieDomain() // Conform the server name like we conform cookie domain by prefixing with a dot. // This will ensure both localhost.localdomain and any.localhost.localdomain will match // the same cookie domain. - String servername = conformCookieDomain(context.getRequest().getServerName()); + String servername = conformCookieDomain(contextProvider.get().getRequest().getServerName()); for (String domain : this.cookieDomains) { if (servername.endsWith(domain)) { cookieDomain = domain; diff --git a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsAuthServiceImpl.java b/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsAuthServiceImpl.java index 8dac6f7..a2c77c8 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsAuthServiceImpl.java +++ b/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsAuthServiceImpl.java @@ -19,14 +19,6 @@ */ package org.xwiki.apps.googleapps.internal; - -import com.xpn.xwiki.XWikiContext; -import com.xpn.xwiki.XWikiException; -import com.xpn.xwiki.doc.XWikiDocument; -import com.xpn.xwiki.user.api.XWikiUser; -import com.xpn.xwiki.user.impl.xwiki.XWikiAuthServiceImpl; -import com.xpn.xwiki.web.XWikiRequest; - import java.net.URLEncoder; import java.security.Principal; import java.util.regex.Pattern; @@ -49,19 +41,25 @@ import org.xwiki.model.reference.DocumentReference; import org.xwiki.text.StringUtils; +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.user.api.XWikiUser; +import com.xpn.xwiki.user.impl.xwiki.XWikiAuthServiceImpl; +import com.xpn.xwiki.web.XWikiRequest; + /** - * An authenticator that can include a negotiation with the Google Cloud (e.g. Google Drive) services. - * This authenticator is created, configured and maintained by the GoogleAppsScriptService. + * An authenticator that can include a negotiation with the Google Cloud (e.g. Google Drive) services. This + * authenticator is created, configured and maintained by the GoogleAppsScriptService. * - * @since 3.0 * @version $Id$ + * @since 3.0 */ @Component @InstantiationStrategy(ComponentInstantiationStrategy.PER_LOOKUP) public class GoogleAppsAuthServiceImpl extends XWikiAuthServiceImpl implements GoogleAppsAuthService { - private static final String XWIKISPACE = "XWiki."; @Inject @@ -72,7 +70,12 @@ public class GoogleAppsAuthServiceImpl extends XWikiAuthServiceImpl private GoogleAppsManagerImpl googleAppsManager; - void setGoogleAppsManager(GoogleAppsManagerImpl m) { + @Inject + @Named("xwikicfg") + private Provider xwikicfgProvider; + + void setGoogleAppsManager(GoogleAppsManagerImpl m) + { this.googleAppsManager = m; } @@ -83,7 +86,8 @@ void setGoogleAppsManager(GoogleAppsManagerImpl m) { * @return a valid user, if found. * @throws XWikiException if anything went wrong */ - public XWikiUser checkAuth(XWikiContext context) throws XWikiException { + public XWikiUser checkAuth(XWikiContext context) throws XWikiException + { try { log.info("GoogleApps authentificator - checkAuth"); if (isLogoutRequest(context)) { @@ -103,27 +107,27 @@ public XWikiUser checkAuth(XWikiContext context) throws XWikiException { /** * Checks authentication. * - * @param username the name of the user to verify against - * @param password the password of the user to verify against + * @param username the name of the user to verify against + * @param password the password of the user to verify against * @param rememberme insert-cookies to remember the login - * @param context the context containing the request + * @param context the context containing the request * @return an XWikiUser is it succeded. * @throws XWikiException in case something goes wrong */ public XWikiUser checkAuth(String username, String password, - String rememberme, XWikiContext context) throws XWikiException { + String rememberme, XWikiContext context) throws XWikiException + { return super.checkAuth(username, password, rememberme, context); } - - /** * Redirect user to the login. * * @param context the xwiki-context of the request * @throws XWikiException a wrapped exception */ - public void showLogin(XWikiContext context) throws XWikiException { + public void showLogin(XWikiContext context) throws XWikiException + { log.info("GoogleApps authentificator - showLogin"); if (!googleAppsManager.isActive(context)) { return; @@ -136,8 +140,7 @@ public void showLogin(XWikiContext context) throws XWikiException { XWikiRequest request = context.getRequest(); CookieAuthenticationPersistence cookieTools = componentManager.getInstance(CookieAuthenticationPersistence.class); - cookieTools.initialize(context, googleAppsManager.getConfigCookiesTTL()); - String userCookie = cookieTools.retrieve(); + String userCookie = cookieTools.getUserId(); log.info("retrieved user from cookie : " + userCookie); String savedRequestId = request.getParameter( SavedRequestManager.getSavedRequestIdentifier()); @@ -161,7 +164,7 @@ public void showLogin(XWikiContext context) throws XWikiException { String finalURL = url + "?" + sridParameter + "&xredirect=" + URLEncoder.encode(redirectBack.toString(), "UTF-8"); - log.info("Redirecting to " + finalURL); + log.info("Redirecting to " + finalURL); redirected = true; context.getResponse().sendRedirect(finalURL); } @@ -180,12 +183,13 @@ public void showLogin(XWikiContext context) throws XWikiException { * * @param username the provided user-name * @param password the provided password - * @param context the context describing the request + * @param context the context describing the request * @return a null Principal Object if the user hasn't been authenticated or a valid Principal Object if the user is - * correctly authenticated + * correctly authenticated * @throws XWikiException if something goes wrong. */ - public Principal authenticate(String username, String password, XWikiContext context) throws XWikiException { + public Principal authenticate(String username, String password, XWikiContext context) throws XWikiException + { try { log.info("GoogleApps authentificator - authenticate"); @@ -204,13 +208,12 @@ public Principal authenticate(String username, String password, XWikiContext con log.info("Authenticate with cookie"); CookieAuthenticationPersistence cookieTools = componentManager.getInstance(CookieAuthenticationPersistence.class); - cookieTools.initialize(context, googleAppsManager.getConfigCookiesTTL()); - String userCookie = cookieTools.retrieve(); + String userCookie = cookieTools.getUserId(); if (userCookie != null) { log.info("Found user from cookie : " + userCookie); DocumentReference userDocRef = googleAppsManager.createUserReference(username); XWikiDocument userDoc = context.getWiki().getDocument(userDocRef, context); - if (!userDoc.isNew()) { + if (!userDoc.isNew()) { xwikiUser = userDocRef.getName(); } log.info("xwikiUser from cookie : " + xwikiUser); @@ -234,14 +237,11 @@ public Principal authenticate(String username, String password, XWikiContext con private Pattern logoutRequestMatcher; - @Inject - @Named("xwikicfg") - private Provider xwikicfgProvider; - /** * @return true if the current request match the configured logout page pattern. */ - private boolean isLogoutRequest(XWikiContext context) { + private boolean isLogoutRequest(XWikiContext context) + { if (logoutRequestMatcher == null) { if (xwikicfgProvider == null) { return false; diff --git a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsEventListener.java b/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsEventListener.java new file mode 100644 index 0000000..424f046 --- /dev/null +++ b/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsEventListener.java @@ -0,0 +1,82 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.xwiki.apps.googleapps.internal; + +import java.util.Arrays; +import java.util.List; + +import org.xwiki.bridge.event.ApplicationReadyEvent; +import org.xwiki.bridge.event.DocumentUpdatedEvent; +import org.xwiki.component.phase.InitializationException; +import org.xwiki.model.reference.DocumentReference; +import org.xwiki.observation.EventListener; +import org.xwiki.observation.event.Event; + +import com.xpn.xwiki.doc.XWikiDocument; + +public class GoogleAppsEventListener implements EventListener +{ + void GoogleAppsEventListener(GoogleAppsManagerImpl manager) + { + this.manager = manager; + } + + GoogleAppsManagerImpl manager; + + @Override + public String getName() + { + return "googleapps.scriptservice"; + } + + @Override + public List getEvents() + { + return Arrays.asList(new ApplicationReadyEvent(), new DocumentUpdatedEvent()); + } + + @Override + public void onEvent(Event event, Object source, Object data) + { + boolean applicationStarted = false, configChanged = false; + if (event instanceof ApplicationReadyEvent) { + applicationStarted = true; + } + if (event instanceof DocumentUpdatedEvent) { + XWikiDocument document = (XWikiDocument) source; + DocumentReference configDocRef = manager.getConfigDocRef(); + if (document != null && document.getDocumentReference().compareTo(configDocRef) == 0) { + configChanged = true; + } + } + + if (configChanged) { + manager.readConfigDoc(null); + } + + if (applicationStarted || configChanged) { + try { + manager.initialize(); + } catch (InitializationException e) { + e.printStackTrace(); + } + } + } +} diff --git a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsManagerImpl.java b/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsManagerImpl.java index 8bf7c3a..c9f7298 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsManagerImpl.java +++ b/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsManagerImpl.java @@ -19,6 +19,53 @@ */ package org.xwiki.apps.googleapps.internal; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; +import javax.servlet.http.HttpSession; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.RandomStringUtils; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.slf4j.Logger; +import org.xwiki.apps.googleapps.CookieAuthenticationPersistence; +import org.xwiki.apps.googleapps.DriveDocMetadata; +import org.xwiki.apps.googleapps.GoogleAppsAuthService; +import org.xwiki.apps.googleapps.GoogleAppsManager; +import org.xwiki.component.annotation.Component; +import org.xwiki.component.manager.ComponentLookupException; +import org.xwiki.component.manager.ComponentManager; +import org.xwiki.component.phase.Disposable; +import org.xwiki.component.phase.Initializable; +import org.xwiki.component.phase.InitializationException; +import org.xwiki.environment.Environment; +import org.xwiki.model.reference.DocumentReference; +import org.xwiki.model.reference.DocumentReferenceResolver; +import org.xwiki.model.reference.ObjectReference; +import org.xwiki.query.Query; +import org.xwiki.query.QueryException; +import org.xwiki.query.QueryManager; +import org.xwiki.stability.Unstable; + import com.google.api.client.auth.oauth2.Credential; import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeRequestUrl; @@ -48,58 +95,6 @@ import com.xpn.xwiki.web.XWikiRequest; import com.xpn.xwiki.web.XWikiResponse; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.RandomStringUtils; -import org.apache.http.HttpEntity; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.xwiki.apps.googleapps.CookieAuthenticationPersistence; -import org.xwiki.apps.googleapps.GoogleAppsAuthService; -import org.xwiki.apps.googleapps.GoogleAppsManager; -import org.xwiki.bridge.event.ApplicationReadyEvent; -import org.xwiki.bridge.event.DocumentUpdatedEvent; -import org.xwiki.component.manager.ComponentLookupException; -import org.xwiki.component.manager.ComponentManager; -import org.xwiki.component.phase.Disposable; -import org.xwiki.component.phase.Initializable; -import org.xwiki.component.phase.InitializationException; -import org.xwiki.environment.Environment; -import org.xwiki.model.reference.DocumentReference; -import org.xwiki.model.reference.DocumentReferenceResolver; -import org.xwiki.model.reference.ObjectReference; -import org.xwiki.observation.EventListener; -import org.xwiki.observation.event.Event; - -import javax.inject.Named; -import javax.inject.Provider; -import javax.inject.Singleton; -import javax.inject.Inject; -import javax.servlet.http.HttpSession; - -import org.slf4j.Logger; -import org.xwiki.component.annotation.Component; -import org.xwiki.query.Query; -import org.xwiki.query.QueryException; -import org.xwiki.query.QueryManager; -import org.xwiki.stability.Unstable; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.jar.Attributes; -import java.util.jar.Manifest; - /** * Set of methods accessible to the scripts using the GoogleApps functions. * @@ -109,7 +104,7 @@ @Component @Singleton public class GoogleAppsManagerImpl - implements GoogleAppsManager, EventListener, Initializable, Disposable + implements GoogleAppsManager, Initializable, Disposable { // ----------------------------- Lifecycle --------------------------- @@ -137,19 +132,12 @@ public class GoogleAppsManagerImpl @Inject private ComponentManager componentManager; - @Override - public String getName() - { - return "googleapps.scriptservice"; - } - - @Override public void initialize() throws InitializationException { - log.info("GoogleAppsScriptService initting."); - XWiki xwiki = getXWiki(); + log.info("GoogleAppsScriptService initializing."); XWikiContext context = xwikiContextProvider.get(); + XWiki xwiki = context.getWiki(); if (context != null) { readConfigDoc(context); @@ -175,57 +163,23 @@ public void initialize() throws InitializationException try { jacksonFactory = JacksonFactory.getDefaultInstance(); httpTransport = GoogleNetHttpTransport.newTrustedTransport(); - } catch (Exception e) { + } catch (Exception e) { e.printStackTrace(); throw new InitializationException("Trouble at initializing", e); } } - @Override - public List getEvents() - { - return Arrays.asList(new ApplicationReadyEvent(), new DocumentUpdatedEvent()); - } - - @Override - public void onEvent(Event event, Object source, Object data) - { - log.info("Event triggered: " + event + " with source " + source); - boolean applicationStarted = false, configChanged = false; - if (event instanceof ApplicationReadyEvent) { - applicationStarted = true; - } - if (event instanceof DocumentUpdatedEvent) { - XWikiDocument document = (XWikiDocument) source; - configDocRef = getConfigDocRef(); - if (document != null && document.getDocumentReference().compareTo(configDocRef) == 0) { - configChanged = true; - } - } - - if(configChanged) { - log.info("Reloading config."); - readConfigDoc(xwikiContextProvider.get()); - } - - if(applicationStarted || configChanged) { - try { - initialize(); - } catch (InitializationException e) { - e.printStackTrace(); - } - } - } - - // internals private GoogleAppsAuthServiceImpl authService; private DocumentReference configDocRef; + private ObjectReference configObjRef; - /** A map of hash to full redirects. */ + /** + * A map of hash to full redirects. + */ private Map storedStates = new HashMap<>(); private FileDataStoreFactory dsFactory; @@ -236,90 +190,120 @@ public void onEvent(Event event, Object source, Object data) private CloseableHttpClient httpclient = HttpClients.createDefault(); - private BaseObject getConfigDoc(XWikiContext context) throws XWikiException { configDocRef = getConfigDocRef(); XWikiDocument doc = context.getWiki().getDocument(configObjRef, context); BaseObject result = doc.getXObject(configObjRef, false, context); - if (result == null) { + if (result == null) { log.warn("Can't access Config document."); } return result; } private Boolean configActiveFlag; + private Boolean useCookies; + private Boolean skipLoginPage; + private Boolean authWithCookies; + private String configAppName; + private String configClientId; + private String configClientSecret; + private String configDomain; private Boolean configScopeUseAvatar; + private Boolean configScopeUseDrive; - private Long configCookiesTTL; + + private Integer configCookiesTTL; // ---------------------------- constants --------------------------------------------- private static final String AVATAR = "avatar"; + private static final String SPACENAME = "GoogleApps"; + private static final String VIEWACTION = "view"; + private static final String WIKINAME = "xwiki"; + private static final String XWIKISPACE = "XWiki"; + private static final String XWIKIGUEST = "XWikiGuest"; + private static final String AUTOAPPROVAL = "auto"; + private static final String EMAIL = "email"; + private static final String PASSWORD = "password"; + private static final String FIRSTNAME = "first_name"; + private static final String LASTNAME = "last_name"; + private static final String OAUTH = "OAuth"; + private static final String FAILEDLOGIN = "failed login"; + private static final String NOUSER = "no user"; + private static final String USER = "user"; + private static final String GOOGLEUSERATT = "googleUser"; + private static final String ID = "id"; + private static final String FILENAME = "fileName"; + private static final String VERSION = "version"; + private static final String URL = "url"; + private static final String EXPORTLINK = "exportLink"; + private static final String EDITLINK = "editLink"; - private static final String EMBEDLINK = "embedLink"; + private static final String EMBEDLINK = "embedLink"; private static final String UPDATECOMMENT = "Updated Google Apps Document metadata"; - private static final String EXPORTFORMATEQ = "exportFormat="; + private static final String EXPORTFORMATEQ = "exportFormat="; // ----------------------------- APIs ------------------------------------------------- /** - * Evaluates weather the application is active and licensed by looking at the stored documents. - * Within a request, this method should always be the first to be called so that the config-object - * is read and other properties are cached if need be. The context is extracted from the thead-local. + * Evaluates weather the application is active and licensed by looking at the stored documents. Within a request, + * this method should always be the first to be called so that the config-object is read and other properties are + * cached if need be. The context is extracted from the thead-local. * * @return True if documents were readable, and the is licensed and active; false otherwise. * @since 3.0 - * */ + */ @Unstable - public boolean isActive() { + public boolean isActive() + { return isActive(xwikiContextProvider.get()); } /** - * Evaluates weather the application is active and licensed by looking at the stored documents. - * Within a request, this method should always be the first to be called so that the config-object - * is read and other properties are cached if need be. + * Evaluates weather the application is active and licensed by looking at the stored documents. Within a request, + * this method should always be the first to be called so that the config-object is read and other properties are + * cached if need be. * * @param context The context (a page request). * @return True if documents were readable, and the is licensed and active; false otherwise. * @since 3.0 - * */ + */ @Unstable public boolean isActive(XWikiContext context) { - log.info("Is active " + this.toString() + " with configClient non-null? " + (configClientId!=null)); + log.info("Is active " + this.toString() + " with configClient non-null? " + (configClientId != null)); if (configActiveFlag == null || configClientId == null || configClientId.length() == 0) { readConfigDoc(context); } @@ -341,7 +325,8 @@ public boolean isActive(XWikiContext context) * @since 3.0 */ @Unstable - public boolean useCookies() { + public boolean useCookies() + { return useCookies; } @@ -350,7 +335,8 @@ public boolean useCookies() { * @since 3.0 */ @Unstable - public boolean skipLoginPage() { + public boolean skipLoginPage() + { return skipLoginPage; } @@ -359,7 +345,8 @@ public boolean skipLoginPage() { * @since 3.0 */ @Unstable - public boolean authWithCookies() { + public boolean authWithCookies() + { return authWithCookies; } @@ -368,31 +355,37 @@ public boolean authWithCookies() { * @since 3.0 */ @Unstable - long getConfigCookiesTTL() { + public int getConfigCookiesTTL() + { return configCookiesTTL; } - private void readConfigDoc(XWikiContext context) { + void readConfigDoc(XWikiContext context) + { + + if (context == null) { + context = xwikiContextProvider.get(); + } try { log.warn("Attempting to fetch Config doc"); BaseObject config = getConfigDoc(context); if (config != null) { - configActiveFlag = 0 != config.getIntValue("activate"); - useCookies = 0 != config.getIntValue("useCookies"); - skipLoginPage = 0 != config.getIntValue("skipLoginPage"); - authWithCookies = 0 != config.getIntValue("authWithCookies"); - configAppName = config.getStringValue("appname").trim(); - configClientId = config.getStringValue("clientid").trim(); + configActiveFlag = 0 != config.getIntValue("activate"); + useCookies = 0 != config.getIntValue("useCookies"); + skipLoginPage = 0 != config.getIntValue("skipLoginPage"); + authWithCookies = 0 != config.getIntValue("authWithCookies"); + configAppName = config.getStringValue("appname").trim(); + configClientId = config.getStringValue("clientid").trim(); configClientSecret = config.getStringValue("secret").trim(); - configDomain = config.getStringValue("domain").trim(); + configDomain = config.getStringValue("domain").trim(); if (configDomain.length() == 0) { configDomain = null; } List configScopes = Arrays.asList(config.getStringValue("scope").split("\\s")); configScopeUseAvatar = configScopes.contains(AVATAR); configScopeUseDrive = configScopes.contains("drive"); - configCookiesTTL = config.getLongValue("cookiesTTL"); + configCookiesTTL = config.getIntValue("cookiesTTL"); } } catch (XWikiException e) { e.printStackTrace(); @@ -409,7 +402,8 @@ private void readConfigDoc(XWikiContext context) { * @since 3.0 */ @Unstable - public Date getBuildTime() { + public Date getBuildTime() + { try { Class clazz = getClass(); String className = clazz.getSimpleName() @@ -427,7 +421,6 @@ public Date getBuildTime() { } } - // from ActiveDirectorySetupListener /** @@ -446,7 +439,6 @@ public void dispose() } } - // ----------------------------------------------------------------------------------------- private XWiki getXWiki() { @@ -467,27 +459,28 @@ private XWiki getXWiki() * @since 3.0 */ @Unstable - public boolean useDrive() { + public boolean isDriveEnabled() + { return configScopeUseDrive; } - - - // ----------------------------- Google Apps Tool (mostly request specific) ----------------------------------- - private String getOAuthUrl() throws XWikiException { + private String getOAuthUrl() throws XWikiException + { XWikiContext context = xwikiContextProvider.get(); DocumentReference oauthReference = new DocumentReference(context.getWikiId(), SPACENAME, OAUTH); return getXWiki().getDocument(oauthReference, context).getExternalURL(VIEWACTION, context); } - private DocumentReference getXWikiUserClassRef() { + private DocumentReference getXWikiUserClassRef() + { return new DocumentReference(WIKINAME, XWIKISPACE, "XWikiUsers"); } - private String getCurrentXWikiUserName() { + private String getCurrentXWikiUserName() + { DocumentReference userDoc = xwikiContextProvider.get().getUserReference(); String uName = userDoc == null ? XWIKIGUEST : userDoc.getName(); if (XWIKIGUEST.equals(uName)) { @@ -502,26 +495,31 @@ private String getCurrentXWikiUserName() { * @since 3.0 */ @Unstable - DocumentReference createUserReference(String userName) { + DocumentReference createUserReference(String userName) + { return userResolver.resolve(userName); } private DocumentReference gauthClassRef; - private DocumentReference getGoogleAuthClassReference() { + + private DocumentReference getGoogleAuthClassReference() + { if (gauthClassRef == null) { gauthClassRef = new DocumentReference(WIKINAME, SPACENAME, "GoogleAppsAuthClass"); } return gauthClassRef; } - private DocumentReference getSyncDocClassReference() { + private DocumentReference getSyncDocClassReference() + { if (gauthClassRef == null) { gauthClassRef = new DocumentReference(WIKINAME, SPACENAME, "SynchronizedDocumentClass"); } return gauthClassRef; } - private DocumentReference getConfigDocRef() { + DocumentReference getConfigDocRef() + { if (configDocRef == null) { configDocRef = new DocumentReference(WIKINAME, SPACENAME, "GoogleAppsConfig"); @@ -530,7 +528,6 @@ private DocumentReference getConfigDocRef() { return configDocRef; } - /** * Build flow and trigger user authorization request. * @@ -555,27 +552,26 @@ private GoogleAuthorizationCodeFlow getFlow() throws IOException // create flow return new GoogleAuthorizationCodeFlow.Builder( - httpTransport, - jacksonFactory, configClientId, configClientSecret, gScopes) - .setDataStoreFactory(dsFactory) - .setAccessType("online").setApprovalPrompt(AUTOAPPROVAL) - .setClientId(configClientId) - .build(); + httpTransport, + jacksonFactory, configClientId, configClientSecret, gScopes) + .setDataStoreFactory(dsFactory) + .setAccessType("online").setApprovalPrompt(AUTOAPPROVAL) + .setClientId(configClientId) + .build(); } catch (Exception e) { e.printStackTrace(); throw new IOException("Issue at building Google Authorization Flow.", e); } } - /** * Exchange an authorization code for OAuth 2.0 credentials. * - * @param authorizationCode Authorization code to exchange for OAuth 2.0 - * credentials. + * @param authorizationCode Authorization code to exchange for OAuth 2.0 credentials. * @return OAuth 2.0 credentials. */ - private Credential exchangeCode(String authorizationCode) { + private Credential exchangeCode(String authorizationCode) + { try { GoogleAuthorizationCodeFlow flow = getFlow(); GoogleTokenResponse tokenResponse = flow @@ -591,7 +587,8 @@ private Credential exchangeCode(String authorizationCode) { } } - private Map getCredentialStore() { + private Map getCredentialStore() + { final String key = "GoogleAppsCredentialStore"; HttpSession session = xwikiContextProvider.get().getRequest().getSession(true); Map store = (Map) (session.getAttribute(key)); @@ -602,15 +599,15 @@ private Map getCredentialStore() { return store; } - private void storeCredentials(String userId, Credential credentials) throws XWikiException { + private void storeCredentials(String userId, Credential credentials) throws XWikiException + { try { if (userId.contains(XWIKIGUEST)) { if (useCookies) { // create a cookie CookieAuthenticationPersistence cookieTools = componentManager.getInstance(CookieAuthenticationPersistence.class); - cookieTools.initialize(xwikiContextProvider.get(), configCookiesTTL); - cookieTools.store(userId); + cookieTools.setUserId(userId); } } log.info("Storing credentials for user " + userId); @@ -621,7 +618,8 @@ private void storeCredentials(String userId, Credential credentials) throws XWik } } - private Credential getStoredCredentials(String userId) { + private Credential getStoredCredentials(String userId) + { if (userId == null) { return null; } @@ -629,26 +627,22 @@ private Credential getStoredCredentials(String userId) { return getCredentialStore().get(userId); } - /** * Retrieve credentials using the provided authorization code. + *

+ * This function exchanges the authorization code for an access token and queries the UserInfo API to retrieve the + * user's e-mail address. If a refresh token has been retrieved along with an access token, it is stored in the + * application database using the user's e-mail address as key. If no refresh token has been retrieved, the function + * checks in the application database for one and returns it if found or throws a NoRefreshTokenException with the + * authorization URL to redirect the user to. * - * This function exchanges the authorization code for an access token and - * queries the UserInfo API to retrieve the user's e-mail address. If a - * refresh token has been retrieved along with an access token, it is stored - * in the application database using the user's e-mail address as key. If no - * refresh token has been retrieved, the function checks in the application - * database for one and returns it if found or throws a NoRefreshTokenException - * with the authorization URL to redirect the user to. - * - * @param authorizationCode Authorization code to use to retrieve an access - * token. - * @return OAuth 2.0 credentials instance containing an access and refresh - * token. + * @param authorizationCode Authorization code to use to retrieve an access token. + * @return OAuth 2.0 credentials instance containing an access and refresh token. * @throws IOException Unable to load client_secret.json. */ private Credential retrieveCredentials(String authorizationCode, boolean redirect) - throws XWikiException, IOException { + throws XWikiException, IOException + { Credential credentials; String user = getCurrentXWikiUserName(); @@ -686,7 +680,8 @@ private Credential retrieveCredentials(String authorizationCode, boolean redirec return null; } - private String getAuthorizationURL() throws XWikiException, IOException { + private String getAuthorizationURL() throws XWikiException, IOException + { String state = ""; XWikiContext context = xwikiContextProvider.get(); XWikiRequest request = context.getRequest(); @@ -695,7 +690,7 @@ private String getAuthorizationURL() throws XWikiException, IOException { String finalRedirect = new URL( new URL(getXWiki().getExternalURL("GoogleApps.Login", VIEWACTION, context)), - context.getDoc().getURL(VIEWACTION, request.getQueryString(), context)).toExternalForm(); + context.getDoc().getURL(VIEWACTION, request.getQueryString(), context)).toExternalForm(); state = Integer.toHexString(finalRedirect.hashCode()); storedStates.put(state, finalRedirect); } @@ -710,8 +705,7 @@ private String getAuthorizationURL() throws XWikiException, IOException { try { CookieAuthenticationPersistence cookieTools = componentManager.getInstance(CookieAuthenticationPersistence.class); - cookieTools.initialize(context, configCookiesTTL); - String userId = cookieTools.retrieve(); + String userId = cookieTools.getUserId(); if (userId != null) { XWikiDocument userDoc = getXWiki().getDocument(createUserReference(userId), xwikiContextProvider.get()); @@ -737,28 +731,32 @@ private String getAuthorizationURL() throws XWikiException, IOException { return authurl; } - /** Inspects the stored information to see if an authorization or a redirect needs to be pronounced. + /** + * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. * * @return found credential * @throws XWikiException if the interaction with xwiki failed - * @throws IOException if a communication problem to Google services occured + * @throws IOException if a communication problem to Google services occured * @since 3.0 */ @Unstable - public Credential authorize() throws XWikiException, IOException { + public Credential authorize() throws XWikiException, IOException + { return authorize(true); } - /** Inspects the stored information to see if an authorization or a redirect needs to be pronounced. + /** + * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. * * @param redirect If a redirect can be done * @return found credential * @throws XWikiException if the interaction with xwiki failed - * @throws IOException if a communication problem to Google services occured + * @throws IOException if a communication problem to Google services occured * @since 3.0 */ @Unstable - public Credential authorize(boolean redirect) throws XWikiException, IOException { + public Credential authorize(boolean redirect) throws XWikiException, IOException + { log.info("In authorize"); GoogleAuthorizationCodeFlow flow = getFlow(); // useless? XWikiRequest request = xwikiContextProvider.get().getRequest(); @@ -776,17 +774,16 @@ public Credential authorize(boolean redirect) throws XWikiException, IOException return creds; } - /** - * Performs the necessary communication with Google-Services to fetch identity and - * update the XWiki-user object or possibly sends a redirect to a Google login screen. + * Performs the necessary communication with Google-Services to fetch identity and update the XWiki-user object or + * possibly sends a redirect to a Google login screen. * - * @return "failed login" if failed, NOUSER (can be attempted to Google-OAuth), - * or "ok" if successful + * @return "failed login" if failed, NOUSER (can be attempted to Google-OAuth), or "ok" if successful * @since 3.0 */ @Unstable - public String updateUser() { + public String updateUser() + { try { if (!isActive()) { return FAILEDLOGIN; @@ -807,16 +804,19 @@ public String updateUser() { // name:[familyName:..., givenName:...]] } log.info("user: " + user); + String usersEmailAddress = null; context.getRequest().setAttribute(GOOGLEUSERATT, user); if (user == null) { return NOUSER; - } else if (configDomain != null) { + } + if (configDomain != null) { boolean foundCompatibleDomain = false; if (user.getEmailAddresses() != null) { - for (EmailAddress address: user.getEmailAddresses()) { - String email = address.getValue(); - if (email.endsWith(configDomain)) { + for (EmailAddress address : user.getEmailAddresses()) { + String oneOfEmails = address.getValue(); + if (oneOfEmails.endsWith(configDomain)) { foundCompatibleDomain = true; + usersEmailAddress = oneOfEmails; break; } } @@ -827,182 +827,188 @@ public String updateUser() { log.debug("Wrong domain: Removed credentials for userid " + userId); return FAILEDLOGIN; } - } else { - // this seems undocumented but well working - String id = (String) user.get("resourceName"); - String email; - String currentWiki = context.getWikiId(); - try { - // Force main wiki database to create the user as global - context.setMainXWiki(WIKINAME); - email = (user.getEmailAddresses() != null && user.getEmailAddresses().size() > 0) - ? user.getEmailAddresses().get(0).getValue() : ""; - List wikiUserList = queryManager.createQuery( - "from doc.object(GoogleApps.GoogleAppsAuthClass) as auth where auth.id=:id", - Query.XWQL).bindValue(ID, id).execute(); - if ((wikiUserList == null) || (wikiUserList.size() == 0)) { - wikiUserList = queryManager.createQuery( - "from doc.object(XWiki.XWikiUsers) as user where user.email=:email", - Query.XWQL) - .bindValue(EMAIL, email).execute(); + } + // this seems undocumented but well working + String id = (String) user.get("resourceName"); + String currentWiki = context.getWikiId(); + try { + // Force main wiki database to create the user as global + context.setMainXWiki(WIKINAME); + if(usersEmailAddress == null) { + if (user.getEmailAddresses() != null && user.getEmailAddresses().size() > 0) { + usersEmailAddress = user.getEmailAddresses().get(0).getValue(); + } else { + usersEmailAddress = ""; } + } + List wikiUserList = queryManager.createQuery( + "from doc.object(GoogleApps.GoogleAppsAuthClass) as auth where auth.id=:id", + Query.XWQL).bindValue(ID, id).execute(); + if ((wikiUserList == null) || (wikiUserList.size() == 0)) { + wikiUserList = queryManager.createQuery( + "from doc.object(XWiki.XWikiUsers) as user where user.email=:email", + Query.XWQL) + .bindValue(EMAIL, usersEmailAddress).execute(); + } - if ((wikiUserList == null) || (wikiUserList.size() == 0)) { - // user not found.. need to create new user - xwikiUser = email.substring(0, email.indexOf("@")); - // make sure user is unique - xwikiUser = getXWiki().getUniquePageName(XWIKISPACE, xwikiUser, context); - // create user - DocumentReference userDirRef = new DocumentReference( - context.getWikiId(), "Main", "UserDirectory"); - String randomPassword = RandomStringUtils.randomAlphanumeric(8); - Map userAttributes = new HashMap<>(); + if ((wikiUserList == null) || (wikiUserList.size() == 0)) { + // user not found.. need to create new user + xwikiUser = usersEmailAddress.substring(0, usersEmailAddress.indexOf("@")); + // make sure user is unique + xwikiUser = getXWiki().getUniquePageName(XWIKISPACE, xwikiUser, context); + // create user + DocumentReference userDirRef = new DocumentReference( + context.getWikiId(), "Main", "UserDirectory"); + String randomPassword = RandomStringUtils.randomAlphanumeric(8); + Map userAttributes = new HashMap<>(); + + if (user.getNames() != null && user.getNames().size() > 0) { + userAttributes.put(FIRSTNAME, user.getNames().get(0).getGivenName()); + userAttributes.put(LASTNAME, user.getNames().get(0).getFamilyName()); + } + userAttributes.put(EMAIL, usersEmailAddress); + userAttributes.put(PASSWORD, randomPassword); + int isCreated = getXWiki().createUser(xwikiUser, userAttributes, + userDirRef, null, null, "edit", context); + // Add google apps id to the user + if (isCreated == 1) { + log.debug("Creating user " + xwikiUser); + XWikiDocument userDoc = getXWiki() + .getDocument(createUserReference(xwikiUser), context); + BaseObject userObj = userDoc.getXObject(getXWikiUserClassRef()); + // TODO: is this not redundant when having used createUser (map) ? if (user.getNames() != null && user.getNames().size() > 0) { - userAttributes.put(FIRSTNAME, user.getNames().get(0).getGivenName()); - userAttributes.put(LASTNAME, user.getNames().get(0).getFamilyName()); + userObj.set(FIRSTNAME, user.getNames().get(0).getGivenName(), context); + userObj.set(LASTNAME, user.getNames().get(0).getFamilyName(), context); } - userAttributes.put(EMAIL, email); - userAttributes.put(PASSWORD, randomPassword); - int isCreated = getXWiki().createUser(xwikiUser, userAttributes, - userDirRef, null, null, "edit", context); - // Add google apps id to the user - if (isCreated == 1) { - log.debug("Creating user " + xwikiUser); - XWikiDocument userDoc = getXWiki() - .getDocument(createUserReference(xwikiUser), context); - BaseObject userObj = userDoc.getXObject(getXWikiUserClassRef()); - - // TODO: is this not redundant when having used createUser (map) ? - if (user.getNames() != null && user.getNames().size() > 0) { - userObj.set(FIRSTNAME, user.getNames().get(0).getGivenName(), context); - userObj.set(LASTNAME, user.getNames().get(0).getFamilyName(), context); - } - userObj.set("active", 1, context); - if (configScopeUseAvatar && user.getPhotos() != null - && user.getPhotos().size() > 0 - && user.getPhotos().get(0).getUrl() != null) { - String photoUrl = user.getPhotos().get(0).getUrl(); - log.debug("Adding avatar " + photoUrl); - URL u = new URL(photoUrl); - InputStream b = u.openStream(); - String fileName = u.getFile().substring(u.getFile().lastIndexOf('/') + 1); - userDoc.addAttachment(fileName, u.openStream(), context); - userObj.set(AVATAR, fileName, context); - b.close(); - } - - userDoc.createXObject(getGoogleAuthClassReference(), context); - BaseObject gAppsAuthClass = userDoc.getXObject(getGoogleAuthClassReference()); - gAppsAuthClass.set(ID, id, context); - getXWiki().saveDocument(userDoc, "Google Apps login user creation", false, context); - } else { - log.debug("User creation failed"); - return FAILEDLOGIN; + userObj.set("active", 1, context); + if (configScopeUseAvatar && user.getPhotos() != null + && user.getPhotos().size() > 0 + && user.getPhotos().get(0).getUrl() != null) + { + String photoUrl = user.getPhotos().get(0).getUrl(); + log.debug("Adding avatar " + photoUrl); + URL u = new URL(photoUrl); + InputStream b = u.openStream(); + String fileName = u.getFile().substring(u.getFile().lastIndexOf('/') + 1); + userDoc.addAttachment(fileName, u.openStream(), context); + userObj.set(AVATAR, fileName, context); + b.close(); } + + userDoc.createXObject(getGoogleAuthClassReference(), context); + BaseObject gAppsAuthClass = userDoc.getXObject(getGoogleAuthClassReference()); + gAppsAuthClass.set(ID, id, context); + getXWiki().saveDocument(userDoc, "Google Apps login user creation", false, context); } else { - // user found.. we should update it if needed - xwikiUser = (String) (wikiUserList.get(0)); - log.debug("Found user " + xwikiUser); - boolean changed = false; - XWikiDocument userDoc = getXWiki().getDocument(createUserReference(xwikiUser), context); - BaseObject userObj = userDoc.getXObject(getXWikiUserClassRef()); - if (userObj == null) { - log.debug("User found is not a user"); - return FAILEDLOGIN; - } else { - if (!userObj.getStringValue(EMAIL).equals(email)) { - userObj.set(EMAIL, email, context); + log.debug("User creation failed"); + return FAILEDLOGIN; + } + } else { + // user found.. we should update it if needed + xwikiUser = (String) (wikiUserList.get(0)); + log.debug("Found user " + xwikiUser); + boolean changed = false; + XWikiDocument userDoc = getXWiki().getDocument(createUserReference(xwikiUser), context); + BaseObject userObj = userDoc.getXObject(getXWikiUserClassRef()); + if (userObj == null) { + log.debug("User found is not a user"); + return FAILEDLOGIN; + } else { + if (!userObj.getStringValue(EMAIL).equals(usersEmailAddress)) { + userObj.set(EMAIL, usersEmailAddress, context); + changed = true; + } + if (user.getNames() != null && user.getNames().size() > 0) { + if (!userObj.getStringValue(FIRSTNAME).equals( + user.getNames().get(0).getGivenName())) + { + userObj.set(FIRSTNAME, user.getNames().get(0).getGivenName(), context); changed = true; } - if (user.getNames() != null && user.getNames().size() > 0) { - if (!userObj.getStringValue(FIRSTNAME).equals( - user.getNames().get(0).getGivenName())) { - userObj.set(FIRSTNAME, user.getNames().get(0).getGivenName(), context); - changed = true; - } - if (!userObj.getStringValue(LASTNAME).equals( - user.getNames().get(0).getFamilyName())) { - userObj.set(LASTNAME, user.getNames().get(0).getFamilyName(), context); - changed = true; - } + if (!userObj.getStringValue(LASTNAME).equals( + user.getNames().get(0).getFamilyName())) + { + userObj.set(LASTNAME, user.getNames().get(0).getFamilyName(), context); + changed = true; } - if (configScopeUseAvatar && user.getPhotos() != null && user.getPhotos().size() > 0 - && user.getPhotos().get(0).getUrl() != null) { - String imageUrl = user.getPhotos().get(0).getUrl(); - imageUrl = imageUrl - + (imageUrl.contains("?") ? "&" : "?") - + "sz=256"; - log.debug("Pulling avatar " + imageUrl); - HttpGet httpget = new HttpGet(imageUrl); - // TODO: add an if-modified-since - CloseableHttpResponse response = httpclient.execute(httpget); - HttpEntity entity = response.getEntity(); - if (entity != null) { - ByteArrayOutputStream bOut = - new ByteArrayOutputStream((int) entity.getContentLength()); - IOUtils.copy(entity.getContent(), bOut); - byte[] bytesFromGoogle = bOut.toByteArray(); - - XWikiAttachment attachment = - userObj.getStringValue(AVATAR) == null ? null - : userDoc.getAttachment(userObj.getStringValue(AVATAR)); - boolean fileChanged = attachment == null - || attachment.getFilesize() != bytesFromGoogle.length; - if (!fileChanged) { - byte[] b = attachment.getContent(context); - for (int i = 0; i < b.length; i++) { - if (b[i] != bytesFromGoogle[i]) { - fileChanged = true; - break; - } + } + if (configScopeUseAvatar && user.getPhotos() != null && user.getPhotos().size() > 0 + && user.getPhotos().get(0).getUrl() != null) + { + String imageUrl = user.getPhotos().get(0).getUrl(); + imageUrl = imageUrl + + (imageUrl.contains("?") ? "&" : "?") + + "sz=256"; + log.debug("Pulling avatar " + imageUrl); + HttpGet httpget = new HttpGet(imageUrl); + // TODO: add an if-modified-since + CloseableHttpResponse response = httpclient.execute(httpget); + HttpEntity entity = response.getEntity(); + if (entity != null) { + ByteArrayOutputStream bOut = + new ByteArrayOutputStream((int) entity.getContentLength()); + IOUtils.copy(entity.getContent(), bOut); + byte[] bytesFromGoogle = bOut.toByteArray(); + + XWikiAttachment attachment = + userObj.getStringValue(AVATAR) == null ? null + : userDoc.getAttachment(userObj.getStringValue(AVATAR)); + boolean fileChanged = attachment == null + || attachment.getFilesize() != bytesFromGoogle.length; + if (!fileChanged) { + byte[] b = attachment.getContent(context); + for (int i = 0; i < b.length; i++) { + if (b[i] != bytesFromGoogle[i]) { + fileChanged = true; + break; } } - if (fileChanged) { - String fileName = imageUrl.substring(imageUrl.lastIndexOf('/') + 1); - log.debug("Avatar changed " + fileName); - userObj.set(AVATAR, fileName, context); - userDoc.addAttachment(fileName, bytesFromGoogle, context); - changed = true; - } } - + if (fileChanged) { + String fileName = imageUrl.substring(imageUrl.lastIndexOf('/') + 1); + log.debug("Avatar changed " + fileName); + userObj.set(AVATAR, fileName, context); + userDoc.addAttachment(fileName, bytesFromGoogle, context); + changed = true; + } } + } - BaseObject googleAppsAuth = userDoc.getXObject(getGoogleAuthClassReference()); - if (googleAppsAuth == null) { - userDoc.createXObject(getGoogleAuthClassReference(), context); - googleAppsAuth = userDoc.getXObject(getGoogleAuthClassReference()); - changed = true; - } + BaseObject googleAppsAuth = userDoc.getXObject(getGoogleAuthClassReference()); + if (googleAppsAuth == null) { + userDoc.createXObject(getGoogleAuthClassReference(), context); + googleAppsAuth = userDoc.getXObject(getGoogleAuthClassReference()); + changed = true; + } - if (!googleAppsAuth.getStringValue(ID).equals(id)) { - googleAppsAuth.set(ID, id, context); - changed = true; - } + if (!googleAppsAuth.getStringValue(ID).equals(id)) { + googleAppsAuth.set(ID, id, context); + changed = true; + } - if (changed) { - log.info("User changed."); - getXWiki().saveDocument(userDoc, "Google Apps login user updated.", context); - } else { - log.info("User unchanged."); - } + if (changed) { + log.info("User changed."); + getXWiki().saveDocument(userDoc, "Google Apps login user updated.", context); + } else { + log.info("User unchanged."); } } - } catch (QueryException qe) { - log.warn("Cannot query for users.", qe); - throw new XWikiException("Can't query for users.", qe); - } finally { - // Restore database - context.setMainXWiki(currentWiki); } + } catch (QueryException qe) { + log.warn("Cannot query for users.", qe); + throw new XWikiException("Can't query for users.", qe); + } finally { + // Restore database + context.setMainXWiki(currentWiki); + } - // we need to restore the credentials as the user will now be logged-in - storeCredentials(xwikiUser, credential); + // we need to restore the credentials as the user will now be logged-in + storeCredentials(xwikiUser, credential); - // store the validated xwiki user for the authentication module - context.getRequest().getSession().setAttribute("googleappslogin", xwikiUser); - } + // store the validated xwiki user for the authentication module + context.getRequest().getSession().setAttribute("googleappslogin", xwikiUser); return "ok"; } catch (Exception e) { log.warn("Problem at updateUser", e); @@ -1010,17 +1016,14 @@ public String updateUser() { } } - - - - /** * Builds and returns an authorized Drive client service. * * @return an authorized Drive client service * @throws IOException if a communication error occurs */ - private Drive getDriveService() throws XWikiException, IOException { + private Drive getDriveService() throws XWikiException, IOException + { Credential credential = authorize(); return new Drive.Builder( httpTransport, jacksonFactory, credential) @@ -1034,7 +1037,8 @@ private Drive getDriveService() throws XWikiException, IOException { * @return an authorized Drive client service * @throws IOException if a communication error occurred */ - private DocsService getDocsService() throws XWikiException, IOException { + private DocsService getDocsService() throws XWikiException, IOException + { Credential credential = authorize(); DocsService service = new DocsService(configAppName); service.setOAuth2Credentials(credential); @@ -1042,15 +1046,16 @@ private DocsService getDocsService() throws XWikiException, IOException { } /** - * Get the list of all documents in the user's associated account. + * Get the list of all documents in the user's associated account. * * @return A list of max 10 documents. * @throws XWikiException if an authorization process failed. - * @throws IOException if a communication process to Google services occurred. + * @throws IOException if a communication process to Google services occurred. * @since 3.0 */ @Unstable - public List getDocumentList() throws XWikiException, IOException { + public List getDocumentList() throws XWikiException, IOException + { Drive drive = getDriveService(); FileList result = drive.files().list().setMaxResults(10).execute(); return result.getItems(); @@ -1059,15 +1064,16 @@ public List getDocumentList() throws XWikiException, IOException { /** * Fetches a list of Google Drive document matching a substring query in the filename. * - * @param query the expected query (e.g. fullText contains winter ski) + * @param query the expected query (e.g. fullText contains winter ski) * @param nbResults max number of results * @return The list of files at Google Drive. * @throws XWikiException if an XWiki issue occurs - * @throws IOException if an error interacting with Google services occurred + * @throws IOException if an error interacting with Google services occurred * @since 3.0 */ @Unstable - public List listDriveDocumentsWithTypes(String query, int nbResults) throws XWikiException, IOException { + public List listDriveDocumentsWithTypes(String query, int nbResults) throws XWikiException, IOException + { Drive drive = getDriveService(); Drive.Files.List req = drive.files().list() .setQ(query) @@ -1080,15 +1086,16 @@ public List listDriveDocumentsWithTypes(String query, int nbResults) throw /** * Fetches a list of Google Drive document matching a given query. * - * @param query the expected filename substring + * @param query the expected filename substring * @param nbResults max number of results * @return The list of files at Google Drive. * @throws XWikiException if an XWiki issue occurs - * @throws IOException if an error interacting with Google services occurred + * @throws IOException if an error interacting with Google services occurred * @since 3.0 */ @Unstable - public FileList listDocuments(String query, int nbResults) throws XWikiException, IOException { + public FileList listDocuments(String query, int nbResults) throws XWikiException, IOException + { Drive drive = getDriveService(); Drive.Files.List req = drive.files().list().setQ(query).setMaxResults(nbResults); FileList result = req.execute(); @@ -1100,21 +1107,23 @@ public FileList listDocuments(String query, int nbResults) throws XWikiException * * @param page attach to this page * @param name attach using this file name - * @param id store object attached to this attachment using this id (for later sync) - * @param url fetch from this URL + * @param id store object attached to this attachment using this id (for later sync) + * @param url fetch from this URL * @return true if successful * @throws XWikiException if an issue occurred in XWiki - * @throws IOException if an issue occurred in the communication with teh Google services + * @throws IOException if an issue occurred in the communication with teh Google services * @since 3.0 */ @Unstable public boolean retrieveFileFromGoogle(String page, String name, String id, String url) - throws XWikiException, IOException { + throws XWikiException, IOException + { return retrieveFileFromGoogle(getDocsService(), getDriveService(), page, name, id, url); } private boolean retrieveFileFromGoogle(DocsService docsService, Drive driveService, - String page, String name, String id, String url) throws XWikiException { + String page, String name, String id, String url) throws XWikiException + { log.info("Retrieving " + name + " to page " + page + ": " + id + url); XWikiDocument adoc = getXWiki().getDocument(documentResolver.resolve(page), xwikiContextProvider.get()); @@ -1128,8 +1137,8 @@ private boolean retrieveFileFromGoogle(DocsService docsService, Drive driveServi } } - - private byte[] downloadFile(DocsService docsService, String exportUrl) throws XWikiException { + private byte[] downloadFile(DocsService docsService, String exportUrl) throws XWikiException + { try { MediaContent mc = new MediaContent(); mc.setUri(exportUrl); @@ -1160,7 +1169,8 @@ private byte[] downloadFile(DocsService docsService, String exportUrl) throws XW } private void saveFileToXWiki(Drive driveService, XWikiDocument adoc, - String id, String name, InputStream data, boolean redirect) throws XWikiException, IOException { + String id, String name, InputStream data, boolean redirect) throws XWikiException, IOException + { XWikiContext context = xwikiContextProvider.get(); XWikiAttachment attachment = adoc.addAttachment(name, data, context); @@ -1210,14 +1220,15 @@ private void saveFileToXWiki(Drive driveService, XWikiDocument adoc, * @since 3.0 */ @Unstable - public GoogleAppsManager.GoogleDocMetadata getGoogleDocument(String pageName, String fileName) - throws XWikiException { + public DriveDocMetadata getGoogleDocument(String pageName, String fileName) + throws XWikiException + { XWikiDocument adoc = getXWiki().getDocument(documentResolver.resolve(pageName), xwikiContextProvider.get()); BaseObject object = adoc.getXObject(getSyncDocClassReference(), FILENAME, fileName, false); if (object == null) { return null; } else { - GoogleAppsManager.GoogleDocMetadata gdm = new GoogleAppsManager.GoogleDocMetadata(); + DriveDocMetadata gdm = new DriveDocMetadata(); gdm.id = object.getStringValue(ID); gdm.editLink = object.getStringValue(EDITLINK); gdm.exportLink = object.getStringValue(EXPORTLINK); @@ -1229,61 +1240,67 @@ public GoogleAppsManager.GoogleDocMetadata getGoogleDocument(String pageName, St * Inserts the current information on the document to be embedded. * * @param docId the identifier of the Google Docs document to be embedded - * @param doc the XWiki document where to attach the embedding - * @param obj the XWiki object where this embedding is to be updated (or null if it is to be created) - * @param nb the number of the embedding across all the page's embeddings + * @param doc the XWiki document where to attach the embedding + * @param obj the XWiki object where this embedding is to be updated (or null if it is to be created) + * @param nb the number of the embedding across all the page's embeddings * @return the created or actualized document - * @throws IOException If the communication with Google went wrong + * @throws IOException If the communication with Google went wrong * @throws XWikiException If something at the XWiki side went wrong (e.g. saving) */ @Unstable - public BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, BaseObject obj, int nb) throws IOException, XWikiException { + public BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, BaseObject obj, int nb) + throws IOException, XWikiException + { Drive drive = getDriveService(); XWikiContext context = xwikiContextProvider.get(); String user = drive.about().get().execute().getUser().getEmailAddress(); File docData = drive.files().get(docId).execute(); String embedLink = docData.getEmbedLink(); - if (embedLink==null) + if (embedLink == null) { embedLink = docData.getAlternateLink(); + } - if (obj==null) { + if (obj == null) { obj = doc.newXObject(getSyncDocClassReference(), context); obj.setNumber(nb); } obj.setStringValue("id", docId); - if (embedLink!=null) + if (embedLink != null) { obj.setStringValue("embedLink", embedLink); + } obj.setStringValue("editLink", docData.getAlternateLink()); obj.setStringValue("version", docData.getVersion().toString()); - obj.setStringValue("fileName", docData.getOriginalFilename() != null ? docData.getOriginalFilename() : docData.getTitle()); + obj.setStringValue("fileName", + docData.getOriginalFilename() != null ? docData.getOriginalFilename() : docData.getTitle()); obj.setStringValue("user", user); getXWiki().saveDocument(doc, "Inserting Google Document", context); return obj; } - /** * Reads the extension and document name. * * @param docName the raw docName - * @param elink the link where to read the extension name + * @param elink the link where to read the extension name * @return an array with extension and simplified document name * @since 3.0 */ @Unstable - public String[] getExportLink(String docName, String elink) { + public String[] getExportLink(String docName, String elink) + { int index = elink.indexOf(EXPORTFORMATEQ) + 13; String extension = elink.substring(index); String newDocName = docName .replaceAll("\\.(doc|docx|odt|xls|xlsx|ods|pptx|svg|png|jpeg|pdf|)$", ""); newDocName += '.' + extension; - return new String[] {extension, newDocName}; + return new String[]{ extension, newDocName }; } - private String findExportLink(String name, File entry) { + private String findExportLink(String name, File entry) + { String exportLink; String lastLink = ""; - for (Map.Entry elink: entry.getExportLinks().entrySet()) { + for (Map.Entry elink : entry.getExportLinks().entrySet()) { log.info("Checking link: " + elink); lastLink = elink.getValue(); int index = lastLink.indexOf(EXPORTFORMATEQ) + 13; @@ -1302,20 +1319,20 @@ private String findExportLink(String name, File entry) { return exportLink; } - /** * Saves the attachment stored in XWiki to the Google drive of the user attached to the current logged-in user. * * @param page the XWiki page name * @param name the attachment name - * @return a record with the keys fileName, exportLink, version, editLink, embedLink, - * and google-user's email-address + * @return a record with the keys fileName, exportLink, version, editLink, embedLink, and google-user's + * email-address * @throws XWikiException if something went wrong at the XWiki side - * @throws IOException if something went wrong int he communication with Google drive. + * @throws IOException if something went wrong int he communication with Google drive. * @since 3.0 */ @Unstable - public Map saveAttachmentToGoogle(String page, String name) throws XWikiException, IOException { + public Map saveAttachmentToGoogle(String page, String name) throws XWikiException, IOException + { log.info("Starting saving attachment ${name} from page ${page}"); XWikiContext context = xwikiContextProvider.get(); XWikiDocument adoc = getXWiki().getDocument(documentResolver.resolve(page), context); @@ -1353,7 +1370,6 @@ public Map saveAttachmentToGoogle(String page, String name) thro getXWiki().saveDocument(adoc, UPDATECOMMENT, context); return r; - } else { log.info("File insert failed"); return null; @@ -1363,13 +1379,13 @@ public Map saveAttachmentToGoogle(String page, String name) thro /** * Reads the google user-info attached to the current user as stored in the request. * - * @return the google user-info with keys displayName, emails (array of type,value pairs), - * etag, id, image (map with keys isDefault and url), kind, language, - * name (map with keys familyName and givenName). + * @return the google user-info with keys displayName, emails (array of type,value pairs), etag, id, image (map with + * keys isDefault and url), kind, language, name (map with keys familyName and givenName). * @since 3.0 */ @Unstable - public Map getGoogleUser() { + public Map getGoogleUser() + { // e.g.: User: [displayName: name name, // emails:[[type:account, value:xxx@googlemail.com]], // etag:"k-5ZH5-al;sdsdkl;-sdsadsd", @@ -1379,5 +1395,4 @@ public Map getGoogleUser() { // name:[familyName:XXX, givenName:xxx]] return (Map) (xwikiContextProvider.get().getRequest().getAttribute(GOOGLEUSERATT)); } - } diff --git a/ui/src/main/resources/GoogleApps/DriveMacro.xml b/ui/src/main/resources/GoogleApps/DriveMacro.xml index d4471f6..d3a049b 100644 --- a/ui/src/main/resources/GoogleApps/DriveMacro.xml +++ b/ui/src/main/resources/GoogleApps/DriveMacro.xml @@ -291,7 +291,7 @@ def escapetool = new org.xwiki.velocity.tools.EscapeTool(); def force = false; def googleApps = services.googleApps -if(!googleApps.active || !googleApps.useDrive) { +if(!googleApps.active || !googleApps.driveEnabled) { println services.localization.render('googleapps.error.driveDisabled') } else { // adding stylesheet diff --git a/ui/src/main/resources/GoogleApps/EditInGoogleApps.xml b/ui/src/main/resources/GoogleApps/EditInGoogleApps.xml index 1d54116..72c9c5e 100644 --- a/ui/src/main/resources/GoogleApps/EditInGoogleApps.xml +++ b/ui/src/main/resources/GoogleApps/EditInGoogleApps.xml @@ -41,7 +41,7 @@ true {{velocity}} #set($googleApps = $services.googleApps) -#if($googleApps.active && $googleApps.useDrive) +#if($googleApps.active && $googleApps.driveEnabled) #set($gdoc = $googleApps.getGoogleDocument($request.page, $request.name)) #if($request.confirm) {{translation key='googleapps.editInGA.uploading'/}} diff --git a/ui/src/main/resources/GoogleApps/JSExtension.xml b/ui/src/main/resources/GoogleApps/JSExtension.xml index 85fc57e..eec8e68 100644 --- a/ui/src/main/resources/GoogleApps/JSExtension.xml +++ b/ui/src/main/resources/GoogleApps/JSExtension.xml @@ -120,7 +120,7 @@ #set($googleApps = $services.googleApps) - #if($googleApps.active && $googleApps.useDrive) + #if($googleApps.active && $googleApps.driveEnabled) var listener = function(event) { if (event.memo.id == 'Attachments') { var buttons = $$(".xwikibuttonlinks") From a5a69382ef5d8946f3b83605b3fd82bd967fb5ec Mon Sep 17 00:00:00 2001 From: Paul Libbrecht Date: Tue, 17 Dec 2019 20:03:41 +0100 Subject: [PATCH 04/17] Introducing GoogleAppsException and thus removing the API-level dependency on old-core. Paul --- .../apps/googleapps/GoogleAppsException.java | 92 +++ .../apps/googleapps/GoogleAppsManager.java | 63 +-- .../googleapps/GoogleAppsScriptService.java | 42 +- .../CookieAuthenticationPersistenceImpl.java | 4 +- .../internal/GoogleAppsAuthServiceImpl.java | 17 +- .../internal/GoogleAppsEventListener.java | 3 +- .../internal/GoogleAppsManagerImpl.java | 530 ++++++++++-------- ui/src/main/resources/GoogleApps/WebHome.xml | 2 + 8 files changed, 438 insertions(+), 315 deletions(-) create mode 100644 api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsException.java diff --git a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsException.java b/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsException.java new file mode 100644 index 0000000..3fb8621 --- /dev/null +++ b/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsException.java @@ -0,0 +1,92 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.xwiki.apps.googleapps; + +import java.io.IOException; + +import org.xwiki.stability.Unstable; + +import com.xpn.xwiki.XWikiException; + +/** + * Generic class to denote an exception condition within the GoogleApps code. Wraps XWikiException and IOException. + * + * @version $Id$ + * @since 3.0 + */ +public class GoogleAppsException extends RuntimeException +{ + + private static final long serialVersionUID = 3000; + /** + * @param msg Message to denote the error for programmers. + * @param wrapped Exception that has caused this one. + * + * @since 3.0 + */ + public GoogleAppsException(String msg, Exception wrapped) + { + super(msg, wrapped); + } + + /** + * + * @param msg Message to denote the error for programmers. + * + * @since 3.0 + */ + public GoogleAppsException(String msg) + { + super(msg); + } + + /** + * + * @param wrapped Exception that has caused this one. + * + * @since 3.0 + */ + public GoogleAppsException(Exception wrapped) + { + super(wrapped); + } + + /** + * + * @return true if the wrapped exception of XWiki origin. + * @since 3.0 + */ + @Unstable + public boolean isWikiException() + { + return super.getCause() instanceof XWikiException; + } + + /** + * + * @return true if the wrapped exception of Google origin (for now: any IO-related exception). + * @since 3.0 + */ + @Unstable + public boolean isGoogleCommException() + { + return super.getCause() instanceof IOException; + } +} diff --git a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsManager.java b/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsManager.java index 0b7775c..14012d5 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsManager.java +++ b/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsManager.java @@ -19,7 +19,6 @@ */ package org.xwiki.apps.googleapps; -import java.io.IOException; import java.util.Date; import java.util.List; import java.util.Map; @@ -30,7 +29,6 @@ import com.google.api.client.auth.oauth2.Credential; import com.google.api.services.drive.model.File; import com.google.api.services.drive.model.FileList; -import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.objects.BaseObject; @@ -46,11 +44,11 @@ public interface GoogleAppsManager { /** * @return if the application is licensed and activated - * @throws XWikiException in case a context cannot be read from thread. + * @throws GoogleAppsException in case a context cannot be read from thread. * @since 3.0 */ @Unstable - boolean isActive() throws XWikiException; + boolean isActive() throws GoogleAppsException; /** * @return if the app is configured to use the Google Drive integration (default: yes). @@ -67,7 +65,7 @@ public interface GoogleAppsManager int getConfigCookiesTTL(); /** - * Reads the manifest to find when the JAR file was assembled by maven. + * Finds when the JAR file was assembled by maven. * * @return the build date. * @since 3.0 @@ -75,28 +73,35 @@ public interface GoogleAppsManager @Unstable Date getBuildTime(); + /** + * Finds the version of the JAR file. + * + * @return the build version. + * @since 3.0 + */ + @Unstable + String getBuildVersion(); + /** * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. * * @return found credential - * @throws XWikiException if the interaction with xwiki failed - * @throws IOException if a communication problem to Google services occured + * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable - Credential authorize() throws XWikiException, IOException; + Credential authorize() throws GoogleAppsException; /** * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. * * @param redirect If a redirect can be done * @return found credential - * @throws XWikiException if the interaction with xwiki failed - * @throws IOException if a communication problem to Google services occured + * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable - Credential authorize(boolean redirect) throws XWikiException, IOException; + Credential authorize(boolean redirect) throws GoogleAppsException; /** * Performs the necessary communication with Google-Services to fetch identity and update the XWiki-user object or @@ -112,12 +117,11 @@ public interface GoogleAppsManager * Get the list of all documents in the user's associated account. * * @return A list of max 10 documents. - * @throws XWikiException if an authorization process failed. - * @throws IOException if a communication process to Google services occurred. + * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable - List getDocumentList() throws XWikiException, IOException; + List getDocumentList() throws GoogleAppsException; /** * Fetches a list of Google Drive document matching a substring query in the filename. @@ -125,12 +129,11 @@ public interface GoogleAppsManager * @param query the expected query (e.g. fullText contains winter ski) * @param nbResults max number of results * @return The list of files at Google Drive. - * @throws XWikiException if an XWiki issue occurs - * @throws IOException if an error interacting with Google services occurred + * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable - List listDriveDocumentsWithTypes(String query, int nbResults) throws XWikiException, IOException; + List listDriveDocumentsWithTypes(String query, int nbResults) throws GoogleAppsException; /** * Fetches a list of Google Drive document matching a given query. @@ -138,12 +141,11 @@ public interface GoogleAppsManager * @param query the expected filename substring * @param nbResults max number of results * @return The list of files at Google Drive. - * @throws XWikiException if an XWiki issue occurs - * @throws IOException if an error interacting with Google services occurred + * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable - FileList listDocuments(String query, int nbResults) throws XWikiException, IOException; + FileList listDocuments(String query, int nbResults) throws GoogleAppsException; /** * Fetches the google-drive document's representation and stores it as attachment. @@ -153,12 +155,11 @@ public interface GoogleAppsManager * @param id store object attached to this attachment using this id (for later sync) * @param url fetch from this URL * @return true if successful - * @throws XWikiException if an issue occurred in XWiki - * @throws IOException if an issue occurred in the communication with teh Google services + * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable - boolean retrieveFileFromGoogle(String page, String name, String id, String url) throws XWikiException, IOException; + boolean retrieveFileFromGoogle(String page, String name, String id, String url) throws GoogleAppsException; /** * Extracts metadata about the Google Drive document corresponding to the named attachment. @@ -166,11 +167,11 @@ public interface GoogleAppsManager * @param pageName The XWiki page where the attachment is * @param fileName The filename of the attachment * @return information about the corresponding Google Drive document - * @throws XWikiException if something happened at XWiki side + * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable - DriveDocMetadata getGoogleDocument(String pageName, String fileName) throws XWikiException; + DriveDocMetadata getGoogleDocument(String pageName, String fileName) throws GoogleAppsException; /** * Reads the extension and document name. @@ -191,12 +192,11 @@ public interface GoogleAppsManager * @param obj the XWiki object where this embedding is to be updated (or null if it is to be created) * @param nb the number of the embedding across all the page's embeddings * @return the created or actualized document - * @throws IOException If the communication with Google went wrong - * @throws XWikiException If something at the XWiki side went wrong (e.g. saving) + * @throws GoogleAppsException if a communication problem with the other components occured */ @Unstable - public BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, BaseObject obj, int nb) - throws IOException, XWikiException; + BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, BaseObject obj, int nb) + throws GoogleAppsException; /** * Saves the attachment stored in XWiki to the Google drive of the user attached to the current logged-in user. @@ -205,12 +205,11 @@ public BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, Bas * @param name the attachment name * @return a record with the keys fileName, exportLink, version, editLink, embedLink, and google-user's * email-address - * @throws XWikiException if something went wrong at the XWiki side - * @throws IOException if something went wrong int he communication with Google drive. + * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable - Map saveAttachmentToGoogle(String page, String name) throws XWikiException, IOException; + Map saveAttachmentToGoogle(String page, String name) throws GoogleAppsException; /** * Reads the google user-info attached to the current user as stored in the request. diff --git a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsScriptService.java b/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsScriptService.java index 5d76125..b78ff05 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsScriptService.java +++ b/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsScriptService.java @@ -19,7 +19,6 @@ */ package org.xwiki.apps.googleapps; -import java.io.IOException; import java.util.Date; import java.util.List; import java.util.Map; @@ -80,7 +79,7 @@ public boolean isDriveEnabled() } /** - * Reads the manifest to find when the JAR file was assembled by maven. + * Finds when the JAR file was assembled by maven. * * @return the build date. * @since 3.0 @@ -91,16 +90,23 @@ public Date getBuildTime() return manager.getBuildTime(); } + /** + * Finds the version of the JAR file. + * + * @return the build version. + * @since 3.0 + */ + @Unstable + public String getBuildVersion() { return manager.getBuildVersion(); } + /** * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. * * @return found credential - * @throws XWikiException if the interaction with xwiki failed - * @throws IOException if a communication problem to Google services occured * @since 3.0 */ @Unstable - public Credential authorize() throws XWikiException, IOException + public Credential authorize() { return manager.authorize(); } @@ -110,12 +116,10 @@ public Credential authorize() throws XWikiException, IOException * * @param redirect If a redirect can be done * @return found credential - * @throws XWikiException if the interaction with xwiki failed - * @throws IOException if a communication problem to Google services occured * @since 3.0 */ @Unstable - public Credential authorize(boolean redirect) throws XWikiException, IOException + public Credential authorize(boolean redirect) { return manager.authorize(redirect); } @@ -137,12 +141,10 @@ public String updateUser() * Get the list of all documents in the user's associated account. * * @return A list of max 10 documents. - * @throws XWikiException if an authorization process failed. - * @throws IOException if a communication process to Google services occurred. * @since 3.0 */ @Unstable - public List getDocumentList() throws XWikiException, IOException + public List getDocumentList() { return manager.getDocumentList(); } @@ -153,12 +155,10 @@ public List getDocumentList() throws XWikiException, IOException * @param query the expected query (e.g. fullText contains winter ski) * @param nbResults max number of results * @return The list of files at Google Drive. - * @throws XWikiException if an XWiki issue occurs - * @throws IOException if an error interacting with Google services occurred * @since 3.0 */ @Unstable - public List listDriveDocumentsWithTypes(String query, int nbResults) throws XWikiException, IOException + public List listDriveDocumentsWithTypes(String query, int nbResults) { return manager.listDriveDocumentsWithTypes(query, nbResults); } @@ -169,12 +169,10 @@ public List listDriveDocumentsWithTypes(String query, int nbResults) throw * @param query the expected filename substring * @param nbResults max number of results * @return The list of files at Google Drive. - * @throws XWikiException if an XWiki issue occurs - * @throws IOException if an error interacting with Google services occurred * @since 3.0 */ @Unstable - public FileList listDocuments(String query, int nbResults) throws XWikiException, IOException + public FileList listDocuments(String query, int nbResults) { return manager.listDocuments(query, nbResults); } @@ -187,12 +185,9 @@ public FileList listDocuments(String query, int nbResults) throws XWikiException * @param obj the XWiki object where this embedding is to be updated (or null if it is to be created) * @param nb the number of the embedding across all the page's embeddings * @return the created or actualized document - * @throws IOException If the communication with Google went wrong - * @throws XWikiException If something at the XWiki side went wrong (e.g. saving) */ @Unstable public Object createOrUpdateEmbedObject(String docId, Document doc, Object obj, int nb) - throws XWikiException, IOException { return new Object(manager.createOrUpdateEmbedObject(docId, doc.getDocument(), obj == null ? null : obj.getXWikiObject(), nb), @@ -207,13 +202,10 @@ public Object createOrUpdateEmbedObject(String docId, Document doc, Object obj, * @param id store object attached to this attachment using this id (for later sync) * @param url fetch from this URL * @return true if successful - * @throws XWikiException if an issue occurred in XWiki - * @throws IOException if an issue occurred in the communication with teh Google services * @since 3.0 */ @Unstable public boolean retrieveFileFromGoogle(String page, String name, String id, String url) - throws XWikiException, IOException { return manager.retrieveFileFromGoogle(page, name, id, url); } @@ -229,7 +221,6 @@ public boolean retrieveFileFromGoogle(String page, String name, String id, Strin */ @Unstable public DriveDocMetadata getGoogleDocument(String pageName, String fileName) - throws XWikiException { return manager.getGoogleDocument(pageName, fileName); } @@ -255,13 +246,10 @@ public String[] getExportLink(String docName, String elink) * @param name the attachment name * @return a record with the keys fileName, exportLink, version, editLink, embedLink, and google-user's * email-address - * @throws XWikiException if something went wrong at the XWiki side - * @throws IOException if something went wrong int he communication with Google drive. * @since 3.0 */ @Unstable public Map saveAttachmentToGoogle(String page, String name) - throws XWikiException, IOException { return manager.saveAttachmentToGoogle(page, name); } diff --git a/api/src/main/java/org/xwiki/apps/googleapps/internal/CookieAuthenticationPersistenceImpl.java b/api/src/main/java/org/xwiki/apps/googleapps/internal/CookieAuthenticationPersistenceImpl.java index 08ca7a5..539bb6d 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/internal/CookieAuthenticationPersistenceImpl.java +++ b/api/src/main/java/org/xwiki/apps/googleapps/internal/CookieAuthenticationPersistenceImpl.java @@ -209,8 +209,8 @@ private String encryptText(String text) return encryptedText; } catch (Exception e) { logger.error("Failed to encrypt text", e); + return null; } - return null; } private String decryptText(String text) @@ -224,8 +224,8 @@ private String decryptText(String text) return decryptedText; } catch (Exception e) { logger.error("Failed to decrypt text", e); + return null; } - return null; } /** diff --git a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsAuthServiceImpl.java b/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsAuthServiceImpl.java index a2c77c8..040846a 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsAuthServiceImpl.java +++ b/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsAuthServiceImpl.java @@ -32,6 +32,7 @@ import org.slf4j.Logger; import org.xwiki.apps.googleapps.CookieAuthenticationPersistence; import org.xwiki.apps.googleapps.GoogleAppsAuthService; +import org.xwiki.apps.googleapps.GoogleAppsManager; import org.xwiki.component.annotation.Component; import org.xwiki.component.annotation.InstantiationStrategy; import org.xwiki.component.descriptor.ComponentInstantiationStrategy; @@ -68,17 +69,13 @@ public class GoogleAppsAuthServiceImpl extends XWikiAuthServiceImpl @Inject private ComponentManager componentManager; - private GoogleAppsManagerImpl googleAppsManager; + @Inject + private Provider googleAppsManagerProvider; @Inject @Named("xwikicfg") private Provider xwikicfgProvider; - void setGoogleAppsManager(GoogleAppsManagerImpl m) - { - this.googleAppsManager = m; - } - /** * Evaluates if the user can be authenticated based on request info such as cookies. * @@ -129,13 +126,14 @@ public XWikiUser checkAuth(String username, String password, public void showLogin(XWikiContext context) throws XWikiException { log.info("GoogleApps authentificator - showLogin"); - if (!googleAppsManager.isActive(context)) { + if (!googleAppsManagerProvider.get().isActive()) { return; } boolean redirected = false; try { String url = context.getWiki().getExternalURL("GoogleApps.Login", "view", context); - if (googleAppsManager.useCookies() && googleAppsManager.skipLoginPage()) { + GoogleAppsManagerImpl manager = (GoogleAppsManagerImpl) googleAppsManagerProvider.get(); + if (manager.useCookies() && manager.skipLoginPage()) { log.info("skip the login page "); XWikiRequest request = context.getRequest(); CookieAuthenticationPersistence cookieTools = @@ -194,7 +192,8 @@ public Principal authenticate(String username, String password, XWikiContext con log.info("GoogleApps authentificator - authenticate"); // case of a too early call or deactivated... can only count on local users - if (googleAppsManager == null || !googleAppsManager.isActive(context)) { + GoogleAppsManagerImpl googleAppsManager = (GoogleAppsManagerImpl) googleAppsManagerProvider.get(); + if (googleAppsManager == null || !googleAppsManager.isActive()) { return super.authenticate(username, password, context); } diff --git a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsEventListener.java b/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsEventListener.java index 424f046..3a8af54 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsEventListener.java +++ b/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsEventListener.java @@ -55,7 +55,8 @@ public List getEvents() @Override public void onEvent(Event event, Object source, Object data) { - boolean applicationStarted = false, configChanged = false; + boolean applicationStarted = false; + boolean configChanged = false; if (event instanceof ApplicationReadyEvent) { applicationStarted = true; } diff --git a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsManagerImpl.java b/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsManagerImpl.java index c9f7298..0328a72 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsManagerImpl.java +++ b/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsManagerImpl.java @@ -50,6 +50,7 @@ import org.xwiki.apps.googleapps.CookieAuthenticationPersistence; import org.xwiki.apps.googleapps.DriveDocMetadata; import org.xwiki.apps.googleapps.GoogleAppsAuthService; +import org.xwiki.apps.googleapps.GoogleAppsException; import org.xwiki.apps.googleapps.GoogleAppsManager; import org.xwiki.component.annotation.Component; import org.xwiki.component.manager.ComponentLookupException; @@ -139,17 +140,14 @@ public void initialize() throws InitializationException XWikiContext context = xwikiContextProvider.get(); XWiki xwiki = context.getWiki(); - if (context != null) { - readConfigDoc(context); - } + readConfigDoc(context); - if (xwiki != null && context != null) { + if (xwiki != null) { log.info("Initting authService."); // We do not verify with the context if the plugin is active and if the license is active // this will be done by the GoogleAppsAuthService and UI pages later on, when it is called within a request try { authService = componentManager.getInstance(GoogleAppsAuthService.class); - authService.setGoogleAppsManager(this); xwiki.setAuthService(authService); log.info("Succeeded initting authService,"); } catch (ComponentLookupException e) { @@ -301,7 +299,7 @@ public boolean isActive() * @since 3.0 */ @Unstable - public boolean isActive(XWikiContext context) + boolean isActive(XWikiContext context) { log.info("Is active " + this.toString() + " with configClient non-null? " + (configClientId != null)); if (configActiveFlag == null || configClientId == null || configClientId.length() == 0) { @@ -421,6 +419,36 @@ public Date getBuildTime() } } + /** + * Reads the manifest to find the version of the JAR that was built. + * + * @return the build version + * @since 3.0 + */ + @Unstable + public String getBuildVersion() + { + try { + Class clazz = getClass(); + String className = clazz.getSimpleName() + + ".class"; + String classPath = clazz.getResource(className).toString(); + if(classPath!=null) { + String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) + + "/META-INF/MANIFEST.MF"; + Manifest manifest = new Manifest(new URL(manifestPath).openStream()); + Attributes attr = manifest.getMainAttributes(); + return attr.getValue("Specification-Version"); + } else { + return null; + } + } catch (IOException e) { + String msg = "Can't read build version."; + log.warn(msg, e); + throw new RuntimeException(msg, e); + } + } + // from ActiveDirectorySetupListener /** @@ -466,12 +494,16 @@ public boolean isDriveEnabled() // ----------------------------- Google Apps Tool (mostly request specific) ----------------------------------- - private String getOAuthUrl() throws XWikiException + private String getOAuthUrl() { - XWikiContext context = xwikiContextProvider.get(); - DocumentReference oauthReference = new DocumentReference(context.getWikiId(), - SPACENAME, OAUTH); - return getXWiki().getDocument(oauthReference, context).getExternalURL(VIEWACTION, context); + try { + XWikiContext context = xwikiContextProvider.get(); + DocumentReference oauthReference = new DocumentReference(context.getWikiId(), + SPACENAME, OAUTH); + return getXWiki().getDocument(oauthReference, context).getExternalURL(VIEWACTION, context); + } catch (XWikiException e) { + throw new GoogleAppsException("Trouble at getting OAuth URL", e); + } } private DocumentReference getXWikiUserClassRef() @@ -532,9 +564,9 @@ DocumentReference getConfigDocRef() * Build flow and trigger user authorization request. * * @return the configured flow - * @throws IOException in case something can't be built + * @throws GoogleAppsException in case something can't be built */ - private GoogleAuthorizationCodeFlow getFlow() throws IOException + private GoogleAuthorizationCodeFlow getFlow() { try { if (dsFactory == null) { @@ -560,7 +592,7 @@ private GoogleAuthorizationCodeFlow getFlow() throws IOException .build(); } catch (Exception e) { e.printStackTrace(); - throw new IOException("Issue at building Google Authorization Flow.", e); + throw new GoogleAppsException("Issue at building Google Authorization Flow.", e); } } @@ -581,9 +613,7 @@ private Credential exchangeCode(String authorizationCode) log.info("Token: " + tokenResponse); return flow.createAndStoreCredential(tokenResponse, getCurrentXWikiUserName()); } catch (Exception ex) { - log.warn("An error occurred: ", ex); - ex.printStackTrace(); - return null; + throw new GoogleAppsException("Trouble at exchanging authorization code", ex); } } @@ -599,7 +629,7 @@ private Map getCredentialStore() return store; } - private void storeCredentials(String userId, Credential credentials) throws XWikiException + private void storeCredentials(String userId, Credential credentials) { try { if (userId.contains(XWIKIGUEST)) { @@ -614,7 +644,7 @@ private void storeCredentials(String userId, Credential credentials) throws XWik getCredentialStore().put(userId, credentials); } catch (Exception e) { e.printStackTrace(); - throw new XWikiException("Issue at storing credential.", e); + throw new GoogleAppsException("Issue at storing credential.", e); } } @@ -638,71 +668,74 @@ private Credential getStoredCredentials(String userId) * * @param authorizationCode Authorization code to use to retrieve an access token. * @return OAuth 2.0 credentials instance containing an access and refresh token. - * @throws IOException Unable to load client_secret.json. + * @throws GoogleAppsException Unable to load client_secret.json. */ private Credential retrieveCredentials(String authorizationCode, boolean redirect) - throws XWikiException, IOException { - Credential credentials; - String user = getCurrentXWikiUserName(); - - if (authorizationCode != null && authorizationCode.length() > 0) { - log.debug("Trying to get credentials from authorization code: " + authorizationCode); - credentials = exchangeCode(authorizationCode); - if (credentials != null) { - String rtoken = credentials.getRefreshToken(); - if (rtoken != null) { - log.debug("Refresh token has been created: " + rtoken); - storeCredentials(user, credentials); - return credentials; - } else { - log.debug("Failure to create refresh token"); - storeCredentials(user, credentials); - return credentials; + try { + Credential credentials; + String user = getCurrentXWikiUserName(); + + if (authorizationCode != null && authorizationCode.length() > 0) { + log.debug("Trying to get credentials from authorization code: " + authorizationCode); + credentials = exchangeCode(authorizationCode); + if (credentials != null) { + String rtoken = credentials.getRefreshToken(); + if (rtoken != null) { + log.debug("Refresh token has been created: " + rtoken); + storeCredentials(user, credentials); + return credentials; + } else { + log.debug("Failure to create refresh token"); + storeCredentials(user, credentials); + return credentials; + } } } - } - log.debug("No credentials found. Checking stored credentials for user " + user); - credentials = getStoredCredentials(user); - if (credentials != null) { - log.debug("Retrieved stored credentials"); - return credentials; - } - log.debug("Could not find stored credentials"); + log.debug("No credentials found. Checking stored credentials for user " + user); + credentials = getStoredCredentials(user); + if (credentials != null) { + log.debug("Retrieved stored credentials"); + return credentials; + } + log.debug("Could not find stored credentials"); - log.debug("No credentials retrieved."); - // No refresh token has been retrieved. - if (redirect) { - log.debug("Redirecting to authorization URL."); - xwikiContextProvider.get().getResponse().sendRedirect(getAuthorizationURL()); + log.debug("No credentials retrieved."); + // No refresh token has been retrieved. + if (redirect) { + log.debug("Redirecting to authorization URL."); + xwikiContextProvider.get().getResponse().sendRedirect(getAuthorizationURL()); + } + return null; + } catch (Exception e) { + throw new GoogleAppsException("Trouble at retrieving credentials", e); } - return null; } - private String getAuthorizationURL() throws XWikiException, IOException + private String getAuthorizationURL() { - String state = ""; - XWikiContext context = xwikiContextProvider.get(); - XWikiRequest request = context.getRequest(); - DocumentReference ref = context.getDoc().getDocumentReference(); - if (!(OAUTH.equals(ref.getName()) && SPACENAME.equals(ref.getLastSpaceReference().getName()))) { - - String finalRedirect = new URL( - new URL(getXWiki().getExternalURL("GoogleApps.Login", VIEWACTION, context)), - context.getDoc().getURL(VIEWACTION, request.getQueryString(), context)).toExternalForm(); - state = Integer.toHexString(finalRedirect.hashCode()); - storedStates.put(state, finalRedirect); - } + try { + String state = ""; + XWikiContext context = xwikiContextProvider.get(); + XWikiRequest request = context.getRequest(); + DocumentReference ref = context.getDoc().getDocumentReference(); + if (!(OAUTH.equals(ref.getName()) && SPACENAME.equals(ref.getLastSpaceReference().getName()))) { + + String finalRedirect = new URL( + new URL(getXWiki().getExternalURL("GoogleApps.Login", VIEWACTION, context)), + context.getDoc().getURL(VIEWACTION, request.getQueryString(), context)).toExternalForm(); + state = Integer.toHexString(finalRedirect.hashCode()); + storedStates.put(state, finalRedirect); + } - GoogleAuthorizationCodeRequestUrl urlBuilder = getFlow() - .newAuthorizationUrl() - .setRedirectUri(getOAuthUrl()) - .setState(state).setClientId(configClientId) - .setAccessType("offline").setApprovalPrompt(AUTOAPPROVAL); - // Add user email to filter account if the user is logged with multiple account - if (useCookies) { - try { + GoogleAuthorizationCodeRequestUrl urlBuilder = getFlow() + .newAuthorizationUrl() + .setRedirectUri(getOAuthUrl()) + .setState(state).setClientId(configClientId) + .setAccessType("offline").setApprovalPrompt(AUTOAPPROVAL); + // Add user email to filter account if the user is logged with multiple account + if (useCookies) { CookieAuthenticationPersistence cookieTools = componentManager.getInstance(CookieAuthenticationPersistence.class); String userId = cookieTools.getUserId(); @@ -721,26 +754,23 @@ private String getAuthorizationURL() throws XWikiException, IOException urlBuilder = urlBuilder.set("login_hint", userEmail); } } - } catch (ComponentLookupException e) { - e.printStackTrace(); - throw new XWikiException("Issue at accessing CookieAuthenticationPersistance", e); } + String authurl = urlBuilder.build(); + log.debug("google authentication url : " + authurl); + return authurl; + } catch (Exception ex) { + throw new GoogleAppsException("trouble at getAuthorizationURL", ex); } - String authurl = urlBuilder.build(); - log.debug("google authentication url : " + authurl); - return authurl; } /** * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. * * @return found credential - * @throws XWikiException if the interaction with xwiki failed - * @throws IOException if a communication problem to Google services occured * @since 3.0 */ @Unstable - public Credential authorize() throws XWikiException, IOException + public Credential authorize() { return authorize(true); } @@ -750,28 +780,30 @@ public Credential authorize() throws XWikiException, IOException * * @param redirect If a redirect can be done * @return found credential - * @throws XWikiException if the interaction with xwiki failed - * @throws IOException if a communication problem to Google services occured * @since 3.0 */ @Unstable - public Credential authorize(boolean redirect) throws XWikiException, IOException + public Credential authorize(boolean redirect) { - log.info("In authorize"); - GoogleAuthorizationCodeFlow flow = getFlow(); // useless? - XWikiRequest request = xwikiContextProvider.get().getRequest(); - String state = request.getParameter("state"); - XWikiResponse response = xwikiContextProvider.get().getResponse(); - Credential creds = retrieveCredentials(request.getParameter("code"), redirect); - log.info("Got credentials: " + creds); - if (state != null && state.length() > 0) { - String url = storedStates.get(state); - if (url != null) { - log.info("Redirecting to final destination after authorization: " + url); - response.sendRedirect(new URL(new URL(request.getRequestURL().toString()), url).toExternalForm()); + try { + log.info("In authorize"); + GoogleAuthorizationCodeFlow flow = getFlow(); // useless? + XWikiRequest request = xwikiContextProvider.get().getRequest(); + String state = request.getParameter("state"); + XWikiResponse response = xwikiContextProvider.get().getResponse(); + Credential creds = retrieveCredentials(request.getParameter("code"), redirect); + log.info("Got credentials: " + creds); + if (state != null && state.length() > 0) { + String url = storedStates.get(state); + if (url != null) { + log.info("Redirecting to final destination after authorization: " + url); + response.sendRedirect(new URL(new URL(request.getRequestURL().toString()), url).toExternalForm()); + } } + return creds; + } catch (IOException e) { + throw new GoogleAppsException(e); } - return creds; } /** @@ -834,7 +866,7 @@ public String updateUser() try { // Force main wiki database to create the user as global context.setMainXWiki(WIKINAME); - if(usersEmailAddress == null) { + if (usersEmailAddress == null) { if (user.getEmailAddresses() != null && user.getEmailAddresses().size() > 0) { usersEmailAddress = user.getEmailAddresses().get(0).getValue(); } else { @@ -1020,9 +1052,8 @@ public String updateUser() * Builds and returns an authorized Drive client service. * * @return an authorized Drive client service - * @throws IOException if a communication error occurs */ - private Drive getDriveService() throws XWikiException, IOException + private Drive getDriveService() { Credential credential = authorize(); return new Drive.Builder( @@ -1035,9 +1066,8 @@ private Drive getDriveService() throws XWikiException, IOException * Build and return an authorized Drive client service. * * @return an authorized Drive client service - * @throws IOException if a communication error occurred */ - private DocsService getDocsService() throws XWikiException, IOException + private DocsService getDocsService() { Credential credential = authorize(); DocsService service = new DocsService(configAppName); @@ -1049,16 +1079,18 @@ private DocsService getDocsService() throws XWikiException, IOException * Get the list of all documents in the user's associated account. * * @return A list of max 10 documents. - * @throws XWikiException if an authorization process failed. - * @throws IOException if a communication process to Google services occurred. * @since 3.0 */ @Unstable - public List getDocumentList() throws XWikiException, IOException + public List getDocumentList() { - Drive drive = getDriveService(); - FileList result = drive.files().list().setMaxResults(10).execute(); - return result.getItems(); + try { + Drive drive = getDriveService(); + FileList result = drive.files().list().setMaxResults(10).execute(); + return result.getItems(); + } catch (IOException e) { + throw new GoogleAppsException(e); + } } /** @@ -1067,20 +1099,22 @@ public List getDocumentList() throws XWikiException, IOException * @param query the expected query (e.g. fullText contains winter ski) * @param nbResults max number of results * @return The list of files at Google Drive. - * @throws XWikiException if an XWiki issue occurs - * @throws IOException if an error interacting with Google services occurred * @since 3.0 */ @Unstable - public List listDriveDocumentsWithTypes(String query, int nbResults) throws XWikiException, IOException + public List listDriveDocumentsWithTypes(String query, int nbResults) { - Drive drive = getDriveService(); - Drive.Files.List req = drive.files().list() - .setQ(query) - .setFields("items(id,mimeType,title,exportLinks,selfLink,version,alternateLink)") - .setMaxResults(nbResults); - FileList result = req.execute(); - return result.getItems(); + try { + Drive drive = getDriveService(); + Drive.Files.List req = drive.files().list() + .setQ(query) + .setFields("items(id,mimeType,title,exportLinks,selfLink,version,alternateLink)") + .setMaxResults(nbResults); + FileList result = req.execute(); + return result.getItems(); + } catch (IOException e) { + throw new GoogleAppsException(e); + } } /** @@ -1089,17 +1123,19 @@ public List listDriveDocumentsWithTypes(String query, int nbResults) throw * @param query the expected filename substring * @param nbResults max number of results * @return The list of files at Google Drive. - * @throws XWikiException if an XWiki issue occurs - * @throws IOException if an error interacting with Google services occurred * @since 3.0 */ @Unstable - public FileList listDocuments(String query, int nbResults) throws XWikiException, IOException + public FileList listDocuments(String query, int nbResults) { - Drive drive = getDriveService(); - Drive.Files.List req = drive.files().list().setQ(query).setMaxResults(nbResults); - FileList result = req.execute(); - return result; + try { + Drive drive = getDriveService(); + Drive.Files.List req = drive.files().list().setQ(query).setMaxResults(nbResults); + FileList result = req.execute(); + return result; + } catch (IOException e) { + throw new GoogleAppsException(e); + } } /** @@ -1110,34 +1146,31 @@ public FileList listDocuments(String query, int nbResults) throws XWikiException * @param id store object attached to this attachment using this id (for later sync) * @param url fetch from this URL * @return true if successful - * @throws XWikiException if an issue occurred in XWiki - * @throws IOException if an issue occurred in the communication with teh Google services * @since 3.0 */ @Unstable public boolean retrieveFileFromGoogle(String page, String name, String id, String url) - throws XWikiException, IOException { return retrieveFileFromGoogle(getDocsService(), getDriveService(), page, name, id, url); } private boolean retrieveFileFromGoogle(DocsService docsService, Drive driveService, - String page, String name, String id, String url) throws XWikiException + String page, String name, String id, String url) { log.info("Retrieving " + name + " to page " + page + ": " + id + url); - XWikiDocument adoc = getXWiki().getDocument(documentResolver.resolve(page), xwikiContextProvider.get()); try { + XWikiDocument adoc = getXWiki().getDocument(documentResolver.resolve(page), xwikiContextProvider.get()); byte[] data = downloadFile(docsService, url); saveFileToXWiki(driveService, adoc, id, name, new ByteArrayInputStream(data), true); return true; } catch (Exception e) { log.info(e.getMessage(), e); - throw new XWikiException("Trouble at retrieving from Google.", e); + throw new GoogleAppsException("Trouble at retrieving from Google.", e); } } - private byte[] downloadFile(DocsService docsService, String exportUrl) throws XWikiException + private byte[] downloadFile(DocsService docsService, String exportUrl) { try { MediaContent mc = new MediaContent(); @@ -1164,49 +1197,53 @@ private byte[] downloadFile(DocsService docsService, String exportUrl) throws XW return outStream.toByteArray(); } catch (Exception e) { e.printStackTrace(); - throw new XWikiException("trouble at downloading document", e); + throw new GoogleAppsException("trouble at downloading document", e); } } private void saveFileToXWiki(Drive driveService, XWikiDocument adoc, - String id, String name, InputStream data, boolean redirect) throws XWikiException, IOException + String id, String name, InputStream data, boolean redirect) { - XWikiContext context = xwikiContextProvider.get(); - XWikiAttachment attachment = adoc.addAttachment(name, data, context); + try { + XWikiContext context = xwikiContextProvider.get(); + XWikiAttachment attachment = adoc.addAttachment(name, data, context); - // ready to save now - adoc.saveAttachmentContent(attachment, context); + // ready to save now + adoc.saveAttachmentContent(attachment, context); - String user = driveService.about().get().execute().getUser().getEmailAddress(); - File docData = driveService.files().get(id).execute(); - String embedLink = docData.getEmbedLink(); - if (embedLink == null) { - embedLink = docData.getAlternateLink(); - } + String user = driveService.about().get().execute().getUser().getEmailAddress(); + File docData = driveService.files().get(id).execute(); + String embedLink = docData.getEmbedLink(); + if (embedLink == null) { + embedLink = docData.getAlternateLink(); + } - getXWiki().saveDocument(adoc, "Updated Attachment From Google Apps", context); + getXWiki().saveDocument(adoc, "Updated Attachment From Google Apps", context); - BaseObject object = adoc.getXObject(getSyncDocClassReference(), FILENAME, name, false); - if (object == null) { - object = adoc.newXObject(getGoogleAuthClassReference(), context); - } - object.set(ID, id, context); - object.set(FILENAME, name, context); - if (context.getRequest().getParameter(URL) != null) { - object.set(EXPORTLINK, context.getRequest().getParameter(URL), context); - } - object.set(VERSION, docData.getVersion().toString(), context); - object.set(EDITLINK, docData.getAlternateLink(), context); - object.set(EMBEDLINK, embedLink, context); - if (object.getStringValue(USER) == null || object.getStringValue(USER).length() == 0) { - object.set(USER, user, context); - } - getXWiki().saveDocument(adoc, UPDATECOMMENT, context); - log.info("Document " + name + " has been saved to XWiki"); + BaseObject object = adoc.getXObject(getSyncDocClassReference(), FILENAME, name, false); + if (object == null) { + object = adoc.newXObject(getGoogleAuthClassReference(), context); + } + object.set(ID, id, context); + object.set(FILENAME, name, context); + if (context.getRequest().getParameter(URL) != null) { + object.set(EXPORTLINK, context.getRequest().getParameter(URL), context); + } + object.set(VERSION, docData.getVersion().toString(), context); + object.set(EDITLINK, docData.getAlternateLink(), context); + object.set(EMBEDLINK, embedLink, context); + if (object.getStringValue(USER) == null || object.getStringValue(USER).length() == 0) { + object.set(USER, user, context); + } + getXWiki().saveDocument(adoc, UPDATECOMMENT, context); + log.info("Document " + name + " has been saved to XWiki"); - if (redirect) { - String rurl = adoc.getURL(VIEWACTION, "#Attachments", context); - context.getResponse().sendRedirect(rurl); + if (redirect) { + String rurl = adoc.getURL(VIEWACTION, "#Attachments", context); + context.getResponse().sendRedirect(rurl); + } + } catch (Exception e) { + throw new GoogleAppsException("Trouble at saving GoogleDrive file to XWiki.", e); } } @@ -1216,23 +1253,25 @@ private void saveFileToXWiki(Drive driveService, XWikiDocument adoc, * @param pageName The XWiki page where the attachment is * @param fileName The filename of the attachment * @return information about the corresponding Google Drive document - * @throws XWikiException if something happened at XWiki side * @since 3.0 */ @Unstable public DriveDocMetadata getGoogleDocument(String pageName, String fileName) - throws XWikiException { - XWikiDocument adoc = getXWiki().getDocument(documentResolver.resolve(pageName), xwikiContextProvider.get()); - BaseObject object = adoc.getXObject(getSyncDocClassReference(), FILENAME, fileName, false); - if (object == null) { - return null; - } else { - DriveDocMetadata gdm = new DriveDocMetadata(); - gdm.id = object.getStringValue(ID); - gdm.editLink = object.getStringValue(EDITLINK); - gdm.exportLink = object.getStringValue(EXPORTLINK); - return gdm; + try { + XWikiDocument adoc = getXWiki().getDocument(documentResolver.resolve(pageName), xwikiContextProvider.get()); + BaseObject object = adoc.getXObject(getSyncDocClassReference(), FILENAME, fileName, false); + if (object == null) { + return null; + } else { + DriveDocMetadata gdm = new DriveDocMetadata(); + gdm.id = object.getStringValue(ID); + gdm.editLink = object.getStringValue(EDITLINK); + gdm.exportLink = object.getStringValue(EXPORTLINK); + return gdm; + } + } catch (XWikiException e) { + throw new GoogleAppsException("Can't get Google-Document inside XWiki.", e); } } @@ -1244,37 +1283,38 @@ public DriveDocMetadata getGoogleDocument(String pageName, String fileName) * @param obj the XWiki object where this embedding is to be updated (or null if it is to be created) * @param nb the number of the embedding across all the page's embeddings * @return the created or actualized document - * @throws IOException If the communication with Google went wrong - * @throws XWikiException If something at the XWiki side went wrong (e.g. saving) */ @Unstable public BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, BaseObject obj, int nb) - throws IOException, XWikiException { - Drive drive = getDriveService(); - XWikiContext context = xwikiContextProvider.get(); - String user = drive.about().get().execute().getUser().getEmailAddress(); - File docData = drive.files().get(docId).execute(); - String embedLink = docData.getEmbedLink(); - if (embedLink == null) { - embedLink = docData.getAlternateLink(); - } + try { + Drive drive = getDriveService(); + XWikiContext context = xwikiContextProvider.get(); + String user = drive.about().get().execute().getUser().getEmailAddress(); + File docData = drive.files().get(docId).execute(); + String embedLink = docData.getEmbedLink(); + if (embedLink == null) { + embedLink = docData.getAlternateLink(); + } - if (obj == null) { - obj = doc.newXObject(getSyncDocClassReference(), context); - obj.setNumber(nb); - } - obj.setStringValue("id", docId); - if (embedLink != null) { - obj.setStringValue("embedLink", embedLink); + if (obj == null) { + obj = doc.newXObject(getSyncDocClassReference(), context); + obj.setNumber(nb); + } + obj.setStringValue("id", docId); + if (embedLink != null) { + obj.setStringValue("embedLink", embedLink); + } + obj.setStringValue("editLink", docData.getAlternateLink()); + obj.setStringValue("version", docData.getVersion().toString()); + obj.setStringValue("fileName", + docData.getOriginalFilename() != null ? docData.getOriginalFilename() : docData.getTitle()); + obj.setStringValue("user", user); + getXWiki().saveDocument(doc, "Inserting Google Document", context); + return obj; + } catch (Exception e) { + throw new GoogleAppsException("Can't create or update embedded document.", e); } - obj.setStringValue("editLink", docData.getAlternateLink()); - obj.setStringValue("version", docData.getVersion().toString()); - obj.setStringValue("fileName", - docData.getOriginalFilename() != null ? docData.getOriginalFilename() : docData.getTitle()); - obj.setStringValue("user", user); - getXWiki().saveDocument(doc, "Inserting Google Document", context); - return obj; } /** @@ -1326,53 +1366,55 @@ private String findExportLink(String name, File entry) * @param name the attachment name * @return a record with the keys fileName, exportLink, version, editLink, embedLink, and google-user's * email-address - * @throws XWikiException if something went wrong at the XWiki side - * @throws IOException if something went wrong int he communication with Google drive. * @since 3.0 */ @Unstable - public Map saveAttachmentToGoogle(String page, String name) throws XWikiException, IOException + public Map saveAttachmentToGoogle(String page, String name) { - log.info("Starting saving attachment ${name} from page ${page}"); - XWikiContext context = xwikiContextProvider.get(); - XWikiDocument adoc = getXWiki().getDocument(documentResolver.resolve(page), context); - XWikiAttachment attach = adoc.getAttachment(name); - String ctype = attach.getMimeType(); - - File file = new com.google.api.services.drive.model.File(); - file.setTitle(name); - file.setOriginalFilename(name); - InputStreamContent content = new InputStreamContent(ctype, attach.getContentInputStream(context)); - Drive drive = getDriveService(); - String user = drive.about().get().execute().getUser().getEmailAddress(); - Drive.Files.Insert insert = drive.files().insert(file, content); - insert.setConvert(true); - File docData = insert.execute(); - if (docData != null) { - log.info("File inserted " + docData); - String embedLink = docData.getEmbedLink(); - if (embedLink == null) { - embedLink = docData.getAlternateLink(); - } - - BaseObject object = adoc.newXObject(getSyncDocClassReference(), context); - Map r = new HashMap<>(); - object.set(ID, docData.getId(), context); - r.put(ID, docData.getId()); - object.set(FILENAME, name, context); - object.set(EXPORTLINK, findExportLink(name, docData), context); - r.put(EXPORTLINK, findExportLink(name, docData)); - object.set(VERSION, Long.toString(docData.getVersion()), context); - object.set(EDITLINK, docData.getAlternateLink(), context); - r.put(EDITLINK, docData.getAlternateLink()); - object.set(EMBEDLINK, embedLink, context); - object.set(USER, user, context); + try { + log.info("Starting saving attachment ${name} from page ${page}"); + XWikiContext context = xwikiContextProvider.get(); + XWikiDocument adoc = getXWiki().getDocument(documentResolver.resolve(page), context); + XWikiAttachment attach = adoc.getAttachment(name); + String ctype = attach.getMimeType(); + + File file = new File(); + file.setTitle(name); + file.setOriginalFilename(name); + InputStreamContent content = new InputStreamContent(ctype, attach.getContentInputStream(context)); + Drive drive = getDriveService(); + String user = drive.about().get().execute().getUser().getEmailAddress(); + Drive.Files.Insert insert = drive.files().insert(file, content); + insert.setConvert(true); + File docData = insert.execute(); + if (docData != null) { + log.info("File inserted " + docData); + String embedLink = docData.getEmbedLink(); + if (embedLink == null) { + embedLink = docData.getAlternateLink(); + } - getXWiki().saveDocument(adoc, UPDATECOMMENT, context); - return r; - } else { - log.info("File insert failed"); - return null; + BaseObject object = adoc.newXObject(getSyncDocClassReference(), context); + Map r = new HashMap<>(); + object.set(ID, docData.getId(), context); + r.put(ID, docData.getId()); + object.set(FILENAME, name, context); + object.set(EXPORTLINK, findExportLink(name, docData), context); + r.put(EXPORTLINK, findExportLink(name, docData)); + object.set(VERSION, Long.toString(docData.getVersion()), context); + object.set(EDITLINK, docData.getAlternateLink(), context); + r.put(EDITLINK, docData.getAlternateLink()); + object.set(EMBEDLINK, embedLink, context); + object.set(USER, user, context); + + getXWiki().saveDocument(adoc, UPDATECOMMENT, context); + return r; + } else { + log.info("File insert failed"); + return null; + } + } catch (Exception e) { + throw new GoogleAppsException("Couldn't save attachment to Google.", e); } } diff --git a/ui/src/main/resources/GoogleApps/WebHome.xml b/ui/src/main/resources/GoogleApps/WebHome.xml index ed0d3f7..3f24805 100644 --- a/ui/src/main/resources/GoogleApps/WebHome.xml +++ b/ui/src/main/resources/GoogleApps/WebHome.xml @@ -59,6 +59,8 @@ $services.localization.render('googleapps.webHome.4', ["[[",">>path:$url]] $services.localization.render('googleapps.webHome.5', ["[[",">>DriveMacroTest]]"]) #end #end + +[[GoogleApps Application version ${services.googleApps.buildVersion} built on ${services.googleApps.buildTime}>>https://store.xwiki.com/xwiki/bin/view/Extension/GoogleAppsIntegration]]. {{/velocity}} From 0f0139bd4210765092acd1663042cb2b67af085c Mon Sep 17 00:00:00 2001 From: Paul Libbrecht Date: Wed, 18 Dec 2019 11:28:11 +0100 Subject: [PATCH 05/17] Changing package to com.xwiki.googleapps. Paul --- .../googleapps/CookieAuthenticationPersistence.java | 2 +- .../xwiki}/googleapps/DriveDocMetadata.java | 2 +- .../xwiki}/googleapps/GoogleAppsAuthService.java | 2 +- .../xwiki}/googleapps/GoogleAppsException.java | 2 +- .../xwiki}/googleapps/GoogleAppsManager.java | 2 +- .../xwiki}/googleapps/GoogleAppsScriptService.java | 2 +- .../CookieAuthenticationPersistenceImpl.java | 6 +++--- .../internal/GoogleAppsAuthServiceImpl.java | 8 ++++---- .../googleapps/internal/GoogleAppsEventListener.java | 2 +- .../googleapps/internal/GoogleAppsManagerImpl.java | 12 ++++++------ api/src/main/resources/META-INF/components.txt | 8 ++++---- 11 files changed, 24 insertions(+), 24 deletions(-) rename api/src/main/java/{org/xwiki/apps => com/xwiki}/googleapps/CookieAuthenticationPersistence.java (97%) rename api/src/main/java/{org/xwiki/apps => com/xwiki}/googleapps/DriveDocMetadata.java (97%) rename api/src/main/java/{org/xwiki/apps => com/xwiki}/googleapps/GoogleAppsAuthService.java (97%) rename api/src/main/java/{org/xwiki/apps => com/xwiki}/googleapps/GoogleAppsException.java (98%) rename api/src/main/java/{org/xwiki/apps => com/xwiki}/googleapps/GoogleAppsManager.java (99%) rename api/src/main/java/{org/xwiki/apps => com/xwiki}/googleapps/GoogleAppsScriptService.java (99%) rename api/src/main/java/{org/xwiki/apps => com/xwiki}/googleapps/internal/CookieAuthenticationPersistenceImpl.java (98%) rename api/src/main/java/{org/xwiki/apps => com/xwiki}/googleapps/internal/GoogleAppsAuthServiceImpl.java (97%) rename api/src/main/java/{org/xwiki/apps => com/xwiki}/googleapps/internal/GoogleAppsEventListener.java (98%) rename api/src/main/java/{org/xwiki/apps => com/xwiki}/googleapps/internal/GoogleAppsManagerImpl.java (99%) diff --git a/api/src/main/java/org/xwiki/apps/googleapps/CookieAuthenticationPersistence.java b/api/src/main/java/com/xwiki/googleapps/CookieAuthenticationPersistence.java similarity index 97% rename from api/src/main/java/org/xwiki/apps/googleapps/CookieAuthenticationPersistence.java rename to api/src/main/java/com/xwiki/googleapps/CookieAuthenticationPersistence.java index 4a951f0..67d6997 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/CookieAuthenticationPersistence.java +++ b/api/src/main/java/com/xwiki/googleapps/CookieAuthenticationPersistence.java @@ -17,7 +17,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package org.xwiki.apps.googleapps; +package com.xwiki.googleapps; import org.xwiki.component.annotation.Role; diff --git a/api/src/main/java/org/xwiki/apps/googleapps/DriveDocMetadata.java b/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java similarity index 97% rename from api/src/main/java/org/xwiki/apps/googleapps/DriveDocMetadata.java rename to api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java index 1f30143..51129c4 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/DriveDocMetadata.java +++ b/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java @@ -17,7 +17,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package org.xwiki.apps.googleapps; +package com.xwiki.googleapps; import org.xwiki.stability.Unstable; diff --git a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsAuthService.java b/api/src/main/java/com/xwiki/googleapps/GoogleAppsAuthService.java similarity index 97% rename from api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsAuthService.java rename to api/src/main/java/com/xwiki/googleapps/GoogleAppsAuthService.java index 921c2dc..1e164c8 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsAuthService.java +++ b/api/src/main/java/com/xwiki/googleapps/GoogleAppsAuthService.java @@ -17,7 +17,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package org.xwiki.apps.googleapps; +package com.xwiki.googleapps; import org.xwiki.component.annotation.Role; diff --git a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsException.java b/api/src/main/java/com/xwiki/googleapps/GoogleAppsException.java similarity index 98% rename from api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsException.java rename to api/src/main/java/com/xwiki/googleapps/GoogleAppsException.java index 3fb8621..5561bee 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsException.java +++ b/api/src/main/java/com/xwiki/googleapps/GoogleAppsException.java @@ -17,7 +17,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package org.xwiki.apps.googleapps; +package com.xwiki.googleapps; import java.io.IOException; diff --git a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsManager.java b/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java similarity index 99% rename from api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsManager.java rename to api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java index 14012d5..2218050 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsManager.java +++ b/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java @@ -17,7 +17,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package org.xwiki.apps.googleapps; +package com.xwiki.googleapps; import java.util.Date; import java.util.List; diff --git a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsScriptService.java b/api/src/main/java/com/xwiki/googleapps/GoogleAppsScriptService.java similarity index 99% rename from api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsScriptService.java rename to api/src/main/java/com/xwiki/googleapps/GoogleAppsScriptService.java index b78ff05..44c5ca2 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/GoogleAppsScriptService.java +++ b/api/src/main/java/com/xwiki/googleapps/GoogleAppsScriptService.java @@ -17,7 +17,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package org.xwiki.apps.googleapps; +package com.xwiki.googleapps; import java.util.Date; import java.util.List; diff --git a/api/src/main/java/org/xwiki/apps/googleapps/internal/CookieAuthenticationPersistenceImpl.java b/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistenceImpl.java similarity index 98% rename from api/src/main/java/org/xwiki/apps/googleapps/internal/CookieAuthenticationPersistenceImpl.java rename to api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistenceImpl.java index 539bb6d..ac49c2d 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/internal/CookieAuthenticationPersistenceImpl.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistenceImpl.java @@ -17,7 +17,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package org.xwiki.apps.googleapps.internal; +package com.xwiki.googleapps.internal; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; @@ -34,8 +34,8 @@ import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; -import org.xwiki.apps.googleapps.CookieAuthenticationPersistence; -import org.xwiki.apps.googleapps.GoogleAppsManager; +import com.xwiki.googleapps.CookieAuthenticationPersistence; +import com.xwiki.googleapps.GoogleAppsManager; import org.xwiki.component.annotation.Component; import org.xwiki.component.annotation.InstantiationStrategy; import org.xwiki.component.descriptor.ComponentInstantiationStrategy; diff --git a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsAuthServiceImpl.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthServiceImpl.java similarity index 97% rename from api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsAuthServiceImpl.java rename to api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthServiceImpl.java index 040846a..a3e9a6b 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsAuthServiceImpl.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthServiceImpl.java @@ -17,7 +17,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package org.xwiki.apps.googleapps.internal; +package com.xwiki.googleapps.internal; import java.net.URLEncoder; import java.security.Principal; @@ -30,9 +30,9 @@ import org.securityfilter.realm.SimplePrincipal; import org.slf4j.Logger; -import org.xwiki.apps.googleapps.CookieAuthenticationPersistence; -import org.xwiki.apps.googleapps.GoogleAppsAuthService; -import org.xwiki.apps.googleapps.GoogleAppsManager; +import com.xwiki.googleapps.CookieAuthenticationPersistence; +import com.xwiki.googleapps.GoogleAppsAuthService; +import com.xwiki.googleapps.GoogleAppsManager; import org.xwiki.component.annotation.Component; import org.xwiki.component.annotation.InstantiationStrategy; import org.xwiki.component.descriptor.ComponentInstantiationStrategy; diff --git a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsEventListener.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java similarity index 98% rename from api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsEventListener.java rename to api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java index 3a8af54..3720b11 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsEventListener.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java @@ -17,7 +17,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package org.xwiki.apps.googleapps.internal; +package com.xwiki.googleapps.internal; import java.util.Arrays; import java.util.List; diff --git a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsManagerImpl.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java similarity index 99% rename from api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsManagerImpl.java rename to api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java index 0328a72..c5a5110 100644 --- a/api/src/main/java/org/xwiki/apps/googleapps/internal/GoogleAppsManagerImpl.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java @@ -17,7 +17,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package org.xwiki.apps.googleapps.internal; +package com.xwiki.googleapps.internal; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -47,11 +47,11 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.slf4j.Logger; -import org.xwiki.apps.googleapps.CookieAuthenticationPersistence; -import org.xwiki.apps.googleapps.DriveDocMetadata; -import org.xwiki.apps.googleapps.GoogleAppsAuthService; -import org.xwiki.apps.googleapps.GoogleAppsException; -import org.xwiki.apps.googleapps.GoogleAppsManager; +import com.xwiki.googleapps.CookieAuthenticationPersistence; +import com.xwiki.googleapps.DriveDocMetadata; +import com.xwiki.googleapps.GoogleAppsAuthService; +import com.xwiki.googleapps.GoogleAppsException; +import com.xwiki.googleapps.GoogleAppsManager; import org.xwiki.component.annotation.Component; import org.xwiki.component.manager.ComponentLookupException; import org.xwiki.component.manager.ComponentManager; diff --git a/api/src/main/resources/META-INF/components.txt b/api/src/main/resources/META-INF/components.txt index 7c75ce7..695843a 100644 --- a/api/src/main/resources/META-INF/components.txt +++ b/api/src/main/resources/META-INF/components.txt @@ -1,4 +1,4 @@ -org.xwiki.apps.googleapps.internal.GoogleAppsManagerImpl -org.xwiki.apps.googleapps.internal.CookieAuthenticationPersistenceImpl -org.xwiki.apps.googleapps.GoogleAppsScriptService -org.xwiki.apps.googleapps.internal.GoogleAppsAuthServiceImpl +com.xwiki.googleapps.internal.GoogleAppsManagerImpl +com.xwiki.googleapps.internal.CookieAuthenticationPersistenceImpl +com.xwiki.googleapps.internal.GoogleAppsAuthServiceImpl +com.xwiki.googleapps.GoogleAppsScriptService From cc89974e159141e7c1a3c8a1fb59a149a3e0f7ee Mon Sep 17 00:00:00 2001 From: Paul Libbrecht Date: Fri, 27 Dec 2019 23:26:23 +0100 Subject: [PATCH 06/17] Moved a few components to the internal package (and making their role a package-internal interface). A few style-enhancements too. paul --- .../xwiki/googleapps/DriveDocMetadata.java | 1 + .../xwiki/googleapps/GoogleAppsManager.java | 1 + .../googleapps/GoogleAppsScriptService.java | 1 + .../CookieAuthenticationPersistence.java | 4 +- .../CookieAuthenticationPersistenceImpl.java | 9 ++- .../{ => internal}/GoogleAppsAuthService.java | 4 +- .../internal/GoogleAppsAuthServiceImpl.java | 2 - .../internal/GoogleAppsEventListener.java | 49 +++++++++++----- .../internal/GoogleAppsManagerImpl.java | 57 +++++++++---------- 9 files changed, 76 insertions(+), 52 deletions(-) rename api/src/main/java/com/xwiki/googleapps/{ => internal}/CookieAuthenticationPersistence.java (94%) rename api/src/main/java/com/xwiki/googleapps/{ => internal}/GoogleAppsAuthService.java (92%) diff --git a/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java b/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java index 51129c4..412bb33 100644 --- a/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java +++ b/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java @@ -24,6 +24,7 @@ /** * Simple pojo for metadata about a doc in Google Drive. * + * @version $Id$ * @since 3.0 */ @Unstable diff --git a/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java b/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java index 2218050..740f733 100644 --- a/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java +++ b/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java @@ -193,6 +193,7 @@ public interface GoogleAppsManager * @param nb the number of the embedding across all the page's embeddings * @return the created or actualized document * @throws GoogleAppsException if a communication problem with the other components occured + * @since 3.0 */ @Unstable BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, BaseObject obj, int nb) diff --git a/api/src/main/java/com/xwiki/googleapps/GoogleAppsScriptService.java b/api/src/main/java/com/xwiki/googleapps/GoogleAppsScriptService.java index 44c5ca2..74d2307 100644 --- a/api/src/main/java/com/xwiki/googleapps/GoogleAppsScriptService.java +++ b/api/src/main/java/com/xwiki/googleapps/GoogleAppsScriptService.java @@ -185,6 +185,7 @@ public FileList listDocuments(String query, int nbResults) * @param obj the XWiki object where this embedding is to be updated (or null if it is to be created) * @param nb the number of the embedding across all the page's embeddings * @return the created or actualized document + * @since 3.0 */ @Unstable public Object createOrUpdateEmbedObject(String docId, Document doc, Object obj, int nb) diff --git a/api/src/main/java/com/xwiki/googleapps/CookieAuthenticationPersistence.java b/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistence.java similarity index 94% rename from api/src/main/java/com/xwiki/googleapps/CookieAuthenticationPersistence.java rename to api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistence.java index 67d6997..9220526 100644 --- a/api/src/main/java/com/xwiki/googleapps/CookieAuthenticationPersistence.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistence.java @@ -17,7 +17,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package com.xwiki.googleapps; +package com.xwiki.googleapps.internal; import org.xwiki.component.annotation.Role; @@ -28,7 +28,7 @@ * @since 3.0 */ @Role -public interface CookieAuthenticationPersistence +interface CookieAuthenticationPersistence { /** * Stores the user-id in an encryted fashion in the cookie. diff --git a/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistenceImpl.java b/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistenceImpl.java index ac49c2d..1378c46 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistenceImpl.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistenceImpl.java @@ -34,7 +34,6 @@ import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; -import com.xwiki.googleapps.CookieAuthenticationPersistence; import com.xwiki.googleapps.GoogleAppsManager; import org.xwiki.component.annotation.Component; import org.xwiki.component.annotation.InstantiationStrategy; @@ -49,7 +48,8 @@ /** * Tools to help storing and retrieving enriched information within cookies such as the linked Google user profile. *

- * Copied code from xwiki-authenticator-trusted https://github.com/xwiki-contrib/xwiki-authenticator-trusted/edit/master\ + * This code is inspired by from xwiki-authenticator-trusted + * https://github.com/xwiki-contrib/xwiki-authenticator-trusted/edit/master\ * /xwiki-authenticator-trusted-api/src/main/java/org/xwiki/contrib/authentication\ * /internal/CookieAuthenticationPersistenceStore.java. * @@ -108,6 +108,11 @@ public class CookieAuthenticationPersistenceImpl implements CookieAuthentication @Named("xwikicfg") private Provider xwikicfgProvider; + /** + * Loads the configuration. + * + * @throws InitializationException if the configuration fails loading. + */ public void initialize() throws InitializationException { cookiePfx = xwikicfgProvider.get().getProperty(COOKIE_PREFIX_PROPERTY, ""); diff --git a/api/src/main/java/com/xwiki/googleapps/GoogleAppsAuthService.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthService.java similarity index 92% rename from api/src/main/java/com/xwiki/googleapps/GoogleAppsAuthService.java rename to api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthService.java index 1e164c8..6f7041f 100644 --- a/api/src/main/java/com/xwiki/googleapps/GoogleAppsAuthService.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthService.java @@ -17,7 +17,7 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ -package com.xwiki.googleapps; +package com.xwiki.googleapps.internal; import org.xwiki.component.annotation.Role; @@ -30,6 +30,6 @@ * @since 3.0 */ @Role -public interface GoogleAppsAuthService extends XWikiAuthService +interface GoogleAppsAuthService extends XWikiAuthService { } diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthServiceImpl.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthServiceImpl.java index a3e9a6b..3800d7a 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthServiceImpl.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthServiceImpl.java @@ -30,8 +30,6 @@ import org.securityfilter.realm.SimplePrincipal; import org.slf4j.Logger; -import com.xwiki.googleapps.CookieAuthenticationPersistence; -import com.xwiki.googleapps.GoogleAppsAuthService; import com.xwiki.googleapps.GoogleAppsManager; import org.xwiki.component.annotation.Component; import org.xwiki.component.annotation.InstantiationStrategy; diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java index 3720b11..84f927f 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java @@ -19,9 +19,7 @@ */ package com.xwiki.googleapps.internal; -import java.util.Arrays; -import java.util.List; - +import com.xpn.xwiki.doc.XWikiDocument; import org.xwiki.bridge.event.ApplicationReadyEvent; import org.xwiki.bridge.event.DocumentUpdatedEvent; import org.xwiki.component.phase.InitializationException; @@ -29,32 +27,53 @@ import org.xwiki.observation.EventListener; import org.xwiki.observation.event.Event; -import com.xpn.xwiki.doc.XWikiDocument; +import java.util.Arrays; +import java.util.List; -public class GoogleAppsEventListener implements EventListener +/** + * Registered object to listen to document changes. + * + * @version $Id$ + * @since 3.0 + */ +class GoogleAppsEventListener implements EventListener { - void GoogleAppsEventListener(GoogleAppsManagerImpl manager) - { + private GoogleAppsManagerImpl manager; + + GoogleAppsEventListener(GoogleAppsManagerImpl manager) { this.manager = manager; } - GoogleAppsManagerImpl manager; - + /** + * The name of the event listener. + * + * @return googleapps.scriptservice. + */ @Override - public String getName() - { + public String getName() { return "googleapps.scriptservice"; } + /** + * The event-types listened to. + * + * @return ApplicationReadyEvent and DocumentUpdatedEvent + */ @Override - public List getEvents() - { + public List getEvents() { return Arrays.asList(new ApplicationReadyEvent(), new DocumentUpdatedEvent()); } + /** + * Triggers a configuration reload (if the configuration is changed or the app is started) or + * an initialization (if the app is started). + * + * @param event The event listened to. + * @param source The object sending the event. + * @param data Data about the event. + */ @Override - public void onEvent(Event event, Object source, Object data) - { + public void onEvent(Event event, Object source, Object data) { boolean applicationStarted = false; boolean configChanged = false; if (event instanceof ApplicationReadyEvent) { diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java index c5a5110..1abaf09 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java @@ -47,9 +47,7 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.slf4j.Logger; -import com.xwiki.googleapps.CookieAuthenticationPersistence; import com.xwiki.googleapps.DriveDocMetadata; -import com.xwiki.googleapps.GoogleAppsAuthService; import com.xwiki.googleapps.GoogleAppsException; import com.xwiki.googleapps.GoogleAppsManager; import org.xwiki.component.annotation.Component; @@ -358,9 +356,9 @@ public int getConfigCookiesTTL() return configCookiesTTL; } - void readConfigDoc(XWikiContext context) + void readConfigDoc(XWikiContext contextp) { - + XWikiContext context = contextp; if (context == null) { context = xwikiContextProvider.get(); } @@ -402,21 +400,11 @@ void readConfigDoc(XWikiContext context) @Unstable public Date getBuildTime() { - try { - Class clazz = getClass(); - String className = clazz.getSimpleName() - + ".class"; - String classPath = clazz.getResource(className).toString(); - String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) - + "/META-INF/MANIFEST.MF"; - Manifest manifest = new Manifest(new URL(manifestPath).openStream()); - Attributes attr = manifest.getMainAttributes(); - return new Date(Long.parseLong(attr.getValue("Bnd-LastModified"))); - } catch (IOException e) { - String msg = "Can't read build time."; - log.warn(msg, e); - throw new RuntimeException(msg, e); + Attributes attr = getManifestMainAttributes(); + if (attr == null) { + return null; } + return new Date(Long.parseLong(attr.getValue("Bnd-LastModified"))); } /** @@ -428,17 +416,25 @@ public Date getBuildTime() @Unstable public String getBuildVersion() { + Attributes attr = getManifestMainAttributes(); + if (attr == null) { + return null; + } + return attr.getValue("Specification-Version"); + } + + private Attributes getManifestMainAttributes() { try { Class clazz = getClass(); String className = clazz.getSimpleName() + ".class"; String classPath = clazz.getResource(className).toString(); - if(classPath!=null) { + if (classPath != null) { String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) + "/META-INF/MANIFEST.MF"; Manifest manifest = new Manifest(new URL(manifestPath).openStream()); Attributes attr = manifest.getMainAttributes(); - return attr.getValue("Specification-Version"); + return attr; } else { return null; } @@ -787,7 +783,8 @@ public Credential authorize(boolean redirect) { try { log.info("In authorize"); - GoogleAuthorizationCodeFlow flow = getFlow(); // useless? + // useless? + GoogleAuthorizationCodeFlow flow = getFlow(); XWikiRequest request = xwikiContextProvider.get().getRequest(); String state = request.getParameter("state"); XWikiResponse response = xwikiContextProvider.get().getResponse(); @@ -971,8 +968,8 @@ public String updateUser() { String imageUrl = user.getPhotos().get(0).getUrl(); imageUrl = imageUrl - + (imageUrl.contains("?") ? "&" : "?") - + "sz=256"; + + (imageUrl.contains("?") ? "&" : '?') + + "sz=512"; log.debug("Pulling avatar " + imageUrl); HttpGet httpget = new HttpGet(imageUrl); // TODO: add an if-modified-since @@ -1280,14 +1277,16 @@ public DriveDocMetadata getGoogleDocument(String pageName, String fileName) * * @param docId the identifier of the Google Docs document to be embedded * @param doc the XWiki document where to attach the embedding - * @param obj the XWiki object where this embedding is to be updated (or null if it is to be created) + * @param objp the XWiki object where this embedding is to be updated (or null if it is to be created) * @param nb the number of the embedding across all the page's embeddings * @return the created or actualized document + * @since 3.0 */ @Unstable - public BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, BaseObject obj, int nb) + public BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, BaseObject objp, int nb) { try { + BaseObject obj = objp; Drive drive = getDriveService(); XWikiContext context = xwikiContextProvider.get(); String user = drive.about().get().execute().getUser().getEmailAddress(); @@ -1305,11 +1304,11 @@ public BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, Bas if (embedLink != null) { obj.setStringValue("embedLink", embedLink); } - obj.setStringValue("editLink", docData.getAlternateLink()); - obj.setStringValue("version", docData.getVersion().toString()); - obj.setStringValue("fileName", + obj.setStringValue(EDITLINK, docData.getAlternateLink()); + obj.setStringValue(VERSION, docData.getVersion().toString()); + obj.setStringValue(FILENAME, docData.getOriginalFilename() != null ? docData.getOriginalFilename() : docData.getTitle()); - obj.setStringValue("user", user); + obj.setStringValue(USER, user); getXWiki().saveDocument(doc, "Inserting Google Document", context); return obj; } catch (Exception e) { From bda0e8ec5e77d5d5388505165ab25f6d9d508e3d Mon Sep 17 00:00:00 2001 From: Paul Libbrecht Date: Wed, 5 Feb 2020 21:56:09 +0000 Subject: [PATCH 07/17] Code-style automatic adjustments with correct (XWiki) style. Variable-names reformulation suggestions. paul --- .../xwiki/googleapps/GoogleAppsException.java | 13 +- .../xwiki/googleapps/GoogleAppsManager.java | 18 +- .../googleapps/GoogleAppsScriptService.java | 5 +- .../CookieAuthenticationPersistence.java | 12 +- .../CookieAuthenticationPersistenceImpl.java | 45 ++-- .../internal/GoogleAppsAuthServiceImpl.java | 6 +- .../internal/GoogleAppsEventListener.java | 23 +- .../internal/GoogleAppsManagerImpl.java | 206 ++++++++---------- 8 files changed, 158 insertions(+), 170 deletions(-) diff --git a/api/src/main/java/com/xwiki/googleapps/GoogleAppsException.java b/api/src/main/java/com/xwiki/googleapps/GoogleAppsException.java index 5561bee..86feb57 100644 --- a/api/src/main/java/com/xwiki/googleapps/GoogleAppsException.java +++ b/api/src/main/java/com/xwiki/googleapps/GoogleAppsException.java @@ -33,12 +33,11 @@ */ public class GoogleAppsException extends RuntimeException { - private static final long serialVersionUID = 3000; + /** - * @param msg Message to denote the error for programmers. + * @param msg Message to denote the error for programmers. * @param wrapped Exception that has caused this one. - * * @since 3.0 */ public GoogleAppsException(String msg, Exception wrapped) @@ -47,9 +46,7 @@ public GoogleAppsException(String msg, Exception wrapped) } /** - * * @param msg Message to denote the error for programmers. - * * @since 3.0 */ public GoogleAppsException(String msg) @@ -58,9 +55,7 @@ public GoogleAppsException(String msg) } /** - * * @param wrapped Exception that has caused this one. - * * @since 3.0 */ public GoogleAppsException(Exception wrapped) @@ -69,7 +64,6 @@ public GoogleAppsException(Exception wrapped) } /** - * * @return true if the wrapped exception of XWiki origin. * @since 3.0 */ @@ -80,12 +74,11 @@ public boolean isWikiException() } /** - * * @return true if the wrapped exception of Google origin (for now: any IO-related exception). * @since 3.0 */ @Unstable - public boolean isGoogleCommException() + public boolean isGoogleException() { return super.getCause() instanceof IOException; } diff --git a/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java b/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java index 740f733..f53557d 100644 --- a/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java +++ b/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java @@ -86,7 +86,7 @@ public interface GoogleAppsManager * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. * * @return found credential - * @throws GoogleAppsException if a communication problem with the other components occured + * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable @@ -97,7 +97,7 @@ public interface GoogleAppsManager * * @param redirect If a redirect can be done * @return found credential - * @throws GoogleAppsException if a communication problem with the other components occured + * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable @@ -117,7 +117,7 @@ public interface GoogleAppsManager * Get the list of all documents in the user's associated account. * * @return A list of max 10 documents. - * @throws GoogleAppsException if a communication problem with the other components occured + * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable @@ -129,7 +129,7 @@ public interface GoogleAppsManager * @param query the expected query (e.g. fullText contains winter ski) * @param nbResults max number of results * @return The list of files at Google Drive. - * @throws GoogleAppsException if a communication problem with the other components occured + * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable @@ -141,7 +141,7 @@ public interface GoogleAppsManager * @param query the expected filename substring * @param nbResults max number of results * @return The list of files at Google Drive. - * @throws GoogleAppsException if a communication problem with the other components occured + * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable @@ -155,7 +155,7 @@ public interface GoogleAppsManager * @param id store object attached to this attachment using this id (for later sync) * @param url fetch from this URL * @return true if successful - * @throws GoogleAppsException if a communication problem with the other components occured + * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable @@ -167,7 +167,7 @@ public interface GoogleAppsManager * @param pageName The XWiki page where the attachment is * @param fileName The filename of the attachment * @return information about the corresponding Google Drive document - * @throws GoogleAppsException if a communication problem with the other components occured + * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable @@ -192,7 +192,7 @@ public interface GoogleAppsManager * @param obj the XWiki object where this embedding is to be updated (or null if it is to be created) * @param nb the number of the embedding across all the page's embeddings * @return the created or actualized document - * @throws GoogleAppsException if a communication problem with the other components occured + * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable @@ -206,7 +206,7 @@ BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, BaseObject * @param name the attachment name * @return a record with the keys fileName, exportLink, version, editLink, embedLink, and google-user's * email-address - * @throws GoogleAppsException if a communication problem with the other components occured + * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable diff --git a/api/src/main/java/com/xwiki/googleapps/GoogleAppsScriptService.java b/api/src/main/java/com/xwiki/googleapps/GoogleAppsScriptService.java index 74d2307..b98af2f 100644 --- a/api/src/main/java/com/xwiki/googleapps/GoogleAppsScriptService.java +++ b/api/src/main/java/com/xwiki/googleapps/GoogleAppsScriptService.java @@ -97,7 +97,10 @@ public Date getBuildTime() * @since 3.0 */ @Unstable - public String getBuildVersion() { return manager.getBuildVersion(); } + public String getBuildVersion() + { + return manager.getBuildVersion(); + } /** * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. diff --git a/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistence.java b/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistence.java index 9220526..4f3e68b 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistence.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistence.java @@ -31,20 +31,20 @@ interface CookieAuthenticationPersistence { /** - * Stores the user-id in an encryted fashion in the cookie. + * Reads the user-id from the cookie. * - * @param userId the string to store + * @return the decrypted user-id * @since 3.0 */ - void setUserId(String userId); + String getUserId(); /** - * Reads the user-id from the cookie. + * Stores the user-id in an encryted fashion in the cookie. * - * @return the decrypted user-id + * @param userId the string to store * @since 3.0 */ - String getUserId(); + void setUserId(String userId); /** * Removes stored information from the cookie. diff --git a/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistenceImpl.java b/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistenceImpl.java index 1378c46..71eef39 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistenceImpl.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistenceImpl.java @@ -34,7 +34,9 @@ import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; + import com.xwiki.googleapps.GoogleAppsManager; + import org.xwiki.component.annotation.Component; import org.xwiki.component.annotation.InstantiationStrategy; import org.xwiki.component.descriptor.ComponentInstantiationStrategy; @@ -48,8 +50,7 @@ /** * Tools to help storing and retrieving enriched information within cookies such as the linked Google user profile. *

- * This code is inspired by from xwiki-authenticator-trusted - * https://github.com/xwiki-contrib/xwiki-authenticator-trusted/edit/master\ + * This code is inspired by from xwiki-authenticator-trusted https://github.com/xwiki-contrib/xwiki-authenticator-trusted/edit/master\ * /xwiki-authenticator-trusted-api/src/main/java/org/xwiki/contrib/authentication\ * /internal/CookieAuthenticationPersistenceStore.java. * @@ -92,7 +93,7 @@ public class CookieAuthenticationPersistenceImpl implements CookieAuthentication @Inject private GoogleAppsManager manager; - private String cookiePfx; + private String cookiePrefix; private String cookiePath; @@ -115,7 +116,7 @@ public class CookieAuthenticationPersistenceImpl implements CookieAuthentication */ public void initialize() throws InitializationException { - cookiePfx = xwikicfgProvider.get().getProperty(COOKIE_PREFIX_PROPERTY, ""); + cookiePrefix = xwikicfgProvider.get().getProperty(COOKIE_PREFIX_PROPERTY, ""); cookiePath = xwikicfgProvider.get().getProperty(COOKIE_PATH_PROPERTY, "/"); String[] cdlist = StringUtils.split( @@ -151,6 +152,23 @@ public void clear() this.setUserId(this.getUserId()); } + /** + * Retrieving the login read from the cookie. + * + * @return the login name found, or null. + * @since 3.0 + */ + @Unstable + public String getUserId() + { + logger.info("retrieve cookie " + cookiePrefix + AUTHENTICATION_COOKIE); + String cookie = getCookieValue(cookiePrefix + AUTHENTICATION_COOKIE); + if (cookie != null) { + return decryptText(cookie); + } + return null; + } + /** * Store the user-information within the cookie. * @@ -160,7 +178,7 @@ public void clear() @Unstable public void setUserId(String userUid) { - Cookie cookie = new Cookie(cookiePfx + AUTHENTICATION_COOKIE, encryptText(userUid)); + Cookie cookie = new Cookie(cookiePrefix + AUTHENTICATION_COOKIE, encryptText(userUid)); cookie.setMaxAge(cookieMaxAge); cookie.setPath(cookiePath); String cookieDomain = getCookieDomain(); @@ -173,23 +191,6 @@ public void setUserId(String userUid) contextProvider.get().getResponse().addCookie(cookie); } - /** - * Retrieving the login read from the cookie. - * - * @return the login name found, or null. - * @since 3.0 - */ - @Unstable - public String getUserId() - { - logger.info("retrieve cookie " + cookiePfx + AUTHENTICATION_COOKIE); - String cookie = getCookieValue(cookiePfx + AUTHENTICATION_COOKIE); - if (cookie != null) { - return decryptText(cookie); - } - return null; - } - private Cipher getCipher(boolean encrypt) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException { diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthServiceImpl.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthServiceImpl.java index 3800d7a..5a68506 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthServiceImpl.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthServiceImpl.java @@ -30,7 +30,6 @@ import org.securityfilter.realm.SimplePrincipal; import org.slf4j.Logger; -import com.xwiki.googleapps.GoogleAppsManager; import org.xwiki.component.annotation.Component; import org.xwiki.component.annotation.InstantiationStrategy; import org.xwiki.component.descriptor.ComponentInstantiationStrategy; @@ -46,6 +45,7 @@ import com.xpn.xwiki.user.api.XWikiUser; import com.xpn.xwiki.user.impl.xwiki.XWikiAuthServiceImpl; import com.xpn.xwiki.web.XWikiRequest; +import com.xwiki.googleapps.GoogleAppsManager; /** * An authenticator that can include a negotiation with the Google Cloud (e.g. Google Drive) services. This @@ -74,6 +74,8 @@ public class GoogleAppsAuthServiceImpl extends XWikiAuthServiceImpl @Named("xwikicfg") private Provider xwikicfgProvider; + private Pattern logoutRequestMatcher; + /** * Evaluates if the user can be authenticated based on request info such as cookies. * @@ -232,8 +234,6 @@ public Principal authenticate(String username, String password, XWikiContext con } } - private Pattern logoutRequestMatcher; - /** * @return true if the current request match the configured logout page pattern. */ diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java index 84f927f..d25ec58 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java @@ -19,7 +19,9 @@ */ package com.xwiki.googleapps.internal; -import com.xpn.xwiki.doc.XWikiDocument; +import java.util.Arrays; +import java.util.List; + import org.xwiki.bridge.event.ApplicationReadyEvent; import org.xwiki.bridge.event.DocumentUpdatedEvent; import org.xwiki.component.phase.InitializationException; @@ -27,8 +29,7 @@ import org.xwiki.observation.EventListener; import org.xwiki.observation.event.Event; -import java.util.Arrays; -import java.util.List; +import com.xpn.xwiki.doc.XWikiDocument; /** * Registered object to listen to document changes. @@ -40,7 +41,8 @@ class GoogleAppsEventListener implements EventListener { private GoogleAppsManagerImpl manager; - GoogleAppsEventListener(GoogleAppsManagerImpl manager) { + GoogleAppsEventListener(GoogleAppsManagerImpl manager) + { this.manager = manager; } @@ -50,7 +52,8 @@ class GoogleAppsEventListener implements EventListener * @return googleapps.scriptservice. */ @Override - public String getName() { + public String getName() + { return "googleapps.scriptservice"; } @@ -60,20 +63,22 @@ public String getName() { * @return ApplicationReadyEvent and DocumentUpdatedEvent */ @Override - public List getEvents() { + public List getEvents() + { return Arrays.asList(new ApplicationReadyEvent(), new DocumentUpdatedEvent()); } /** - * Triggers a configuration reload (if the configuration is changed or the app is started) or - * an initialization (if the app is started). + * Triggers a configuration reload (if the configuration is changed or the app is started) or an initialization (if + * the app is started). * * @param event The event listened to. * @param source The object sending the event. * @param data Data about the event. */ @Override - public void onEvent(Event event, Object source, Object data) { + public void onEvent(Event event, Object source, Object data) + { boolean applicationStarted = false; boolean configChanged = false; if (event instanceof ApplicationReadyEvent) { diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java index 1abaf09..8ad03de 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java @@ -47,9 +47,6 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.slf4j.Logger; -import com.xwiki.googleapps.DriveDocMetadata; -import com.xwiki.googleapps.GoogleAppsException; -import com.xwiki.googleapps.GoogleAppsManager; import org.xwiki.component.annotation.Component; import org.xwiki.component.manager.ComponentLookupException; import org.xwiki.component.manager.ComponentManager; @@ -93,6 +90,9 @@ import com.xpn.xwiki.objects.BaseObject; import com.xpn.xwiki.web.XWikiRequest; import com.xpn.xwiki.web.XWikiResponse; +import com.xwiki.googleapps.DriveDocMetadata; +import com.xwiki.googleapps.GoogleAppsException; +import com.xwiki.googleapps.GoogleAppsManager; /** * Set of methods accessible to the scripts using the GoogleApps functions. @@ -106,7 +106,55 @@ public class GoogleAppsManagerImpl implements GoogleAppsManager, Initializable, Disposable { - // ----------------------------- Lifecycle --------------------------- + private static final String AVATAR = "avatar"; + + private static final String SPACENAME = "GoogleApps"; + + private static final String VIEWACTION = "view"; + + private static final String WIKINAME = "xwiki"; + + private static final String XWIKISPACE = "XWiki"; + + private static final String XWIKIGUEST = "XWikiGuest"; + + private static final String AUTOAPPROVAL = "auto"; + + private static final String EMAIL = "email"; + + private static final String PASSWORD = "password"; + + private static final String FIRSTNAME = "first_name"; + + private static final String LASTNAME = "last_name"; + + private static final String OAUTH = "OAuth"; + + private static final String FAILEDLOGIN = "failed login"; + + private static final String NOUSER = "no user"; + + private static final String USER = "user"; + + private static final String GOOGLEUSERATT = "googleUser"; + + private static final String ID = "id"; + + private static final String FILENAME = "fileName"; + + private static final String VERSION = "version"; + + private static final String URL = "url"; + + private static final String EXPORTLINK = "exportLink"; + + private static final String EDITLINK = "editLink"; + + private static final String EMBEDLINK = "embedLink"; + + private static final String UPDATECOMMENT = "Updated Google Apps Document metadata"; + + private static final String EXPORTFORMATEQ = "exportFormat="; @Inject private Provider xwikiContextProvider; @@ -131,42 +179,6 @@ public class GoogleAppsManagerImpl @Inject private ComponentManager componentManager; - @Override - public void initialize() throws InitializationException - { - log.info("GoogleAppsScriptService initializing."); - XWikiContext context = xwikiContextProvider.get(); - XWiki xwiki = context.getWiki(); - - readConfigDoc(context); - - if (xwiki != null) { - log.info("Initting authService."); - // We do not verify with the context if the plugin is active and if the license is active - // this will be done by the GoogleAppsAuthService and UI pages later on, when it is called within a request - try { - authService = componentManager.getInstance(GoogleAppsAuthService.class); - xwiki.setAuthService(authService); - log.info("Succeeded initting authService,"); - } catch (ComponentLookupException e) { - log.info("Failed initting authService", e); - } - } - if (authService == null) { - log.info("Not yet initting authService."); - } - - try { - jacksonFactory = JacksonFactory.getDefaultInstance(); - httpTransport = GoogleNetHttpTransport.newTrustedTransport(); - } catch (Exception e) { - e.printStackTrace(); - throw new InitializationException("Trouble at initializing", e); - } - } - - // internals - private GoogleAppsAuthServiceImpl authService; private DocumentReference configDocRef; @@ -186,17 +198,6 @@ public void initialize() throws InitializationException private CloseableHttpClient httpclient = HttpClients.createDefault(); - private BaseObject getConfigDoc(XWikiContext context) throws XWikiException - { - configDocRef = getConfigDocRef(); - XWikiDocument doc = context.getWiki().getDocument(configObjRef, context); - BaseObject result = doc.getXObject(configObjRef, false, context); - if (result == null) { - log.warn("Can't access Config document."); - } - return result; - } - private Boolean configActiveFlag; private Boolean useCookies; @@ -219,59 +220,52 @@ private BaseObject getConfigDoc(XWikiContext context) throws XWikiException private Integer configCookiesTTL; - // ---------------------------- constants --------------------------------------------- - - private static final String AVATAR = "avatar"; - - private static final String SPACENAME = "GoogleApps"; - - private static final String VIEWACTION = "view"; - - private static final String WIKINAME = "xwiki"; - - private static final String XWIKISPACE = "XWiki"; - - private static final String XWIKIGUEST = "XWikiGuest"; - - private static final String AUTOAPPROVAL = "auto"; - - private static final String EMAIL = "email"; - - private static final String PASSWORD = "password"; - - private static final String FIRSTNAME = "first_name"; - - private static final String LASTNAME = "last_name"; - - private static final String OAUTH = "OAuth"; - - private static final String FAILEDLOGIN = "failed login"; - - private static final String NOUSER = "no user"; - - private static final String USER = "user"; - - private static final String GOOGLEUSERATT = "googleUser"; - - private static final String ID = "id"; - - private static final String FILENAME = "fileName"; - - private static final String VERSION = "version"; - - private static final String URL = "url"; - - private static final String EXPORTLINK = "exportLink"; + private DocumentReference gauthClassRef; - private static final String EDITLINK = "editLink"; + @Override + public void initialize() throws InitializationException + { + log.info("GoogleAppsScriptService initializing."); + XWikiContext context = xwikiContextProvider.get(); + XWiki xwiki = context.getWiki(); - private static final String EMBEDLINK = "embedLink"; + readConfigDoc(context); - private static final String UPDATECOMMENT = "Updated Google Apps Document metadata"; + if (xwiki != null) { + log.info("Initting authService."); + // We do not verify with the context if the plugin is active and if the license is active + // this will be done by the GoogleAppsAuthService and UI pages later on, when it is called within a request + try { + authService = componentManager.getInstance(GoogleAppsAuthService.class); + xwiki.setAuthService(authService); + log.info("Succeeded initting authService,"); + } catch (ComponentLookupException e) { + log.info("Failed initting authService", e); + } + } + if (authService == null) { + log.info("Not yet initting authService."); + } - private static final String EXPORTFORMATEQ = "exportFormat="; + try { + jacksonFactory = JacksonFactory.getDefaultInstance(); + httpTransport = GoogleNetHttpTransport.newTrustedTransport(); + } catch (Exception e) { + e.printStackTrace(); + throw new InitializationException("Trouble at initializing", e); + } + } - // ----------------------------- APIs ------------------------------------------------- + private BaseObject getConfigDoc(XWikiContext context) throws XWikiException + { + configDocRef = getConfigDocRef(); + XWikiDocument doc = context.getWiki().getDocument(configObjRef, context); + BaseObject result = doc.getXObject(configObjRef, false, context); + if (result == null) { + log.warn("Can't access Config document."); + } + return result; + } /** * Evaluates weather the application is active and licensed by looking at the stored documents. Within a request, @@ -423,7 +417,8 @@ public String getBuildVersion() return attr.getValue("Specification-Version"); } - private Attributes getManifestMainAttributes() { + private Attributes getManifestMainAttributes() + { try { Class clazz = getClass(); String className = clazz.getSimpleName() @@ -445,8 +440,6 @@ private Attributes getManifestMainAttributes() { } } - // from ActiveDirectorySetupListener - /** * Note that this dispose() will get called when this Extension is uninstalled which is the use case we want to * serve. The fact that it'll also be called when XWiki stops is a side effect that is ok. @@ -463,7 +456,6 @@ public void dispose() } } - // ----------------------------------------------------------------------------------------- private XWiki getXWiki() { XWiki result = null; @@ -476,8 +468,6 @@ private XWiki getXWiki() return result; } - // ------------------------- public API --------------------------------------------- - /** * @return if the app is configured to use the Google Drive integration (default: yes). * @since 3.0 @@ -488,8 +478,6 @@ public boolean isDriveEnabled() return configScopeUseDrive; } - // ----------------------------- Google Apps Tool (mostly request specific) ----------------------------------- - private String getOAuthUrl() { try { @@ -528,8 +516,6 @@ DocumentReference createUserReference(String userName) return userResolver.resolve(userName); } - private DocumentReference gauthClassRef; - private DocumentReference getGoogleAuthClassReference() { if (gauthClassRef == null) { @@ -1277,7 +1263,7 @@ public DriveDocMetadata getGoogleDocument(String pageName, String fileName) * * @param docId the identifier of the Google Docs document to be embedded * @param doc the XWiki document where to attach the embedding - * @param objp the XWiki object where this embedding is to be updated (or null if it is to be created) + * @param objp the XWiki object where this embedding is to be updated (or null if it is to be created) * @param nb the number of the embedding across all the page's embeddings * @return the created or actualized document * @since 3.0 From 782b36dad9f7983b33a6cac36fcd18c0014f3927 Mon Sep 17 00:00:00 2001 From: Paul Libbrecht Date: Wed, 5 Feb 2020 22:16:03 +0000 Subject: [PATCH 08/17] Unstability for complete classes. paul --- .../java/com/xwiki/googleapps/GoogleAppsException.java | 7 +++---- .../xwiki/googleapps/internal/GoogleAppsEventListener.java | 5 ++++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/api/src/main/java/com/xwiki/googleapps/GoogleAppsException.java b/api/src/main/java/com/xwiki/googleapps/GoogleAppsException.java index 86feb57..2178ebf 100644 --- a/api/src/main/java/com/xwiki/googleapps/GoogleAppsException.java +++ b/api/src/main/java/com/xwiki/googleapps/GoogleAppsException.java @@ -31,6 +31,7 @@ * @version $Id$ * @since 3.0 */ +@Unstable public class GoogleAppsException extends RuntimeException { private static final long serialVersionUID = 3000; @@ -67,19 +68,17 @@ public GoogleAppsException(Exception wrapped) * @return true if the wrapped exception of XWiki origin. * @since 3.0 */ - @Unstable public boolean isWikiException() { - return super.getCause() instanceof XWikiException; + return getCause() instanceof XWikiException; } /** * @return true if the wrapped exception of Google origin (for now: any IO-related exception). * @since 3.0 */ - @Unstable public boolean isGoogleException() { - return super.getCause() instanceof IOException; + return getCause() instanceof IOException; } } diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java index d25ec58..6835dbc 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java @@ -28,6 +28,8 @@ import org.xwiki.model.reference.DocumentReference; import org.xwiki.observation.EventListener; import org.xwiki.observation.event.Event; +import org.xwiki.stability.Unstable; + import com.xpn.xwiki.doc.XWikiDocument; @@ -37,6 +39,7 @@ * @version $Id$ * @since 3.0 */ +@Unstable class GoogleAppsEventListener implements EventListener { private GoogleAppsManagerImpl manager; @@ -87,7 +90,7 @@ public void onEvent(Event event, Object source, Object data) if (event instanceof DocumentUpdatedEvent) { XWikiDocument document = (XWikiDocument) source; DocumentReference configDocRef = manager.getConfigDocRef(); - if (document != null && document.getDocumentReference().compareTo(configDocRef) == 0) { + if (document != null && document.getDocumentReference().equals(configDocRef)) { configChanged = true; } } From 70368b23ea6498a229ef3f4e26fb78dbfaffce12 Mon Sep 17 00:00:00 2001 From: Paul Libbrecht Date: Wed, 5 Feb 2020 22:59:01 +0000 Subject: [PATCH 09/17] Do use the GoogleAppsListener! --- .../googleapps/internal/GoogleAppsManagerImpl.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java index 8ad03de..3c32d6a 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java @@ -57,6 +57,7 @@ import org.xwiki.model.reference.DocumentReference; import org.xwiki.model.reference.DocumentReferenceResolver; import org.xwiki.model.reference.ObjectReference; +import org.xwiki.observation.ObservationManager; import org.xwiki.query.Query; import org.xwiki.query.QueryException; import org.xwiki.query.QueryManager; @@ -179,12 +180,16 @@ public class GoogleAppsManagerImpl @Inject private ComponentManager componentManager; + @Inject + private ObservationManager observationManager; + private GoogleAppsAuthServiceImpl authService; private DocumentReference configDocRef; private ObjectReference configObjRef; + private GoogleAppsEventListener eventListener; /** * A map of hash to full redirects. */ @@ -247,6 +252,11 @@ public void initialize() throws InitializationException log.info("Not yet initting authService."); } + if (eventListener == null) { + eventListener = new GoogleAppsEventListener(this); + observationManager.addListener(eventListener); + } + try { jacksonFactory = JacksonFactory.getDefaultInstance(); httpTransport = GoogleNetHttpTransport.newTrustedTransport(); From 48c0b31892f0d2044bb7cb825c669a946f3ae81c Mon Sep 17 00:00:00 2001 From: Paul Libbrecht Date: Fri, 3 Apr 2020 13:05:19 +0200 Subject: [PATCH 10/17] Indenting. paul --- .../GoogleApps/GoogleAppsConfigClass.js | 228 +++++++++--------- .../GoogleApps/GoogleAppsConfigSheet.vm | 214 +++++++++------- 2 files changed, 242 insertions(+), 200 deletions(-) diff --git a/ui/src/main/resources/GoogleApps/GoogleAppsConfigClass.js b/ui/src/main/resources/GoogleApps/GoogleAppsConfigClass.js index 245e71a..938409f 100644 --- a/ui/src/main/resources/GoogleApps/GoogleAppsConfigClass.js +++ b/ui/src/main/resources/GoogleApps/GoogleAppsConfigClass.js @@ -1,115 +1,119 @@ require(['jquery'], function (jQuery) { - var prefix = '#GoogleApps\\.GoogleAppsConfigClass_0_'; - - //deactivating - function deactivate(elts) { - elts.each(function () { - jQuery(jQuery(this).closest('dl')).find('label').css('color','darkgrey'); - this.wasDisabled = jQuery(this).prop('disabled'); - jQuery(this).prop('disabled', true); - }); - } - - - // reactivating - function reactivate(elts) { - jQuery(elts).each(function () { - if (typeof (this.wasDisabled)) { - jQuery(jQuery(this).closest('dl')).find('label').css('color','black'); - jQuery(this).prop('disabled', this.wasDisabled); - } - }); - } - - - // updaters - function updateAllInputs() { - if (this.checked) { - reactivate(allInputs); - } else { - deactivate(allInputs); - } - } - - function updateCookieFields() { - if (this.checked) { - reactivate(cookieInputs); - } else { - deactivate(cookieInputs); - } - } - - function updateDomainHint() { - if (typeof(this.value)=='undefined') return; - var domainHint = jQuery('#googleapps-domain-livehint'); - var valid = /^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$/.test(this.value); - if (this.value.length > 0) { - if (valid) { - domainHint.text(hintTextOn.replace('\{0\}', this.value)); - domainHint.removeClass('warningmessage'); - } else { - domainHint.text(hintTextInvaliddomain.replace('\{0\}', this.value)); - domainHint.addClass('warningmessage'); - } - } else { - domainHint.text(hintTextOff); - domainHint.removeClass('warningmessage'); - } - } - - - var allInput, cookieInputs; - var hintTextOn = "${escapetool.javascript($services.localization.render('GoogleApps.GoogleAppsConfigClass_domain.hintTextOn'))}", - hintTextOff = "${escapetool.javascript($services.localization.render('GoogleApps.GoogleAppsConfigClass_domain.hintTextOff'))}", - hintTextInvaliddomain = "${escapetool.javascript($services.localization.render('GoogleApps.GoogleAppsConfigClass_domain.hintTextInvaliddomain'))}"; - - function updateScopes() { - var scopes = ""; - jQuery(("input[name^='scope_']")).each(function() { - if(jQuery.attr(this, "disabled")!=="disabled" && this.checked) - scopes = scopes + " " + this.name.substring('scope_'.length); - }); - jQuery("input[name$='_scope']").val(scopes); - updateDriveNowWhat(); - } - - function readScopes() { - var scope = jQuery("input[name$='_scope']").val(); - jQuery(("input[name^='scope_']")).each(function() { - if(jQuery.attr(this, "disabled")!=="disabled") { - var n = this.name.substring('scope_'.length); - if (scope.indexOf(n) > -1) { - this.checked = true; - } else { - this.checked = false; - } - } - }); - updateDriveNowWhat(); - } - - function updateDriveNowWhat() { - var driveInput = jQuery("input[name='scope_drive']")[0]; - if(driveInput.checked) jQuery("#driveOnNowWhat").show(); - else jQuery("#driveOnNowWhat").hide(); - } - - (function () { - // register listeners - cookieInputs = jQuery(prefix + 'skipLoginPage, ' + prefix + 'authWithCookies, ' + prefix + 'cookiesTTL'); - jQuery(prefix + 'useCookies').each(updateCookieFields).change(updateCookieFields); - - allInputs = jQuery('#googleapps_GoogleApps\\.GoogleAppsConfig input:not([name$=\'_activate\'])'); - jQuery(prefix + 'activate').each(updateAllInputs).change(updateAllInputs); - - jQuery(prefix + 'domain').each(updateDomainHint).on('change keyup', updateDomainHint); - - jQuery(("input[name^='scope_']")).each(function() { - jQuery(this).change(function() { updateScopes(); }); - }); - readScopes(); - - jQuery('#googleapps_GoogleApps\.GoogleAppsConfig').submit(cancelCancelEdit); - }).defer(); + var prefix = '#GoogleApps\\.GoogleAppsConfigClass_0_'; + + //deactivating + function deactivate(elts) { + elts.each(function () { + jQuery(jQuery(this).closest('dl')).find('label').css('color','darkgrey'); + this.wasDisabled = jQuery(this).prop('disabled'); + jQuery(this).prop('disabled', true); + }); + } + + + // reactivating + function reactivate(elts) { + jQuery(elts).each(function () { + if (typeof (this.wasDisabled)) { + jQuery(jQuery(this).closest('dl')).find('label').css('color','black'); + jQuery(this).prop('disabled', this.wasDisabled); + } + }); + } + + + // updaters + function updateAllInputs() { + if (this.checked) { + reactivate(allInputs); + } else { + deactivate(allInputs); + } + } + + function updateCookieFields() { + if (this.checked) { + reactivate(cookieInputs); + } else { + deactivate(cookieInputs); + } + } + + function updateDomainHint() { + if (typeof(this.value)=='undefined') return; + var domainHint = jQuery('#googleapps-domain-livehint'); + var valid = /^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$/.test(this.value); + if (this.value.length > 0) { + if (valid) { + domainHint.text(hintTextOn.replace('\{0\}', this.value)); + domainHint.removeClass('warningmessage'); + } else { + domainHint.text(hintTextInvaliddomain.replace('\{0\}', this.value)); + domainHint.addClass('warningmessage'); + } + } else { + domainHint.text(hintTextOff); + domainHint.removeClass('warningmessage'); + } + } + + + var allInput, cookieInputs; + var hintTextOn = "${escapetool.javascript($services.localization.render('GoogleApps.GoogleAppsConfigClass_domain.hintTextOn'))}", + hintTextOff = "${escapetool.javascript($services.localization.render('GoogleApps.GoogleAppsConfigClass_domain.hintTextOff'))}", + hintTextInvaliddomain = "${escapetool.javascript($services.localization.render('GoogleApps.GoogleAppsConfigClass_domain.hintTextInvaliddomain'))}"; + + function updateScopes() { + var scopes = ""; + jQuery(("input[name^='scope_']")).each(function() { + if(jQuery.attr(this, "disabled")!=="disabled" && this.checked) + scopes = scopes + " " + this.name.substring('scope_'.length); + }); + jQuery("input[name$='_scope']").val(scopes); + updateDriveNowWhat(); + } + + function readScopes() { + var scope = jQuery("input[name$='_scope']").val(); + jQuery(("input[name^='scope_']")).each(function() { + if(jQuery.attr(this, "disabled")!=="disabled") { + var n = this.name.substring('scope_'.length); + if (scope.indexOf(n) > -1) { + this.checked = true; + } else { + this.checked = false; + } + } + }); + updateDriveNowWhat(); + } + + function updateDriveNowWhat() { + var driveInput = jQuery("input[name='scope_drive']")[0]; + if(driveInput.checked) jQuery("#driveOnNowWhat").show(); + else jQuery("#driveOnNowWhat").hide(); + } + + (function () { + // register listeners + cookieInputs = jQuery(prefix + 'skipLoginPage, ' + prefix + 'authWithCookies, ' + prefix + 'cookiesTTL'); + jQuery(prefix + 'useCookies').each(updateCookieFields).change(updateCookieFields); + + var keepThem = "name$=\'_activate\'],[name=\'formactionsac\'],[name=\'form_token\']," + + "[name=\'xcontinue\'],[name=\'xredirect\'"; + + allInputs = jQuery( + '#googleapps_GoogleApps\\.GoogleAppsConfig input:not(['+keepThem+'])'); + jQuery(prefix + 'activate').each(updateAllInputs).change(updateAllInputs); + + jQuery(prefix + 'domain').each(updateDomainHint).on('change keyup', updateDomainHint); + + jQuery(("input[name^='scope_']")).each(function() { + jQuery(this).change(function() { updateScopes(); }); + }); + readScopes(); + + jQuery('#googleapps_GoogleApps\.GoogleAppsConfig').submit(cancelCancelEdit); + }).defer(); }); diff --git a/ui/src/main/resources/GoogleApps/GoogleAppsConfigSheet.vm b/ui/src/main/resources/GoogleApps/GoogleAppsConfigSheet.vm index abc25f2..0cb0512 100644 --- a/ui/src/main/resources/GoogleApps/GoogleAppsConfigSheet.vm +++ b/ui/src/main/resources/GoogleApps/GoogleAppsConfigSheet.vm @@ -6,108 +6,146 @@ $xwiki.ssx.use('GoogleApps.GoogleAppsConfigClass') #set($formId = "${section.toLowerCase()}_${configClassName}") #set($configDoc = $xwiki.getDocument($configClassName)) #set($className="GoogleApps.GoogleAppsConfigClass") -#set($prefix="${configDoc.fullName}_${className}_0") +#set($propNamePrefix="${configDoc.fullName}_${className}_0") #set($obj = $configDoc.getObject($className)) ## shorthand for t(ranslation) and tp(translation with parameters). -#macro(t $n)## - $services.localization.render("GoogleApps.GoogleAppsConfigClass_${n}")## +#macro (t $name) + $services.localization.render("GoogleApps.GoogleAppsConfigClass_${name}") #end -#macro(tp $n $p)## - $services.localization.render("GoogleApps.GoogleAppsConfigClass_${n}",$p)## +#macro (tp $name $params) + #set ($transNamePrefix = 'GoogleApps.GoogleAppsConfigClass_') + #if ($name.startsWith("googleapps")) + #set($transNamePrefix='') + #end + #set ($msg = $services.localization.render("GoogleApps.GoogleAppsConfigClass_${name}",$params)) + ## convert to link-text for the simple wiki syntax of links + $stringtool.replacePattern($msg, '\[\[([^>]*)>>([^\]]*)\]\]', '$1') +#end +#macro (displayInput $property) + #set ($output = $doc.display($property, 'edit', $obj)) + #set ($output = $stringtool.removeStart($output, '{{html clean="false" wiki="false"}}')) + #set ($output = $stringtool.removeEnd($output, '{{/html}}')) + $output #end -$services.localization.render('googleapps.config.explanation') +$services.localization.render('googleapps.config.explanation') -{{html wiki=true}} +{{html clean="false"}}

-; $configDoc.display("activate", 'edit', $obj) - -
-#t("communicate") - -; ## -#tp("communicate.hint", ['[[', '>>https://accounts.google.com/ManageDomains]]'])
-#tp("communicate.hint2", ['[[', '>>https://store.xwiki.com/xwiki/bin/view/Extension/GoogleAppsIntegration#installation]]'])
- - -; ## -#t("clientid.hint") -: $configDoc.display("clientid", 'edit', $obj) - -; ## -#t("secret.hint") -: $configDoc.display("secret", 'edit', $obj) - -## checkboxes for scope, needs JS -; ## -#t("scope.hint") -: ## - ## - ## - ## -$configDoc.display("scope", "hidden") - -; ## -#t("appname.hint") -: $configDoc.display("appname", 'edit', $obj) - -
- -
-#t("loginbehaviour") - -; ## -#t("domain.hint") -: $configDoc.display("domain", 'edit', $obj)
## -  - -; $configDoc.display("useCookies", 'edit', $obj) ## -#t("useCookies.hint") - -:; $configDoc.display("skipLoginPage", 'edit', $obj) ## - ## -#t("skipLoginPage.hint") - -:; $configDoc.display("authWithCookies", 'edit', $obj) ## - ## -#t("authWithCookies.hint") - -:; ## -#t("cookiesTTL.hint") -:: $configDoc.display("cookiesTTL", 'edit', $obj) -
- - -## Hidden form elements -#set($params="editor=${escapetool.url(${editor})}&section=${escapetool.url(${section})}") -#set($params="${params}&space=${escapetool.url(${currentSpace})}") -#set($continueURL=$xwiki.getURL($currentDoc, 'admin', $params)) - - - - -## submit -
-

- -

-
+
+
#displayInput ('activate')
+
+
+ +
+ #t ('communicate') + +
+
+ #tp ('communicate.hint', ['[[', '>>https://accounts.google.com/ManageDomains]]'])
+ #tp ('communicate.hint2', ['[[', '>>https://store.xwiki.com/xwiki/bin/view/Extension/GoogleAppsIntegration#installation]]'])
+
+
+
+ + +
+
+ #t ("clientid.hint")
+
#displayInput ('clientid')
+
+ +
+
+ #t("secret.hint")
+
#displayInput ("secret")
+
+ + ## checkboxes for scope, needs JS +
+
+ #t ('scope.hint')
+
+ + + + + +
+
+ +
+
+ #t ('appname.hint')
+
#displayInput ('appname')
+
+
+ +
+ #t ('loginbehaviour') + +
+
+ #t("domain.hint")
+
#displayInput ("domain")
+  
+
+ +
+
#displayInput ('useCookies') + #t ('useCookies.hint')
+
+
+
#displayInput ('skipLoginPage') + + #t ('skipLoginPage.hint')
+
+
+
+
#displayInput ('authWithCookies') + + #t ('authWithCookies.hint')
+
+
+
+
+ #t ('cookiesTTL.hint')
+
#displayInput ('cookiesTTL')
+
+
+
+
+ + + ## Hidden form elements + #set ($params = "editor=${escapetool.url(${editor})}&section=${escapetool.url(${section})}") + #set ($params = "${params}&space=${escapetool.url(${currentSpace})}") + #set ($continueURL = $xwiki.getURL($currentDoc, 'admin', $params)) + + + + + ## submit +
+

+ +

+
-
-#tp("nowWhat1", ['[[','>>GoogleApps.TestDocumentList]]']) +

#tp ('nowWhat1', ['[[',">>${xwiki.getURL('GoogleApps.TestDocumentList')}]]"])

-#tp("nowWhat2", ['##~{~{drive/~}~}##']) +

#tp ('nowWhat2', ['{{drive/}}'])

{{/html}} -{{/velocity}} +{{/velocity}} \ No newline at end of file From 4e44d34951d8ff78cbc3f0405a3dc136f212af67 Mon Sep 17 00:00:00 2001 From: Paul Libbrecht Date: Thu, 9 Apr 2020 19:18:30 +0200 Subject: [PATCH 11/17] Indenting and normalising spaces. paul --- .../internal/GoogleAppsEventListener.java | 1 - .../internal/GoogleAppsManagerImpl.java | 2 +- .../GoogleApps/GoogleAppsConfigClass.js | 232 +++++++++--------- .../GoogleApps/GoogleAppsConfigSheet.vm | 58 ++--- 4 files changed, 144 insertions(+), 149 deletions(-) diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java index 6835dbc..12cb512 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java @@ -30,7 +30,6 @@ import org.xwiki.observation.event.Event; import org.xwiki.stability.Unstable; - import com.xpn.xwiki.doc.XWikiDocument; /** diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java index 3c32d6a..bca0b4c 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java @@ -106,7 +106,6 @@ public class GoogleAppsManagerImpl implements GoogleAppsManager, Initializable, Disposable { - private static final String AVATAR = "avatar"; private static final String SPACENAME = "GoogleApps"; @@ -190,6 +189,7 @@ public class GoogleAppsManagerImpl private ObjectReference configObjRef; private GoogleAppsEventListener eventListener; + /** * A map of hash to full redirects. */ diff --git a/ui/src/main/resources/GoogleApps/GoogleAppsConfigClass.js b/ui/src/main/resources/GoogleApps/GoogleAppsConfigClass.js index 938409f..87c1e06 100644 --- a/ui/src/main/resources/GoogleApps/GoogleAppsConfigClass.js +++ b/ui/src/main/resources/GoogleApps/GoogleAppsConfigClass.js @@ -1,119 +1,119 @@ require(['jquery'], function (jQuery) { - var prefix = '#GoogleApps\\.GoogleAppsConfigClass_0_'; - - //deactivating - function deactivate(elts) { - elts.each(function () { - jQuery(jQuery(this).closest('dl')).find('label').css('color','darkgrey'); - this.wasDisabled = jQuery(this).prop('disabled'); - jQuery(this).prop('disabled', true); - }); - } - - - // reactivating - function reactivate(elts) { - jQuery(elts).each(function () { - if (typeof (this.wasDisabled)) { - jQuery(jQuery(this).closest('dl')).find('label').css('color','black'); - jQuery(this).prop('disabled', this.wasDisabled); - } - }); - } - - - // updaters - function updateAllInputs() { - if (this.checked) { - reactivate(allInputs); - } else { - deactivate(allInputs); - } - } - - function updateCookieFields() { - if (this.checked) { - reactivate(cookieInputs); - } else { - deactivate(cookieInputs); - } - } - - function updateDomainHint() { - if (typeof(this.value)=='undefined') return; - var domainHint = jQuery('#googleapps-domain-livehint'); - var valid = /^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$/.test(this.value); - if (this.value.length > 0) { - if (valid) { - domainHint.text(hintTextOn.replace('\{0\}', this.value)); - domainHint.removeClass('warningmessage'); - } else { - domainHint.text(hintTextInvaliddomain.replace('\{0\}', this.value)); - domainHint.addClass('warningmessage'); - } - } else { - domainHint.text(hintTextOff); - domainHint.removeClass('warningmessage'); - } - } - - - var allInput, cookieInputs; - var hintTextOn = "${escapetool.javascript($services.localization.render('GoogleApps.GoogleAppsConfigClass_domain.hintTextOn'))}", - hintTextOff = "${escapetool.javascript($services.localization.render('GoogleApps.GoogleAppsConfigClass_domain.hintTextOff'))}", - hintTextInvaliddomain = "${escapetool.javascript($services.localization.render('GoogleApps.GoogleAppsConfigClass_domain.hintTextInvaliddomain'))}"; - - function updateScopes() { - var scopes = ""; - jQuery(("input[name^='scope_']")).each(function() { - if(jQuery.attr(this, "disabled")!=="disabled" && this.checked) - scopes = scopes + " " + this.name.substring('scope_'.length); - }); - jQuery("input[name$='_scope']").val(scopes); - updateDriveNowWhat(); - } - - function readScopes() { - var scope = jQuery("input[name$='_scope']").val(); - jQuery(("input[name^='scope_']")).each(function() { - if(jQuery.attr(this, "disabled")!=="disabled") { - var n = this.name.substring('scope_'.length); - if (scope.indexOf(n) > -1) { - this.checked = true; - } else { - this.checked = false; - } - } - }); - updateDriveNowWhat(); - } - - function updateDriveNowWhat() { - var driveInput = jQuery("input[name='scope_drive']")[0]; - if(driveInput.checked) jQuery("#driveOnNowWhat").show(); - else jQuery("#driveOnNowWhat").hide(); - } - - (function () { - // register listeners - cookieInputs = jQuery(prefix + 'skipLoginPage, ' + prefix + 'authWithCookies, ' + prefix + 'cookiesTTL'); - jQuery(prefix + 'useCookies').each(updateCookieFields).change(updateCookieFields); - - var keepThem = "name$=\'_activate\'],[name=\'formactionsac\'],[name=\'form_token\']," - + "[name=\'xcontinue\'],[name=\'xredirect\'"; - - allInputs = jQuery( - '#googleapps_GoogleApps\\.GoogleAppsConfig input:not(['+keepThem+'])'); - jQuery(prefix + 'activate').each(updateAllInputs).change(updateAllInputs); - - jQuery(prefix + 'domain').each(updateDomainHint).on('change keyup', updateDomainHint); - - jQuery(("input[name^='scope_']")).each(function() { - jQuery(this).change(function() { updateScopes(); }); - }); - readScopes(); - - jQuery('#googleapps_GoogleApps\.GoogleAppsConfig').submit(cancelCancelEdit); - }).defer(); + var prefix = '#GoogleApps\\.GoogleAppsConfigClass_0_'; + + //deactivating + function deactivate(elts) { + elts.each(function () { + jQuery(jQuery(this).closest('dl')).find('label').css('color','darkgrey'); + this.wasDisabled = jQuery(this).prop('disabled'); + jQuery(this).prop('disabled', true); + }); + } + + + // reactivating + function reactivate(elts) { + jQuery(elts).each(function () { + if (typeof (this.wasDisabled)) { + jQuery(jQuery(this).closest('dl')).find('label').css('color','black'); + jQuery(this).prop('disabled', this.wasDisabled); + } + }); + } + + + // updaters + function updateAllInputs() { + if (this.checked) { + reactivate(allInputs); + } else { + deactivate(allInputs); + } + } + + function updateCookieFields() { + if (this.checked) { + reactivate(cookieInputs); + } else { + deactivate(cookieInputs); + } + } + + function updateDomainHint() { + if (typeof(this.value)=='undefined') return; + var domainHint = jQuery('#googleapps-domain-livehint'); + var valid = /^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$/.test(this.value); + if (this.value.length > 0) { + if (valid) { + domainHint.text(hintTextOn.replace('\{0\}', this.value)); + domainHint.removeClass('warningmessage'); + } else { + domainHint.text(hintTextInvaliddomain.replace('\{0\}', this.value)); + domainHint.addClass('warningmessage'); + } + } else { + domainHint.text(hintTextOff); + domainHint.removeClass('warningmessage'); + } + } + + + var allInput, cookieInputs; + var hintTextOn = "${escapetool.javascript($services.localization.render('GoogleApps.GoogleAppsConfigClass_domain.hintTextOn'))}", + hintTextOff = "${escapetool.javascript($services.localization.render('GoogleApps.GoogleAppsConfigClass_domain.hintTextOff'))}", + hintTextInvaliddomain = "${escapetool.javascript($services.localization.render('GoogleApps.GoogleAppsConfigClass_domain.hintTextInvaliddomain'))}"; + + function updateScopes() { + var scopes = ""; + jQuery(("input[name^='scope_']")).each(function() { + if(jQuery.attr(this, "disabled")!=="disabled" && this.checked) + scopes = scopes + " " + this.name.substring('scope_'.length); + }); + jQuery("input[name$='_scope']").val(scopes); + updateDriveNowWhat(); + } + + function readScopes() { + var scope = jQuery("input[name$='_scope']").val(); + jQuery(("input[name^='scope_']")).each(function() { + if(jQuery.attr(this, "disabled")!=="disabled") { + var n = this.name.substring('scope_'.length); + if (scope.indexOf(n) > -1) { + this.checked = true; + } else { + this.checked = false; + } + } + }); + updateDriveNowWhat(); + } + + function updateDriveNowWhat() { + var driveInput = jQuery("input[name='scope_drive']")[0]; + if(driveInput.checked) jQuery("#driveOnNowWhat").show(); + else jQuery("#driveOnNowWhat").hide(); + } + + (function () { + // register listeners + cookieInputs = jQuery(prefix + 'skipLoginPage, ' + prefix + 'authWithCookies, ' + prefix + 'cookiesTTL'); + jQuery(prefix + 'useCookies').each(updateCookieFields).change(updateCookieFields); + + var keepThem = "name$=\'_activate\'],[name=\'formactionsac\'],[name=\'form_token\']," + + "[name=\'xcontinue\'],[name=\'xredirect\'"; + + allInputs = jQuery( + '#googleapps_GoogleApps\\.GoogleAppsConfig input:not(['+keepThem+'])'); + jQuery(prefix + 'activate').each(updateAllInputs).change(updateAllInputs); + + jQuery(prefix + 'domain').each(updateDomainHint).on('change keyup', updateDomainHint); + + jQuery(("input[name^='scope_']")).each(function() { + jQuery(this).change(function() { updateScopes(); }); + }); + readScopes(); + + jQuery('#googleapps_GoogleApps\.GoogleAppsConfig').submit(cancelCancelEdit); + }).defer(); }); diff --git a/ui/src/main/resources/GoogleApps/GoogleAppsConfigSheet.vm b/ui/src/main/resources/GoogleApps/GoogleAppsConfigSheet.vm index 0cb0512..55385e6 100644 --- a/ui/src/main/resources/GoogleApps/GoogleAppsConfigSheet.vm +++ b/ui/src/main/resources/GoogleApps/GoogleAppsConfigSheet.vm @@ -49,7 +49,6 @@ $services.localization.render('googleapps.config.explanation')
-
#t ("clientid.hint")
@@ -97,27 +96,27 @@ $services.localization.render('googleapps.config.explanation')
-
#displayInput ('useCookies') - #t ('useCookies.hint')
-
-
-
#displayInput ('skipLoginPage') - - #t ('skipLoginPage.hint')
-
-
-
-
#displayInput ('authWithCookies') - - #t ('authWithCookies.hint')
-
-
-
-
- #t ('cookiesTTL.hint')
-
#displayInput ('cookiesTTL')
-
-
+
#displayInput ('useCookies') + #t ('useCookies.hint')
+
+
+
#displayInput ('skipLoginPage') + + #t ('skipLoginPage.hint')
+
+
+
+
#displayInput ('authWithCookies') + + #t ('authWithCookies.hint')
+
+
+
+
+ #t ('cookiesTTL.hint')
+
#displayInput ('cookiesTTL')
+
+
@@ -132,19 +131,16 @@ $services.localization.render('googleapps.config.explanation') ## submit
-

- -

+

+ +

- -

#tp ('nowWhat1', ['[[',">>${xwiki.getURL('GoogleApps.TestDocumentList')}]]"])

- -

#tp ('nowWhat2', ['{{drive/}}'])

- +

#tp ('nowWhat1', ['[[',">>${xwiki.getURL('GoogleApps.TestDocumentList')}]]"])

+

#tp ('nowWhat2', ['{{drive/}}'])

{{/html}} From 19275040d2102c5a80e0e0daa08fb20553e52f99 Mon Sep 17 00:00:00 2001 From: Paul Libbrecht Date: Tue, 21 Apr 2020 22:19:32 +0200 Subject: [PATCH 12/17] Refactoring methods to fetch drive-documents to get rid of gdata and make the way for upgrades. Simplifying the API to call the Google drive APIs, from view to java, so as to delete apparent duplicates. paul --- api/pom.xml | 11 +- .../xwiki/googleapps/DriveDocMetadata.java | 30 ++++ .../xwiki/googleapps/GoogleAppsManager.java | 28 +-- .../googleapps/GoogleAppsScriptService.java | 37 +--- .../CookieAuthenticationPersistenceImpl.java | 2 +- .../internal/GoogleAppsManagerImpl.java | 168 ++++-------------- .../main/resources/GoogleApps/DriveMacro.xml | 6 +- .../GoogleApps/ImportFromGoogleApps.xml | 2 +- .../main/resources/GoogleApps/JSExtension.xml | 24 +-- .../GoogleApps/RetrieveFromGoogleApps.xml | 6 +- .../resources/GoogleApps/TestDocumentList.xml | 2 +- 11 files changed, 105 insertions(+), 211 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 3bb2e00..afc8719 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -42,11 +42,6 @@ google-api-client 1.24.1 - - com.google.gdata - core - 1.47.1 - com.google.apis google-api-services-people @@ -57,6 +52,12 @@ google-api-services-drive v2-rev358-1.24.1 + + javax.servlet + javax.servlet-api + 3.0.1 + compile + diff --git a/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java b/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java index 412bb33..1e43603 100644 --- a/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java +++ b/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java @@ -44,4 +44,34 @@ public class DriveDocMetadata * URL to pull from in order to fetch the document. */ public String exportLink; + + /** + * + * @return the internal Google Id of the document. + */ + public String getId() { + return id; + } + + + /** + * @return the URL to direct the user to for editing. + */ + public String getEditLink() { + return editLink; + } + + /** + * @return the URL to pull from in order to fetch the document. + */ + public String getExportLink() { + return exportLink; + } + + /** + * @return a useful string representation + */ + public String toString() { + return "id " + id + " edit: " + editLink + " export " + exportLink; + } } diff --git a/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java b/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java index f53557d..13f1586 100644 --- a/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java +++ b/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java @@ -28,7 +28,6 @@ import com.google.api.client.auth.oauth2.Credential; import com.google.api.services.drive.model.File; -import com.google.api.services.drive.model.FileList; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.objects.BaseObject; @@ -113,16 +112,6 @@ public interface GoogleAppsManager @Unstable String updateUser(); - /** - * Get the list of all documents in the user's associated account. - * - * @return A list of max 10 documents. - * @throws GoogleAppsException if a communication problem with the other components occured - * @since 3.0 - */ - @Unstable - List getDocumentList() throws GoogleAppsException; - /** * Fetches a list of Google Drive document matching a substring query in the filename. * @@ -135,31 +124,20 @@ public interface GoogleAppsManager @Unstable List listDriveDocumentsWithTypes(String query, int nbResults) throws GoogleAppsException; - /** - * Fetches a list of Google Drive document matching a given query. - * - * @param query the expected filename substring - * @param nbResults max number of results - * @return The list of files at Google Drive. - * @throws GoogleAppsException if a communication problem with the other components occured - * @since 3.0 - */ - @Unstable - FileList listDocuments(String query, int nbResults) throws GoogleAppsException; - /** * Fetches the google-drive document's representation and stores it as attachment. * * @param page attach to this page * @param name attach using this file name * @param id store object attached to this attachment using this id (for later sync) - * @param url fetch from this URL + * @param mediaType content-type of the file to be fetched (or "unknown"; in this case the + * mediaType is read from Tika. * @return true if successful * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable - boolean retrieveFileFromGoogle(String page, String name, String id, String url) throws GoogleAppsException; + boolean retrieveFileFromGoogle(String page, String name, String id, String mediaType) throws GoogleAppsException; /** * Extracts metadata about the Google Drive document corresponding to the named attachment. diff --git a/api/src/main/java/com/xwiki/googleapps/GoogleAppsScriptService.java b/api/src/main/java/com/xwiki/googleapps/GoogleAppsScriptService.java index b98af2f..3af87f8 100644 --- a/api/src/main/java/com/xwiki/googleapps/GoogleAppsScriptService.java +++ b/api/src/main/java/com/xwiki/googleapps/GoogleAppsScriptService.java @@ -34,7 +34,6 @@ import com.google.api.client.auth.oauth2.Credential; import com.google.api.services.drive.model.File; -import com.google.api.services.drive.model.FileList; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.api.Document; @@ -140,24 +139,13 @@ public String updateUser() return manager.updateUser(); } - /** - * Get the list of all documents in the user's associated account. - * - * @return A list of max 10 documents. - * @since 3.0 - */ - @Unstable - public List getDocumentList() - { - return manager.getDocumentList(); - } - /** * Fetches a list of Google Drive document matching a substring query in the filename. + * (used in the import function) * * @param query the expected query (e.g. fullText contains winter ski) * @param nbResults max number of results - * @return The list of files at Google Drive. + * @return The list of {@File} at Google Drive. * @since 3.0 */ @Unstable @@ -166,20 +154,6 @@ public List listDriveDocumentsWithTypes(String query, int nbResults) return manager.listDriveDocumentsWithTypes(query, nbResults); } - /** - * Fetches a list of Google Drive document matching a given query. - * - * @param query the expected filename substring - * @param nbResults max number of results - * @return The list of files at Google Drive. - * @since 3.0 - */ - @Unstable - public FileList listDocuments(String query, int nbResults) - { - return manager.listDocuments(query, nbResults); - } - /** * Inserts the current information on the document to be embedded. * @@ -204,14 +178,15 @@ public Object createOrUpdateEmbedObject(String docId, Document doc, Object obj, * @param page attach to this page * @param name attach using this file name * @param id store object attached to this attachment using this id (for later sync) - * @param url fetch from this URL + * @param mediaType content-type of the file to be fetched (or "unknown"; in this case the + * mediaType is read from Tika. * @return true if successful * @since 3.0 */ @Unstable - public boolean retrieveFileFromGoogle(String page, String name, String id, String url) + public boolean retrieveFileFromGoogle(String page, String name, String id, String mediaType) { - return manager.retrieveFileFromGoogle(page, name, id, url); + return manager.retrieveFileFromGoogle(page, name, id, mediaType); } /** diff --git a/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistenceImpl.java b/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistenceImpl.java index 71eef39..3855323 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistenceImpl.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistenceImpl.java @@ -50,7 +50,7 @@ /** * Tools to help storing and retrieving enriched information within cookies such as the linked Google user profile. *

- * This code is inspired by from xwiki-authenticator-trusted https://github.com/xwiki-contrib/xwiki-authenticator-trusted/edit/master\ + * Inspiration: xwiki-authenticator-trusted https://github.com/xwiki-contrib/xwiki-authenticator-trusted/edit/master\ * /xwiki-authenticator-trusted-api/src/main/java/org/xwiki/contrib/authentication\ * /internal/CookieAuthenticationPersistenceStore.java. * diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java index bca0b4c..b5563c4 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java @@ -19,8 +19,6 @@ */ package com.xwiki.googleapps.internal; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -39,13 +37,14 @@ import javax.inject.Singleton; import javax.servlet.http.HttpSession; -import org.apache.commons.io.IOUtils; +import org.apache.commons.httpclient.util.DateUtil; import org.apache.commons.lang.RandomStringUtils; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; +import org.apache.tika.Tika; import org.slf4j.Logger; import org.xwiki.component.annotation.Component; import org.xwiki.component.manager.ComponentLookupException; @@ -80,9 +79,6 @@ import com.google.api.services.people.v1.PeopleServiceScopes; import com.google.api.services.people.v1.model.EmailAddress; import com.google.api.services.people.v1.model.Person; -import com.google.gdata.client.docs.DocsService; -import com.google.gdata.data.MediaContent; -import com.google.gdata.data.media.MediaSource; import com.xpn.xwiki.XWiki; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; @@ -803,7 +799,7 @@ public Credential authorize(boolean redirect) * Performs the necessary communication with Google-Services to fetch identity and update the XWiki-user object or * possibly sends a redirect to a Google login screen. * - * @return "failed login" if failed, NOUSER (can be attempted to Google-OAuth), or "ok" if successful + * @return "failed login" if failed, {@NOUSER} (can be attempted to Google-OAuth), or "ok" if successful * @since 3.0 */ @Unstable @@ -967,37 +963,23 @@ public String updateUser() + (imageUrl.contains("?") ? "&" : '?') + "sz=512"; log.debug("Pulling avatar " + imageUrl); + XWikiAttachment attachment = + userObj.getStringValue(AVATAR) == null ? null + : userDoc.getAttachment(userObj.getStringValue(AVATAR)); HttpGet httpget = new HttpGet(imageUrl); - // TODO: add an if-modified-since + if (attachment != null) { + httpget.addHeader("If-Modified-Since", + DateUtil.formatDate(attachment.getDate())); + } CloseableHttpResponse response = httpclient.execute(httpget); - HttpEntity entity = response.getEntity(); - if (entity != null) { - ByteArrayOutputStream bOut = - new ByteArrayOutputStream((int) entity.getContentLength()); - IOUtils.copy(entity.getContent(), bOut); - byte[] bytesFromGoogle = bOut.toByteArray(); - - XWikiAttachment attachment = - userObj.getStringValue(AVATAR) == null ? null - : userDoc.getAttachment(userObj.getStringValue(AVATAR)); - boolean fileChanged = attachment == null - || attachment.getFilesize() != bytesFromGoogle.length; - if (!fileChanged) { - byte[] b = attachment.getContent(context); - for (int i = 0; i < b.length; i++) { - if (b[i] != bytesFromGoogle[i]) { - fileChanged = true; - break; - } - } - } - if (fileChanged) { - String fileName = imageUrl.substring(imageUrl.lastIndexOf('/') + 1); - log.debug("Avatar changed " + fileName); - userObj.set(AVATAR, fileName, context); - userDoc.addAttachment(fileName, bytesFromGoogle, context); - changed = true; - } + HttpEntity entity = null; + if (response.getStatusLine().getStatusCode() == 200 + && (entity = response.getEntity()) != null) { + String fileName = imageUrl.substring(imageUrl.lastIndexOf('/') + 1); + log.debug("Avatar changed " + fileName); + userObj.set(AVATAR, fileName, context); + userDoc.addAttachment(fileName, entity.getContent(), context); + changed = true; } } @@ -1055,37 +1037,6 @@ private Drive getDriveService() .build(); } - /** - * Build and return an authorized Drive client service. - * - * @return an authorized Drive client service - */ - private DocsService getDocsService() - { - Credential credential = authorize(); - DocsService service = new DocsService(configAppName); - service.setOAuth2Credentials(credential); - return service; - } - - /** - * Get the list of all documents in the user's associated account. - * - * @return A list of max 10 documents. - * @since 3.0 - */ - @Unstable - public List getDocumentList() - { - try { - Drive drive = getDriveService(); - FileList result = drive.files().list().setMaxResults(10).execute(); - return result.getItems(); - } catch (IOException e) { - throw new GoogleAppsException(e); - } - } - /** * Fetches a list of Google Drive document matching a substring query in the filename. * @@ -1100,9 +1051,11 @@ public List listDriveDocumentsWithTypes(String query, int nbResults) try { Drive drive = getDriveService(); Drive.Files.List req = drive.files().list() - .setQ(query) .setFields("items(id,mimeType,title,exportLinks,selfLink,version,alternateLink)") .setMaxResults(nbResults); + if (query != null && query.length() > 0) { + req.setQ(query); + } FileList result = req.execute(); return result.getItems(); } catch (IOException e) { @@ -1110,52 +1063,36 @@ public List listDriveDocumentsWithTypes(String query, int nbResults) } } - /** - * Fetches a list of Google Drive document matching a given query. - * - * @param query the expected filename substring - * @param nbResults max number of results - * @return The list of files at Google Drive. - * @since 3.0 - */ - @Unstable - public FileList listDocuments(String query, int nbResults) - { - try { - Drive drive = getDriveService(); - Drive.Files.List req = drive.files().list().setQ(query).setMaxResults(nbResults); - FileList result = req.execute(); - return result; - } catch (IOException e) { - throw new GoogleAppsException(e); - } - } - /** * Fetches the google-drive document's representation and stores it as attachment. * * @param page attach to this page * @param name attach using this file name * @param id store object attached to this attachment using this id (for later sync) - * @param url fetch from this URL + * @param mediaType content-type of the file to be fetched (or "unknown"; in this case the + * mediaType is read from Tika. * @return true if successful * @since 3.0 */ @Unstable - public boolean retrieveFileFromGoogle(String page, String name, String id, String url) + public boolean retrieveFileFromGoogle(String page, String name, String id, String mediaType) { - return retrieveFileFromGoogle(getDocsService(), getDriveService(), page, name, id, url); + return retrieveFileFromGoogle(getDriveService(), page, name, id, mediaType); } - private boolean retrieveFileFromGoogle(DocsService docsService, Drive driveService, - String page, String name, String id, String url) + private boolean retrieveFileFromGoogle(Drive driveService, String page, String name, String id, String mediaType) { - log.info("Retrieving " + name + " to page " + page + ": " + id + url); + String mt = mediaType; + if ("unknown".equalsIgnoreCase(mediaType) || mediaType == null || !mediaType.contains("/")) { + mt = new Tika().detect(name); + } + + log.info("Retrieving " + name + " to page " + page + ": " + id + "(mediatype " + mt + ")."); try { XWikiDocument adoc = getXWiki().getDocument(documentResolver.resolve(page), xwikiContextProvider.get()); - byte[] data = downloadFile(docsService, url); - saveFileToXWiki(driveService, adoc, id, name, new ByteArrayInputStream(data), true); + InputStream downloadStream = driveService.files().export(id, mt).executeMediaAsInputStream(); + saveFileToXWiki(driveService, adoc, id, name, downloadStream, true); return true; } catch (Exception e) { log.info(e.getMessage(), e); @@ -1163,37 +1100,6 @@ private boolean retrieveFileFromGoogle(DocsService docsService, Drive driveServi } } - private byte[] downloadFile(DocsService docsService, String exportUrl) - { - try { - MediaContent mc = new MediaContent(); - mc.setUri(exportUrl); - MediaSource ms = docsService.getMedia(mc); - - InputStream inStream = null; - ByteArrayOutputStream outStream = new ByteArrayOutputStream(); - - try { - inStream = ms.getInputStream(); - - int c; - while ((c = inStream.read()) != -1) { - outStream.write(c); - } - } finally { - if (inStream != null) { - inStream.close(); - } - outStream.flush(); - outStream.close(); - } - return outStream.toByteArray(); - } catch (Exception e) { - e.printStackTrace(); - throw new GoogleAppsException("trouble at downloading document", e); - } - } - private void saveFileToXWiki(Drive driveService, XWikiDocument adoc, String id, String name, InputStream data, boolean redirect) { @@ -1264,7 +1170,7 @@ public DriveDocMetadata getGoogleDocument(String pageName, String fileName) return gdm; } } catch (XWikiException e) { - throw new GoogleAppsException("Can't get Google-Document inside XWiki.", e); + throw new GoogleAppsException("Can't get Google-Document inside XWiki.", e); } } @@ -1296,9 +1202,9 @@ public BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, Bas obj = doc.newXObject(getSyncDocClassReference(), context); obj.setNumber(nb); } - obj.setStringValue("id", docId); + obj.setStringValue(ID, docId); if (embedLink != null) { - obj.setStringValue("embedLink", embedLink); + obj.setStringValue(EMBEDLINK, embedLink); } obj.setStringValue(EDITLINK, docData.getAlternateLink()); obj.setStringValue(VERSION, docData.getVersion().toString()); diff --git a/ui/src/main/resources/GoogleApps/DriveMacro.xml b/ui/src/main/resources/GoogleApps/DriveMacro.xml index d3a049b..6e0e603 100644 --- a/ui/src/main/resources/GoogleApps/DriveMacro.xml +++ b/ui/src/main/resources/GoogleApps/DriveMacro.xml @@ -352,11 +352,11 @@ if(!googleApps.active || !googleApps.driveEnabled) { """ if (query && embednb==nb) { def squery = "'" + query + "'" - def results = googleApps.listDocuments("fullText contains ${squery}", 10) - def nbres = results.items.size(); + def results = googleApps.listDriveDocumentsWithTypes("fullText contains ${squery}", 10) + def nbres = results.size(); println "${nbres} documents found: " - for (entry in results.items) { + for (entry in results) { def docName = entry.title; def embedLink = entry.embedLink; if (embedLink==null) diff --git a/ui/src/main/resources/GoogleApps/ImportFromGoogleApps.xml b/ui/src/main/resources/GoogleApps/ImportFromGoogleApps.xml index 521db3d..6cf5d93 100644 --- a/ui/src/main/resources/GoogleApps/ImportFromGoogleApps.xml +++ b/ui/src/main/resources/GoogleApps/ImportFromGoogleApps.xml @@ -65,7 +65,7 @@ #set($exportData = $googleApps.getExportLink($docName, $elink)) #set($stype = $exportData[0]) #set($newDocName = $exportData[1]) - [[${stype}>>RetrieveFromGoogleApps||queryString="page=${escapetool.url($request.page)}&name=${escapetool.url($newDocName)}&url=${escapetool.url($elink)}&editLink=${escapetool.url($entry.alternateLink)}&version=${entry.version}&id=${entry.id}"]] #end + [[${stype}>>RetrieveFromGoogleApps||queryString="page=${escapetool.url($request.page)}&name=${escapetool.url($newDocName)}&url=${escapetool.url($elink)}&editLink=${escapetool.url($entry.alternateLink)}&version=${entry.version}&mediaType=${stype}&id=${entry.id}"]] #end #end #else diff --git a/ui/src/main/resources/GoogleApps/JSExtension.xml b/ui/src/main/resources/GoogleApps/JSExtension.xml index eec8e68..3436030 100644 --- a/ui/src/main/resources/GoogleApps/JSExtension.xml +++ b/ui/src/main/resources/GoogleApps/JSExtension.xml @@ -123,21 +123,23 @@ #if($googleApps.active && $googleApps.driveEnabled) var listener = function(event) { if (event.memo.id == 'Attachments') { - var buttons = $$(".xwikibuttonlinks") - for (i=0;i<=buttons.length;i++) - { - var dlink = (buttons[i]) ? buttons[i].getElementsByClassName("deletelink") : null; - if (dlink && dlink[0]) { + var buttons = $$(".xwikibuttonlinks") + for (i=0;i<=buttons.length;i++) + { + var dlink = (buttons[i]) ? buttons[i].getElementsByClassName("deletelink") : null; + if (dlink && dlink[0]) { var dlinkurl = dlink[0].href; var dlinkpos = dlinkurl.indexOf("?"); var filename = dlinkurl.substring(dlinkurl.lastIndexOf("/", dlinkpos) + 1, dlinkpos) buttons[i].insert('<a class="editlink" href="$xwiki.getURL("GoogleApps.EditInGoogleApps")?page=' + encodeURIComponent(XWiki.currentSpace) + '.' + encodeURIComponent(XWiki.currentPage) + '&name=' + filename + '" title="$services.localization.render("googleapps.edit.editingoogleapps.link")">$services.localization.render("googleapps.edit.editingoogleapps.link")</a>') - } - } - var attachaddform = $("AddAttachment"); - attachaddform.insert('<span class="buttonwrapper" style="float: right; position: relative; top: -25px;"><a href="$xwiki.getURL("GoogleApps.ImportFromGoogleApps")?page=' + encodeURIComponent(XWiki.currentSpace) + '.' + encodeURIComponent(XWiki.currentPage) + '">$services.localization.render("googleapps.import.importfromgoogleapps")</a></span>') - document.stopObserving("xwiki:docextra:loaded", listener); - delete listener; + } + } + var attachaddform = $("AddAttachment"); + if (attachaddform) { + attachaddform.insert('<span class="buttonwrapper" style="float: right; position: relative; top: -25px;"><a href="$xwiki.getURL("GoogleApps.ImportFromGoogleApps")?page=' + encodeURIComponent(XWiki.currentSpace) + '.' + encodeURIComponent(XWiki.currentPage) + '">$services.localization.render("googleapps.import.importfromgoogleapps")</a></span>') + } + document.stopObserving("xwiki:docextra:loaded", listener); + delete listener; } }.bindAsEventListener(this); diff --git a/ui/src/main/resources/GoogleApps/RetrieveFromGoogleApps.xml b/ui/src/main/resources/GoogleApps/RetrieveFromGoogleApps.xml index fc2c9e4..e6a3d14 100644 --- a/ui/src/main/resources/GoogleApps/RetrieveFromGoogleApps.xml +++ b/ui/src/main/resources/GoogleApps/RetrieveFromGoogleApps.xml @@ -41,8 +41,10 @@ true {{velocity}} #set($googleApps = $services.googleApps) -#if($request.url) - #set($ok = $googleApps.retrieveFileFromGoogle($request.page, $request.name, $request.id, $request.url)) +#if($request.url || $request.mediaType) + #set($mediaType = $request.mediaType) + #if("$!{mediaType}"=="")#set($mediaType="unknown")#end + #set($ok = $googleApps.retrieveFileFromGoogle($request.page, $request.name, $request.id, $mediaType)) #if(!$ok) $services.localization.render("googleapps.retrieve.fail") $request.id #end diff --git a/ui/src/main/resources/GoogleApps/TestDocumentList.xml b/ui/src/main/resources/GoogleApps/TestDocumentList.xml index 8c30515..7128758 100644 --- a/ui/src/main/resources/GoogleApps/TestDocumentList.xml +++ b/ui/src/main/resources/GoogleApps/TestDocumentList.xml @@ -52,7 +52,7 @@ {{translation key='googleapps.testDocList.about'/}} #set($googleApps = $services.googleApps) -#set($doclist = $googleApps.getDocumentList()) +#set($doclist = $googleApps.listDriveDocumentsWithTypes("",10)) #foreach($item in $doclist) * [[$item.title $item.id>>$item.editLink]] #end From 8bc27e8cad12297928330cc15688273e424a1d85 Mon Sep 17 00:00:00 2001 From: Paul Libbrecht Date: Fri, 12 Jun 2020 23:04:10 +0200 Subject: [PATCH 13/17] Refactoring for complexity and syntax style. Now using a UIExtension so as to become prohibit-guest-access-compatible. paul --- .gitignore | 4 +- api/pom.xml | 13 - .../xwiki/googleapps/DriveDocMetadata.java | 136 +- .../xwiki/googleapps/GoogleAppsManager.java | 78 +- .../googleapps/GoogleAppsScriptService.java | 107 +- .../CookieAuthenticationPersistence.java | 251 +++- .../CookieAuthenticationPersistenceImpl.java | 298 ----- .../internal/GoogleAppsAuthService.java | 204 ++- .../internal/GoogleAppsAuthServiceImpl.java | 251 ---- .../internal/GoogleAppsConstants.java | 129 ++ .../internal/GoogleAppsEventListener.java | 21 +- .../internal/GoogleAppsIdentity.java | 432 ++++++ .../internal/GoogleAppsManagerImpl.java | 1182 ++--------------- .../internal/GoogleAppsXWikiObjects.java | 608 +++++++++ .../internal/GoogleDriveAccess.java | 235 ++++ .../main/resources/META-INF/components.txt | 2 - ui/pom.xml | 14 + .../resources/GoogleApps/DriveMacro.groovy | 96 ++ .../main/resources/GoogleApps/DriveMacro.xml | 99 +- .../resources/GoogleApps/DriveMacroTest.xml | 8 +- .../resources/GoogleApps/EditInGoogleApps.xml | 20 +- .../GoogleApps/GoogleAppsConfigSheet.vm | 3 + .../GoogleApps/ImportFromGoogleApps.xml | 29 +- ui/src/main/resources/GoogleApps/Install.xml | 18 +- .../main/resources/GoogleApps/JSExtension.xml | 2 +- ui/src/main/resources/GoogleApps/Login.xml | 71 - .../resources/GoogleApps/LoginExtension.xml | 248 ---- .../resources/GoogleApps/LoginUIExtension.vm | 99 ++ .../resources/GoogleApps/LoginUIExtension.xml | 127 ++ ui/src/main/resources/GoogleApps/OAuth.xml | 4 +- .../GoogleApps/RetrieveFromGoogleApps.xml | 21 +- .../resources/GoogleApps/TestDocumentList.xml | 2 +- .../GoogleApps/Translations.de.properties | 2 +- .../resources/GoogleApps/Translations.de.xml | 2 +- .../GoogleApps/Translations.fr.properties | 4 +- .../resources/GoogleApps/Translations.fr.xml | 2 +- .../GoogleApps/Translations.properties | 5 +- ui/src/main/resources/GoogleApps/WebHome.xml | 24 +- 38 files changed, 2557 insertions(+), 2294 deletions(-) delete mode 100644 api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistenceImpl.java delete mode 100644 api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthServiceImpl.java create mode 100644 api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsConstants.java create mode 100644 api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsIdentity.java create mode 100644 api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsXWikiObjects.java create mode 100644 api/src/main/java/com/xwiki/googleapps/internal/GoogleDriveAccess.java create mode 100644 ui/src/main/resources/GoogleApps/DriveMacro.groovy delete mode 100644 ui/src/main/resources/GoogleApps/Login.xml delete mode 100644 ui/src/main/resources/GoogleApps/LoginExtension.xml create mode 100644 ui/src/main/resources/GoogleApps/LoginUIExtension.vm create mode 100644 ui/src/main/resources/GoogleApps/LoginUIExtension.xml diff --git a/.gitignore b/.gitignore index b49c0bd..aa7ab62 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,6 @@ nbproject *.log.* *.log .sonar-ide.properties -.clover \ No newline at end of file +.clover +.DS_Store + diff --git a/api/pom.xml b/api/pom.xml index afc8719..3855914 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -25,7 +25,6 @@ 3.0-SNAPSHOT application-googleapps-api - 3.0-SNAPSHOT jar Google Apps Integration (API) This is the XWiki-API part of the Google Apps which allows to connect Google Apps to XWiki. @@ -59,16 +58,4 @@ compile - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - false - - - - - diff --git a/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java b/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java index 1e43603..d32f437 100644 --- a/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java +++ b/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java @@ -19,6 +19,9 @@ */ package com.xwiki.googleapps; +import java.util.LinkedList; +import java.util.List; + import org.xwiki.stability.Unstable; /** @@ -46,32 +49,155 @@ public class DriveDocMetadata public String exportLink; /** - * + * URL to use to show an embedded view. + */ + public String embedLink; + + /** + * A stringified version number. + */ + public String version; + + /** + * The name of the file in case it is an uploaded file. + */ + public String fileName; + + /** + * The email-address of the user with which this document's connection was created. + */ + public String user; + + /** + * A list of export possibilities. + */ + public List exportLinksAlternatives = new LinkedList<>(); + + /** * @return the internal Google Id of the document. */ - public String getId() { + public String getId() + { return id; } + /** + * @return the version number + */ + public String getVersion() + { + return version; + } /** * @return the URL to direct the user to for editing. */ - public String getEditLink() { + public String getEditLink() + { return editLink; } /** * @return the URL to pull from in order to fetch the document. */ - public String getExportLink() { + public String getExportLink() + { return exportLink; } + /** + * @return a list of export alternatives. + */ + public List getExportLinksAlternatives() + { + return exportLinksAlternatives; + } + + /** + * Insert one of the possible export alternative's info. + * + * @param extension understood the file type. + * @param newFileName the filename when this file is stored on a desktop with this type + * @param exportUrl the url to pull from. + */ + public void addExportAlternative(String extension, String newFileName, String exportUrl) + { + ExportAlternative ea = new ExportAlternative(); + ea.extension = extension; + ea.exportUrl = exportUrl; + ea.newFileName = newFileName; + if (ea.newFileName == null) { + ea.newFileName = "unnamed"; + } + exportLinksAlternatives.add(ea); + } + + /** + * @return the name of the file in case it is an uploaded file. + */ + public String getFileName() + { + return fileName; + } + + /** + * @return the same as {#getFileName}. + */ + public String getTitle() + { + return fileName; + } + /** * @return a useful string representation */ - public String toString() { + public String toString() + { return "id " + id + " edit: " + editLink + " export " + exportLink; } + + /** + * A class to denote export possibilities of a drive file. + */ + public static class ExportAlternative + { + /** + * a short nickname of the file type, typically the file-ending. + */ + public String extension; + + /** + * the revised filename if exported to this extension. + */ + public String newFileName; + + /** + * the URL to pull from. + */ + public String exportUrl; + + /** + * @return a short nickname of the file type, typically the file-ending. + */ + public String getExtension() + { + return extension; + } + + /** + * @return the revised filename if exported to this extension. + */ + public String getNewFileName() + { + return newFileName; + } + + /** + * @return the URL to pull from. + */ + public String getExportUrl() + { + return exportUrl; + } + } } diff --git a/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java b/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java index 13f1586..c7ee7cc 100644 --- a/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java +++ b/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java @@ -19,15 +19,11 @@ */ package com.xwiki.googleapps; -import java.util.Date; import java.util.List; -import java.util.Map; import org.xwiki.component.annotation.Role; import org.xwiki.stability.Unstable; -import com.google.api.client.auth.oauth2.Credential; -import com.google.api.services.drive.model.File; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.objects.BaseObject; @@ -63,44 +59,16 @@ public interface GoogleAppsManager @Unstable int getConfigCookiesTTL(); - /** - * Finds when the JAR file was assembled by maven. - * - * @return the build date. - * @since 3.0 - */ - @Unstable - Date getBuildTime(); - - /** - * Finds the version of the JAR file. - * - * @return the build version. - * @since 3.0 - */ - @Unstable - String getBuildVersion(); - - /** - * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. - * - * @return found credential - * @throws GoogleAppsException if a communication problem with the other components occured - * @since 3.0 - */ - @Unstable - Credential authorize() throws GoogleAppsException; - /** * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. * * @param redirect If a redirect can be done - * @return found credential + * @return if found a credential * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable - Credential authorize(boolean redirect) throws GoogleAppsException; + boolean authorize(boolean redirect) throws GoogleAppsException; /** * Performs the necessary communication with Google-Services to fetch identity and update the XWiki-user object or @@ -122,22 +90,21 @@ public interface GoogleAppsManager * @since 3.0 */ @Unstable - List listDriveDocumentsWithTypes(String query, int nbResults) throws GoogleAppsException; + List listDriveDocuments(String query, int nbResults) throws GoogleAppsException; /** * Fetches the google-drive document's representation and stores it as attachment. * - * @param page attach to this page - * @param name attach using this file name - * @param id store object attached to this attachment using this id (for later sync) - * @param mediaType content-type of the file to be fetched (or "unknown"; in this case the - * mediaType is read from Tika. - * @return true if successful + * @param page attach to this page + * @param name attach using this file name + * @param id store object attached to this attachment using this id (for later sync) + * @param mediaType content-type of the file to be fetched (or "unknown"; in this case the mediaType is read from + * Tika. * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable - boolean retrieveFileFromGoogle(String page, String name, String id, String mediaType) throws GoogleAppsException; + void retrieveFileFromGoogle(String page, String name, String id, String mediaType) throws GoogleAppsException; /** * Extracts metadata about the Google Drive document corresponding to the named attachment. @@ -149,18 +116,8 @@ public interface GoogleAppsManager * @since 3.0 */ @Unstable - DriveDocMetadata getGoogleDocument(String pageName, String fileName) throws GoogleAppsException; + DriveDocMetadata getSyncDocMetadata(String pageName, String fileName) throws GoogleAppsException; - /** - * Reads the extension and document name. - * - * @param docName the raw docName - * @param elink the link where to read the extension name - * @return an array with extension and simplified document name - * @since 3.0 - */ - @Unstable - String[] getExportLink(String docName, String elink); /** * Inserts the current information on the document to be embedded. @@ -182,21 +139,10 @@ BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, BaseObject * * @param page the XWiki page name * @param name the attachment name - * @return a record with the keys fileName, exportLink, version, editLink, embedLink, and google-user's - * email-address + * @return a metadata about the file * @throws GoogleAppsException if a communication problem with the other components occured * @since 3.0 */ @Unstable - Map saveAttachmentToGoogle(String page, String name) throws GoogleAppsException; - - /** - * Reads the google user-info attached to the current user as stored in the request. - * - * @return the google user-info with keys displayName, emails (array of type,value pairs), etag, id, image (map with - * keys isDefault and url), kind, language, name (map with keys familyName and givenName). - * @since 3.0 - */ - @Unstable - Map getGoogleUser(); + DriveDocMetadata saveAttachmentToGoogle(String page, String name) throws GoogleAppsException; } diff --git a/api/src/main/java/com/xwiki/googleapps/GoogleAppsScriptService.java b/api/src/main/java/com/xwiki/googleapps/GoogleAppsScriptService.java index 3af87f8..e81e540 100644 --- a/api/src/main/java/com/xwiki/googleapps/GoogleAppsScriptService.java +++ b/api/src/main/java/com/xwiki/googleapps/GoogleAppsScriptService.java @@ -19,9 +19,7 @@ */ package com.xwiki.googleapps; -import java.util.Date; import java.util.List; -import java.util.Map; import javax.inject.Inject; import javax.inject.Named; @@ -32,10 +30,7 @@ import org.xwiki.script.service.ScriptService; import org.xwiki.stability.Unstable; -import com.google.api.client.auth.oauth2.Credential; -import com.google.api.services.drive.model.File; import com.xpn.xwiki.XWikiContext; -import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.api.Document; import com.xpn.xwiki.api.Object; @@ -58,11 +53,10 @@ public class GoogleAppsScriptService implements ScriptService /** * @return if the application is licensed and activated - * @throws XWikiException in case a context cannot be read from thread. * @since 3.0 */ @Unstable - public boolean isActive() throws XWikiException + public boolean isActive() { return manager.isActive(); } @@ -77,51 +71,15 @@ public boolean isDriveEnabled() return manager.isDriveEnabled(); } - /** - * Finds when the JAR file was assembled by maven. - * - * @return the build date. - * @since 3.0 - */ - @Unstable - public Date getBuildTime() - { - return manager.getBuildTime(); - } - - /** - * Finds the version of the JAR file. - * - * @return the build version. - * @since 3.0 - */ - @Unstable - public String getBuildVersion() - { - return manager.getBuildVersion(); - } - - /** - * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. - * - * @return found credential - * @since 3.0 - */ - @Unstable - public Credential authorize() - { - return manager.authorize(); - } - /** * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. * * @param redirect If a redirect can be done - * @return found credential + * @return if found a credential * @since 3.0 */ @Unstable - public Credential authorize(boolean redirect) + public boolean authorize(boolean redirect) { return manager.authorize(redirect); } @@ -140,18 +98,18 @@ public String updateUser() } /** - * Fetches a list of Google Drive document matching a substring query in the filename. - * (used in the import function) + * Fetches a list of Google Drive document matching a substring query in the filename. (used in the import + * function) * * @param query the expected query (e.g. fullText contains winter ski) * @param nbResults max number of results - * @return The list of {@File} at Google Drive. + * @return The list of DriveDocMetadata * @since 3.0 */ @Unstable - public List listDriveDocumentsWithTypes(String query, int nbResults) + public List listDriveDocuments(String query, int nbResults) { - return manager.listDriveDocumentsWithTypes(query, nbResults); + return manager.listDriveDocuments(query, nbResults); } /** @@ -175,18 +133,17 @@ public Object createOrUpdateEmbedObject(String docId, Document doc, Object obj, /** * Fetches the google-drive document's representation and stores it as attachment. * - * @param page attach to this page - * @param name attach using this file name - * @param id store object attached to this attachment using this id (for later sync) - * @param mediaType content-type of the file to be fetched (or "unknown"; in this case the - * mediaType is read from Tika. - * @return true if successful + * @param page attach to this page + * @param name attach using this file name + * @param id store object attached to this attachment using this id (for later sync) + * @param mediaType content-type of the file to be fetched (or "unknown"; in this case the mediaType is read from + * Tika. * @since 3.0 */ @Unstable - public boolean retrieveFileFromGoogle(String page, String name, String id, String mediaType) + public void retrieveFileFromGoogle(String page, String name, String id, String mediaType) { - return manager.retrieveFileFromGoogle(page, name, id, mediaType); + manager.retrieveFileFromGoogle(page, name, id, mediaType); } /** @@ -195,27 +152,12 @@ public boolean retrieveFileFromGoogle(String page, String name, String id, Strin * @param pageName The XWiki page where the attachment is * @param fileName The filename of the attachment * @return information about the corresponding Google Drive document - * @throws XWikiException if something happened at XWiki side - * @since 3.0 - */ - @Unstable - public DriveDocMetadata getGoogleDocument(String pageName, String fileName) - { - return manager.getGoogleDocument(pageName, fileName); - } - - /** - * Reads the extension and document name. - * - * @param docName the raw docName - * @param elink the link where to read the extension name - * @return an array with extension and simplified document name * @since 3.0 */ @Unstable - public String[] getExportLink(String docName, String elink) + public DriveDocMetadata getSyncDocMetadata(String pageName, String fileName) { - return manager.getExportLink(docName, elink); + return manager.getSyncDocMetadata(pageName, fileName); } /** @@ -228,21 +170,8 @@ public String[] getExportLink(String docName, String elink) * @since 3.0 */ @Unstable - public Map saveAttachmentToGoogle(String page, String name) + public DriveDocMetadata saveAttachmentToGoogle(String page, String name) { return manager.saveAttachmentToGoogle(page, name); } - - /** - * Reads the google user-info attached to the current user as stored in the request. - * - * @return the google user-info with keys displayName, emails (array of type,value pairs), etag, id, image (map with - * keys isDefault and url), kind, language, name (map with keys familyName and givenName). - * @since 3.0 - */ - @Unstable - public Map getGoogleUser() - { - return manager.getGoogleUser(); - } } diff --git a/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistence.java b/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistence.java index 4f3e68b..b2d74fd 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistence.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistence.java @@ -19,37 +19,264 @@ */ package com.xwiki.googleapps.internal; -import org.xwiki.component.annotation.Role; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.SecretKeySpec; +import javax.inject.Provider; +import javax.servlet.http.Cookie; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.xwiki.configuration.ConfigurationSource; +import org.xwiki.stability.Unstable; + +import com.xpn.xwiki.XWikiContext; +import com.xwiki.googleapps.GoogleAppsException; /** - * Set of methods for the management of the cookies. + * Tools to help storing and retrieving enriched information within cookies such as the linked Google user profile. + *

+ * Inspiration: xwiki-authenticator-trusted https://github.com/xwiki-contrib/xwiki-authenticator-trusted/edit/master\ + * /xwiki-authenticator-trusted-api/src/main/java/org/xwiki/contrib/authentication\ + * /internal/CookieAuthenticationPersistenceStore.java. * * @version $Id$ * @since 3.0 */ -@Role -interface CookieAuthenticationPersistence +public class CookieAuthenticationPersistence { + private static final String AUTHENTICATION_CONFIG_PREFIX = "xwiki.authentication"; + + private static final String COOKIE_PREFIX_PROPERTY = AUTHENTICATION_CONFIG_PREFIX + ".cookieprefix"; + + private static final String COOKIE_PATH_PROPERTY = AUTHENTICATION_CONFIG_PREFIX + ".cookiepath"; + + private static final String COOKIE_DOMAINS_PROPERTY = AUTHENTICATION_CONFIG_PREFIX + ".cookiedomains"; + + private static final String ENCRYPTION_KEY_PROPERTY = AUTHENTICATION_CONFIG_PREFIX + ".encryptionKey"; + + private static final String CIPHER_ALGORITHM = "TripleDES"; + + private static final String AUTHENTICATION_COOKIE = "XWIKITRUSTEDAUTH"; + + /** + * The string used to prefix cookie domain to conform to RFC 2109. + */ + private static final String COOKIE_DOT_PFX = "."; + + private static final String EQUAL_SIGN = "="; + + private static final String UNDERSCORE = "_"; + + private final Logger logger; + + private final GoogleAppsXWikiObjects gaXwikiObjects; + + private final String cookiePrefix; + + private final String cookiePath; + + private final String[] cookieDomains; + + private final Cipher encryptionCipher; + + private final Cipher decryptionCipher; + + private final String encryptionKey; + + private final Provider contextProvider; + + /** + * Builds a configured object. + */ + + CookieAuthenticationPersistence(ConfigurationSource xwikiCfg, + GoogleAppsXWikiObjects gaXwikiObjects, Provider contextProvider, Logger logger) + { + this.cookiePrefix = xwikiCfg.getProperty(COOKIE_PREFIX_PROPERTY, ""); + this.cookiePath = xwikiCfg.getProperty(COOKIE_PATH_PROPERTY, "/"); + this.encryptionKey = xwikiCfg.getProperty(ENCRYPTION_KEY_PROPERTY); + + String[] cdlist = StringUtils.split(xwikiCfg.getProperty(COOKIE_DOMAINS_PROPERTY), ','); + + if (cdlist != null && cdlist.length > 0) { + this.cookieDomains = new String[cdlist.length]; + for (int i = 0; i < cdlist.length; ++i) { + this.cookieDomains[i] = conformCookieDomain(cdlist[i]); + } + } else { + this.cookieDomains = null; + } + + try { + encryptionCipher = getCipher(true); + decryptionCipher = getCipher(false); + } catch (Exception e) { + throw new GoogleAppsException("Unable to initialize ciphers", e); + } + + this.logger = logger; + this.gaXwikiObjects = gaXwikiObjects; + this.contextProvider = contextProvider; + } + + /** + * Erases the information stored. + * + * @since 3.0 + */ + @Unstable + void clear() + { + this.setUserId("XWikiGuest"); + } + + /** + * Retrieving the login read from the cookie. + * + * @return the login name found, or null. + * @since 3.0 + */ + @Unstable + String getUserId() + { + logger.info("retrieve cookie " + cookiePrefix + AUTHENTICATION_COOKIE); + String cookie = getCookieValue(cookiePrefix + AUTHENTICATION_COOKIE); + if (cookie != null) { + return decryptText(cookie); + } + return null; + } + + /** + * Store the user-information within the cookie. + * + * @param userUid the user-name (without xwiki. prefix) + * @since 3.0 + */ + @Unstable + void setUserId(String userUid) + { + Cookie cookie = new Cookie(cookiePrefix + AUTHENTICATION_COOKIE, encryptText(userUid)); + cookie.setMaxAge(gaXwikiObjects.getConfigCookiesTTL()); + cookie.setPath(cookiePath); + String cookieDomain = getCookieDomain(); + if (cookieDomain != null) { + cookie.setDomain(cookieDomain); + } + if (contextProvider.get().getRequest().isSecure()) { + cookie.setSecure(true); + } + contextProvider.get().getResponse().addCookie(cookie); + } + + private Cipher getCipher(boolean encrypt) + throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException + { + Cipher cipher = null; + String secretKey = encryptionKey; + if (secretKey != null) { + secretKey = secretKey.substring(0, 24); + SecretKeySpec key = new SecretKeySpec(secretKey.getBytes(), CIPHER_ALGORITHM); + cipher = Cipher.getInstance(CIPHER_ALGORITHM); + cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, key); + } + return cipher; + } + + private String encryptText(String text) + { + try { + logger.info("text to encrypt : " + text); + String encryptedText = new String(Base64.encodeBase64( + encryptionCipher.doFinal(text.getBytes()))).replaceAll(EQUAL_SIGN, UNDERSCORE); + logger.info("encrypted text : " + encryptedText); + return encryptedText; + } catch (Exception e) { + logger.error("Failed to encrypt text", e); + return null; + } + } + + private String decryptText(String text) + { + try { + logger.info("text to decrypt : " + text); + String decryptedText = new String(decryptionCipher.doFinal( + Base64.decodeBase64(text.replaceAll(UNDERSCORE, EQUAL_SIGN).getBytes( + StandardCharsets.ISO_8859_1)))); + logger.info("decrypted text : " + decryptedText); + return decryptedText; + } catch (Exception e) { + logger.error("Failed to decrypt text", e); + return null; + } + } + /** - * Reads the user-id from the cookie. + * Retrieve given cookie null-safe. * - * @return the decrypted user-id + * @param cookieName name of the cookie + * @return the cookie * @since 3.0 */ - String getUserId(); + private String getCookieValue(String cookieName) + { + if (contextProvider.get().getRequest() != null) { + Cookie cookie = contextProvider.get().getRequest().getCookie(cookieName); + if (cookie != null) { + logger.info("cookie : " + cookie); + return cookie.getValue(); + } + } + return null; + } /** - * Stores the user-id in an encryted fashion in the cookie. + * Compute the actual domain the cookie is supposed to be set for. Search through the list of generalized domains + * for a partial match. If no match is found, then no specific domain is used, which means that the cookie will be + * valid only for the requested host. * - * @param userId the string to store + * @return The configured domain generalization that matches the request, or null if no match is found. * @since 3.0 */ - void setUserId(String userId); + private String getCookieDomain() + { + String cookieDomain = null; + if (this.cookieDomains != null) { + // Conform the server name like we conform cookie domain by prefixing with a dot. + // This will ensure both localhost.localdomain and any.localhost.localdomain will match + // the same cookie domain. + String servername = conformCookieDomain(contextProvider.get().getRequest().getServerName()); + for (String domain : this.cookieDomains) { + if (servername.endsWith(domain)) { + cookieDomain = domain; + break; + } + } + } + logger.debug("Cookie domain is:" + cookieDomain); + return cookieDomain; + } /** - * Removes stored information from the cookie. + * Ensure cookie domains are prefixed with a dot to conform to RFC 2109. * + * @param domain a cookie domain. + * @return a conform cookie domain. * @since 3.0 */ - void clear(); + private String conformCookieDomain(String domain) + { + if (domain != null && !domain.startsWith(COOKIE_DOT_PFX)) { + return COOKIE_DOT_PFX.concat(domain); + } else { + return domain; + } + } } diff --git a/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistenceImpl.java b/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistenceImpl.java deleted file mode 100644 index 3855323..0000000 --- a/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistenceImpl.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * See the NOTICE file distributed with this work for additional - * information regarding copyright ownership. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ -package com.xwiki.googleapps.internal; - -import java.nio.charset.StandardCharsets; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.Cipher; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.SecretKeySpec; -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Provider; -import javax.servlet.http.Cookie; - -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; - -import com.xwiki.googleapps.GoogleAppsManager; - -import org.xwiki.component.annotation.Component; -import org.xwiki.component.annotation.InstantiationStrategy; -import org.xwiki.component.descriptor.ComponentInstantiationStrategy; -import org.xwiki.component.phase.Initializable; -import org.xwiki.component.phase.InitializationException; -import org.xwiki.configuration.ConfigurationSource; -import org.xwiki.stability.Unstable; - -import com.xpn.xwiki.XWikiContext; - -/** - * Tools to help storing and retrieving enriched information within cookies such as the linked Google user profile. - *

- * Inspiration: xwiki-authenticator-trusted https://github.com/xwiki-contrib/xwiki-authenticator-trusted/edit/master\ - * /xwiki-authenticator-trusted-api/src/main/java/org/xwiki/contrib/authentication\ - * /internal/CookieAuthenticationPersistenceStore.java. - * - * @version $Id$ - * @since 3.0 - */ -@Component -@InstantiationStrategy(ComponentInstantiationStrategy.PER_LOOKUP) -public class CookieAuthenticationPersistenceImpl implements CookieAuthenticationPersistence, Initializable -{ - private static final String AUTHENTICATION_CONFIG_PREFIX = "xwiki.authentication"; - - private static final String COOKIE_PREFIX_PROPERTY = AUTHENTICATION_CONFIG_PREFIX + ".cookieprefix"; - - private static final String COOKIE_PATH_PROPERTY = AUTHENTICATION_CONFIG_PREFIX + ".cookiepath"; - - private static final String COOKIE_DOMAINS_PROPERTY = AUTHENTICATION_CONFIG_PREFIX + ".cookiedomains"; - - private static final String ENCRYPTION_KEY_PROPERTY = AUTHENTICATION_CONFIG_PREFIX + ".encryptionKey"; - - private static final String CIPHER_ALGORITHM = "TripleDES"; - - private static final String AUTHENTICATION_COOKIE = "XWIKITRUSTEDAUTH"; - - /** - * The string used to prefix cookie domain to conform to RFC 2109. - */ - private static final String COOKIE_DOT_PFX = "."; - - private static final String EQUAL_SIGN = "="; - - private static final String UNDERSCORE = "_"; - - @Inject - private Logger logger; - - @Inject - private Provider contextProvider; - - @Inject - private GoogleAppsManager manager; - - private String cookiePrefix; - - private String cookiePath; - - private String[] cookieDomains; - - private int cookieMaxAge; - - private Cipher encryptionCipher; - - private Cipher decryptionCipher; - - @Inject - @Named("xwikicfg") - private Provider xwikicfgProvider; - - /** - * Loads the configuration. - * - * @throws InitializationException if the configuration fails loading. - */ - public void initialize() throws InitializationException - { - cookiePrefix = xwikicfgProvider.get().getProperty(COOKIE_PREFIX_PROPERTY, ""); - cookiePath = xwikicfgProvider.get().getProperty(COOKIE_PATH_PROPERTY, "/"); - - String[] cdlist = StringUtils.split( - xwikicfgProvider.get().getProperty(COOKIE_DOMAINS_PROPERTY), ','); - if (cdlist != null && cdlist.length > 0) { - this.cookieDomains = new String[cdlist.length]; - for (int i = 0; i < cdlist.length; ++i) { - cookieDomains[i] = conformCookieDomain(cdlist[i]); - } - } else { - cookieDomains = null; - } - - this.cookieMaxAge = manager.getConfigCookiesTTL(); - - try { - encryptionCipher = getCipher(true); - decryptionCipher = getCipher(false); - } catch (Exception e) { - throw new InitializationException("Unable to initialize ciphers", e); - } - } - - /** - * Erases the information stored. - * - * @since 3.0 - */ - @Unstable - public void clear() - { - cookieMaxAge = 0; - this.setUserId(this.getUserId()); - } - - /** - * Retrieving the login read from the cookie. - * - * @return the login name found, or null. - * @since 3.0 - */ - @Unstable - public String getUserId() - { - logger.info("retrieve cookie " + cookiePrefix + AUTHENTICATION_COOKIE); - String cookie = getCookieValue(cookiePrefix + AUTHENTICATION_COOKIE); - if (cookie != null) { - return decryptText(cookie); - } - return null; - } - - /** - * Store the user-information within the cookie. - * - * @param userUid the user-name (without xwiki. prefix) - * @since 3.0 - */ - @Unstable - public void setUserId(String userUid) - { - Cookie cookie = new Cookie(cookiePrefix + AUTHENTICATION_COOKIE, encryptText(userUid)); - cookie.setMaxAge(cookieMaxAge); - cookie.setPath(cookiePath); - String cookieDomain = getCookieDomain(); - if (cookieDomain != null) { - cookie.setDomain(cookieDomain); - } - if (contextProvider.get().getRequest().isSecure()) { - cookie.setSecure(true); - } - contextProvider.get().getResponse().addCookie(cookie); - } - - private Cipher getCipher(boolean encrypt) - throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException - { - Cipher cipher = null; - String secretKey = xwikicfgProvider.get().getProperty(ENCRYPTION_KEY_PROPERTY); - if (secretKey != null) { - secretKey = secretKey.substring(0, 24); - SecretKeySpec key = new SecretKeySpec(secretKey.getBytes(), CIPHER_ALGORITHM); - cipher = Cipher.getInstance(CIPHER_ALGORITHM); - cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, key); - } - return cipher; - } - - private String encryptText(String text) - { - try { - logger.info("text to encrypt : " + text); - String encryptedText = new String(Base64.encodeBase64( - encryptionCipher.doFinal(text.getBytes()))).replaceAll(EQUAL_SIGN, UNDERSCORE); - logger.info("encrypted text : " + encryptedText); - return encryptedText; - } catch (Exception e) { - logger.error("Failed to encrypt text", e); - return null; - } - } - - private String decryptText(String text) - { - try { - logger.info("text to decrypt : " + text); - String decryptedText = new String(decryptionCipher.doFinal( - Base64.decodeBase64(text.replaceAll(UNDERSCORE, EQUAL_SIGN).getBytes( - StandardCharsets.ISO_8859_1)))); - logger.info("decrypted text : " + decryptedText); - return decryptedText; - } catch (Exception e) { - logger.error("Failed to decrypt text", e); - return null; - } - } - - /** - * Retrieve given cookie null-safe. - * - * @param cookieName name of the cookie - * @return the cookie - * @since 3.0 - */ - private String getCookieValue(String cookieName) - { - if (contextProvider.get().getRequest() != null) { - Cookie cookie = contextProvider.get().getRequest().getCookie(cookieName); - if (cookie != null) { - logger.info("cookie : " + cookie); - return cookie.getValue(); - } - } - return null; - } - - /** - * Compute the actual domain the cookie is supposed to be set for. Search through the list of generalized domains - * for a partial match. If no match is found, then no specific domain is used, which means that the cookie will be - * valid only for the requested host. - * - * @return The configured domain generalization that matches the request, or null if no match is found. - * @since 3.0 - */ - private String getCookieDomain() - { - String cookieDomain = null; - if (this.cookieDomains != null) { - // Conform the server name like we conform cookie domain by prefixing with a dot. - // This will ensure both localhost.localdomain and any.localhost.localdomain will match - // the same cookie domain. - String servername = conformCookieDomain(contextProvider.get().getRequest().getServerName()); - for (String domain : this.cookieDomains) { - if (servername.endsWith(domain)) { - cookieDomain = domain; - break; - } - } - } - logger.debug("Cookie domain is:" + cookieDomain); - return cookieDomain; - } - - /** - * Ensure cookie domains are prefixed with a dot to conform to RFC 2109. - * - * @param domain a cookie domain. - * @return a conform cookie domain. - * @since 3.0 - */ - private String conformCookieDomain(String domain) - { - if (domain != null && !domain.startsWith(COOKIE_DOT_PFX)) { - return COOKIE_DOT_PFX.concat(domain); - } else { - return domain; - } - } -} diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthService.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthService.java index 6f7041f..30a775c 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthService.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthService.java @@ -19,17 +19,211 @@ */ package com.xwiki.googleapps.internal; -import org.xwiki.component.annotation.Role; +import java.net.URLEncoder; +import java.security.Principal; +import java.util.regex.Pattern; -import com.xpn.xwiki.user.api.XWikiAuthService; +import javax.servlet.http.HttpSession; + +import org.securityfilter.realm.SimplePrincipal; +import org.slf4j.Logger; +import org.xwiki.container.servlet.filters.SavedRequestManager; +import org.xwiki.model.reference.DocumentReference; +import org.xwiki.text.StringUtils; + +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.XWikiException; +import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.user.api.XWikiUser; +import com.xpn.xwiki.user.impl.xwiki.XWikiAuthServiceImpl; +import com.xpn.xwiki.web.XWikiRequest; /** - * A badge interface to denote the role of the component that will replace the default authentication-service. + * An authenticator that can include a negotiation with the Google Cloud (e.g. Google Drive) services. This + * authenticator is created, configured and maintained by the GoogleAppsScriptService. * * @version $Id$ * @since 3.0 */ -@Role -interface GoogleAppsAuthService extends XWikiAuthService +public class GoogleAppsAuthService extends XWikiAuthServiceImpl { + private static final String XWIKISPACE = "XWiki."; + + private final Logger log; + + private final GoogleAppsXWikiObjects gaXwikiObjects; + + private final CookieAuthenticationPersistence cookiePersistance; + + private final Pattern logoutRequestMatcher; + + GoogleAppsAuthService(GoogleAppsXWikiObjects gaXwikiObjects, + CookieAuthenticationPersistence cookiePersistance, + String logoutPattern, Logger log) + { + this.log = log; + this.gaXwikiObjects = gaXwikiObjects; + this.logoutRequestMatcher = Pattern.compile(logoutPattern); + this.cookiePersistance = cookiePersistance; + } + + /** + * Evaluates if the user can be authenticated based on request info such as cookies. + * + * @param context the context representign the request + * @return a valid user, if found. + * @throws XWikiException if anything went wrong + */ + public XWikiUser checkAuth(XWikiContext context) throws XWikiException + { + try { + log.info("GoogleApps authentificator - checkAuth"); + if (isLogoutRequest(context)) { + log.info("caught a logout request"); + cookiePersistance.clear(); + log.info("cleared cookie"); + } + return super.checkAuth(context); + } catch (Exception e) { + e.printStackTrace(); + throw new XWikiException(e.getMessage(), e); + } + } + + /** + * Checks authentication. + * + * @param username the name of the user to verify against + * @param password the password of the user to verify against + * @param rememberme insert-cookies to remember the login + * @param context the context containing the request + * @return an XWikiUser is it succeded. + * @throws XWikiException in case something goes wrong + */ + public XWikiUser checkAuth(String username, String password, + String rememberme, XWikiContext context) throws XWikiException + { + return super.checkAuth(username, password, rememberme, context); + } + + /** + * Redirect user to the login. + * + * @param context the xwiki-context of the request + * @throws XWikiException a wrapped exception + */ + public void showLogin(XWikiContext context) throws XWikiException + { + log.debug("GoogleApps authentificator - showLogin"); + if (!gaXwikiObjects.isActive()) { + return; + } + boolean redirected = false; + try { + String url = context.getWiki().getExternalURL("GoogleApps.Login", "view", context); + if (gaXwikiObjects.doesUseCookies() && gaXwikiObjects.doesSkipLoginPage()) { + log.info("skip the login page "); + XWikiRequest request = context.getRequest(); + String userCookie = cookiePersistance.getUserId(); + log.info("retrieved user from cookie : " + userCookie); + String savedRequestId = request.getParameter( + SavedRequestManager.getSavedRequestIdentifier()); + if (StringUtils.isEmpty(savedRequestId)) { + // Save this request + savedRequestId = SavedRequestManager.saveRequest(request); + } + String sridParameter = SavedRequestManager.getSavedRequestIdentifier() + "=" + savedRequestId; + + StringBuilder redirectBack = new StringBuilder(request.getRequestURI()); + redirectBack.append('?'); + String delimiter = ""; + if (StringUtils.isNotEmpty(request.getQueryString())) { + redirectBack.append(request.getQueryString()); + delimiter = "&"; + } + if (!request.getParameterMap().containsKey(SavedRequestManager.getSavedRequestIdentifier())) { + redirectBack.append(delimiter); + redirectBack.append(sridParameter); + } + + String finalURL = url + "?" + sridParameter + "&xredirect=" + + URLEncoder.encode(redirectBack.toString(), "UTF-8"); + log.info("Redirecting to " + finalURL); + redirected = true; + context.getResponse().sendRedirect(finalURL); + } + } catch (Exception e) { + log.error("Exception in showLogin : " + e); + } finally { + if (!redirected) { + super.showLogin(context); + } + log.info("GoogleApps authentificator - showLogin end"); + } + } + + /** + * Processes a password entry and creates the appropriate principal. + * + * @param username the provided user-name + * @param password the provided password + * @param context the context describing the request + * @return a null Principal Object if the user hasn't been authenticated or a valid Principal Object if the user is + * correctly authenticated + * @throws XWikiException if something goes wrong. + */ + public Principal authenticate(String username, String password, XWikiContext context) throws XWikiException + { + try { + log.info("GoogleApps authentificator - authenticate"); + + // case of a too early call or deactivated... can only count on local users + if (!gaXwikiObjects.isActive()) { + return super.authenticate(username, password, context); + } + + HttpSession session = context.getRequest().getSession(); + String xwikiUser = (String) session.getAttribute("googleappslogin"); + log.info("xwikiUser from session : " + xwikiUser); + // get configuration for authentification with cookies + + // authenticate user from cookie value + if (xwikiUser == null && gaXwikiObjects.doesUseCookies() + && gaXwikiObjects.doesAuthWithCookies()) + { + log.info("Authenticate with cookie"); + String userCookie = cookiePersistance.getUserId(); + if (userCookie != null) { + log.info("Found user from cookie : " + userCookie); + DocumentReference userDocRef = gaXwikiObjects.createUserReference(username); + XWikiDocument userDoc = context.getWiki().getDocument(userDocRef, context); + if (!userDoc.isNew()) { + xwikiUser = userDocRef.getName(); + } + log.info("xwikiUser from cookie : " + xwikiUser); + } + } + if (xwikiUser != null) { + if (!xwikiUser.startsWith(XWIKISPACE)) { + xwikiUser = XWIKISPACE + xwikiUser; + } + log.info("Authenticating user " + xwikiUser); + return new SimplePrincipal(xwikiUser); + } else { + log.info("use default authenticate method for user : " + username); + return super.authenticate(username, password, context); + } + } catch (Exception e) { + e.printStackTrace(); + throw new XWikiException("Trouble at authenticating", e); + } + } + + /** + * @return true if the current request match the configured logout page pattern. + */ + private boolean isLogoutRequest(XWikiContext context) + { + return logoutRequestMatcher.matcher(context.getRequest().getPathInfo()).matches(); + } } diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthServiceImpl.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthServiceImpl.java deleted file mode 100644 index 5a68506..0000000 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthServiceImpl.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * See the NOTICE file distributed with this work for additional - * information regarding copyright ownership. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ -package com.xwiki.googleapps.internal; - -import java.net.URLEncoder; -import java.security.Principal; -import java.util.regex.Pattern; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Provider; -import javax.servlet.http.HttpSession; - -import org.securityfilter.realm.SimplePrincipal; -import org.slf4j.Logger; -import org.xwiki.component.annotation.Component; -import org.xwiki.component.annotation.InstantiationStrategy; -import org.xwiki.component.descriptor.ComponentInstantiationStrategy; -import org.xwiki.component.manager.ComponentManager; -import org.xwiki.configuration.ConfigurationSource; -import org.xwiki.container.servlet.filters.SavedRequestManager; -import org.xwiki.model.reference.DocumentReference; -import org.xwiki.text.StringUtils; - -import com.xpn.xwiki.XWikiContext; -import com.xpn.xwiki.XWikiException; -import com.xpn.xwiki.doc.XWikiDocument; -import com.xpn.xwiki.user.api.XWikiUser; -import com.xpn.xwiki.user.impl.xwiki.XWikiAuthServiceImpl; -import com.xpn.xwiki.web.XWikiRequest; -import com.xwiki.googleapps.GoogleAppsManager; - -/** - * An authenticator that can include a negotiation with the Google Cloud (e.g. Google Drive) services. This - * authenticator is created, configured and maintained by the GoogleAppsScriptService. - * - * @version $Id$ - * @since 3.0 - */ -@Component -@InstantiationStrategy(ComponentInstantiationStrategy.PER_LOOKUP) -public class GoogleAppsAuthServiceImpl extends XWikiAuthServiceImpl - implements GoogleAppsAuthService -{ - private static final String XWIKISPACE = "XWiki."; - - @Inject - private Logger log; - - @Inject - private ComponentManager componentManager; - - @Inject - private Provider googleAppsManagerProvider; - - @Inject - @Named("xwikicfg") - private Provider xwikicfgProvider; - - private Pattern logoutRequestMatcher; - - /** - * Evaluates if the user can be authenticated based on request info such as cookies. - * - * @param context the context representign the request - * @return a valid user, if found. - * @throws XWikiException if anything went wrong - */ - public XWikiUser checkAuth(XWikiContext context) throws XWikiException - { - try { - log.info("GoogleApps authentificator - checkAuth"); - if (isLogoutRequest(context)) { - log.info("caught a logout request"); - CookieAuthenticationPersistence cookieTools = - componentManager.getInstance(CookieAuthenticationPersistence.class); - cookieTools.clear(); - log.info("cleared cookie"); - } - return super.checkAuth(context); - } catch (Exception e) { - e.printStackTrace(); - throw new XWikiException(e.getMessage(), e); - } - } - - /** - * Checks authentication. - * - * @param username the name of the user to verify against - * @param password the password of the user to verify against - * @param rememberme insert-cookies to remember the login - * @param context the context containing the request - * @return an XWikiUser is it succeded. - * @throws XWikiException in case something goes wrong - */ - public XWikiUser checkAuth(String username, String password, - String rememberme, XWikiContext context) throws XWikiException - { - return super.checkAuth(username, password, rememberme, context); - } - - /** - * Redirect user to the login. - * - * @param context the xwiki-context of the request - * @throws XWikiException a wrapped exception - */ - public void showLogin(XWikiContext context) throws XWikiException - { - log.info("GoogleApps authentificator - showLogin"); - if (!googleAppsManagerProvider.get().isActive()) { - return; - } - boolean redirected = false; - try { - String url = context.getWiki().getExternalURL("GoogleApps.Login", "view", context); - GoogleAppsManagerImpl manager = (GoogleAppsManagerImpl) googleAppsManagerProvider.get(); - if (manager.useCookies() && manager.skipLoginPage()) { - log.info("skip the login page "); - XWikiRequest request = context.getRequest(); - CookieAuthenticationPersistence cookieTools = - componentManager.getInstance(CookieAuthenticationPersistence.class); - String userCookie = cookieTools.getUserId(); - log.info("retrieved user from cookie : " + userCookie); - String savedRequestId = request.getParameter( - SavedRequestManager.getSavedRequestIdentifier()); - if (StringUtils.isEmpty(savedRequestId)) { - // Save this request - savedRequestId = SavedRequestManager.saveRequest(request); - } - String sridParameter = SavedRequestManager.getSavedRequestIdentifier() + "=" + savedRequestId; - - StringBuilder redirectBack = new StringBuilder(request.getRequestURI()); - redirectBack.append('?'); - String delimiter = ""; - if (StringUtils.isNotEmpty(request.getQueryString())) { - redirectBack.append(request.getQueryString()); - delimiter = "&"; - } - if (!request.getParameterMap().containsKey(SavedRequestManager.getSavedRequestIdentifier())) { - redirectBack.append(delimiter); - redirectBack.append(sridParameter); - } - - String finalURL = url + "?" + sridParameter + "&xredirect=" - + URLEncoder.encode(redirectBack.toString(), "UTF-8"); - log.info("Redirecting to " + finalURL); - redirected = true; - context.getResponse().sendRedirect(finalURL); - } - } catch (Exception e) { - log.error("Exception in showLogin : " + e); - } finally { - if (!redirected) { - super.showLogin(context); - } - log.info("GoogleApps authentificator - showLogin end"); - } - } - - /** - * Processes a password entry and creates the appropriate principal. - * - * @param username the provided user-name - * @param password the provided password - * @param context the context describing the request - * @return a null Principal Object if the user hasn't been authenticated or a valid Principal Object if the user is - * correctly authenticated - * @throws XWikiException if something goes wrong. - */ - public Principal authenticate(String username, String password, XWikiContext context) throws XWikiException - { - try { - log.info("GoogleApps authentificator - authenticate"); - - // case of a too early call or deactivated... can only count on local users - GoogleAppsManagerImpl googleAppsManager = (GoogleAppsManagerImpl) googleAppsManagerProvider.get(); - if (googleAppsManager == null || !googleAppsManager.isActive()) { - return super.authenticate(username, password, context); - } - - HttpSession session = context.getRequest().getSession(); - String xwikiUser = (String) session.getAttribute("googleappslogin"); - log.info("xwikiUser from session : " + xwikiUser); - // get configuration for authentification with cookies - - // authenticate user from cookie value - if (xwikiUser == null && googleAppsManager.useCookies() && googleAppsManager.authWithCookies()) { - log.info("Authenticate with cookie"); - CookieAuthenticationPersistence cookieTools = - componentManager.getInstance(CookieAuthenticationPersistence.class); - String userCookie = cookieTools.getUserId(); - if (userCookie != null) { - log.info("Found user from cookie : " + userCookie); - DocumentReference userDocRef = googleAppsManager.createUserReference(username); - XWikiDocument userDoc = context.getWiki().getDocument(userDocRef, context); - if (!userDoc.isNew()) { - xwikiUser = userDocRef.getName(); - } - log.info("xwikiUser from cookie : " + xwikiUser); - } - } - if (xwikiUser != null) { - if (!xwikiUser.startsWith(XWIKISPACE)) { - xwikiUser = XWIKISPACE + xwikiUser; - } - log.info("Authenticating user " + xwikiUser); - return new SimplePrincipal(xwikiUser); - } else { - log.info("use default authenticate method for user : " + username); - return super.authenticate(username, password, context); - } - } catch (Exception e) { - e.printStackTrace(); - throw new XWikiException("Trouble at authenticating", e); - } - } - - /** - * @return true if the current request match the configured logout page pattern. - */ - private boolean isLogoutRequest(XWikiContext context) - { - if (logoutRequestMatcher == null) { - if (xwikicfgProvider == null) { - return false; - } - String patt = xwikicfgProvider.get().getProperty("xwiki.authentication.logoutpage"); - logoutRequestMatcher = Pattern.compile(patt); - } - return logoutRequestMatcher.matcher(context.getRequest().getPathInfo()).matches(); - } -} diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsConstants.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsConstants.java new file mode 100644 index 0000000..4ba0a37 --- /dev/null +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsConstants.java @@ -0,0 +1,129 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.googleapps.internal; + +/** A set of string constants used across the classes of the app. + * @version $Id$ + * @since 3.0 + */ +public interface GoogleAppsConstants +{ + /** + * avatar. + */ + String AVATAR = "avatar"; + /** + * user. + */ + String USER = "user"; + /** + * GoogleApps. + */ + String SPACENAME = "GoogleApps"; + /** + * view. + */ + String VIEWACTION = "view"; + /** + * xwiki. + */ + String WIKINAME = "xwiki"; + /** + * id. + */ + String ID = "id"; + /** + * fileName. + */ + String FILENAME = "fileName"; + /** + * version. + */ + String VERSION = "version"; + /** + * url. + */ + String URL = "url"; + /** + * exportLink. + */ + String EXPORTLINK = "exportLink"; + /** + * editLink. + */ + String EDITLINK = "editLink"; + /** + * embedLink. + */ + String EMBEDLINK = "embedLink"; + /** + * Comment used when saving a google-document's information. + */ + String UPDATECOMMENT = "Updated Google Apps Document metadata"; + /** + * exportFormat=. + */ + String EXPORTFORMATEQ = "exportFormat="; + /** + * The name of the XWiki space. + */ + String XWIKISPACE = "XWiki"; + /** + * The name of the XWiki login page. + */ + String XWIKILOGIN = "XWikiLogin"; + /** + * The name of the guest user in XWiki. + */ + String XWIKIGUEST = "XWikiGuest"; + /** + * auto. + */ + String AUTOAPPROVAL = "auto"; + /** + * email. + */ + String EMAIL = "email"; + /** + * password. + */ + String PASSWORD = "password"; + /** + * first_name. + */ + String FIRSTNAME = "first_name"; + /** + * last_name. + */ + String LASTNAME = "last_name"; + /** + * OAuth. + */ + String OAUTH = "OAuth"; + /** + * "failed login": a constant sent to the UI to indicate a failed login. + */ + String FAILEDLOGIN = "failed login"; + /** + * "no user": a constant sent to the UI to indicate that Google could not give us a user, e.g. because the user + * rejected the authorization request. + */ + String NOUSER = "no user"; +} diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java index 12cb512..e000f07 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java @@ -24,7 +24,6 @@ import org.xwiki.bridge.event.ApplicationReadyEvent; import org.xwiki.bridge.event.DocumentUpdatedEvent; -import org.xwiki.component.phase.InitializationException; import org.xwiki.model.reference.DocumentReference; import org.xwiki.observation.EventListener; import org.xwiki.observation.event.Event; @@ -41,11 +40,11 @@ @Unstable class GoogleAppsEventListener implements EventListener { - private GoogleAppsManagerImpl manager; + private final GoogleAppsXWikiObjects gaXWikiObjects; - GoogleAppsEventListener(GoogleAppsManagerImpl manager) + GoogleAppsEventListener(GoogleAppsXWikiObjects gaXWikiObjects) { - this.manager = manager; + this.gaXWikiObjects = gaXWikiObjects; } /** @@ -88,22 +87,14 @@ public void onEvent(Event event, Object source, Object data) } if (event instanceof DocumentUpdatedEvent) { XWikiDocument document = (XWikiDocument) source; - DocumentReference configDocRef = manager.getConfigDocRef(); + DocumentReference configDocRef = gaXWikiObjects.getConfigDocRef(); if (document != null && document.getDocumentReference().equals(configDocRef)) { configChanged = true; } } - if (configChanged) { - manager.readConfigDoc(null); - } - - if (applicationStarted || configChanged) { - try { - manager.initialize(); - } catch (InitializationException e) { - e.printStackTrace(); - } + if (configChanged || applicationStarted) { + gaXWikiObjects.restart(); } } } diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsIdentity.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsIdentity.java new file mode 100644 index 0000000..8eff5c0 --- /dev/null +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsIdentity.java @@ -0,0 +1,432 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.googleapps.internal; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.inject.Provider; +import javax.servlet.http.HttpSession; + +import org.slf4j.Logger; +import org.xwiki.model.reference.DocumentReference; +import org.xwiki.stability.Unstable; + +import com.google.api.client.auth.oauth2.Credential; +import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; +import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeRequestUrl; +import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse; +import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.client.util.store.FileDataStoreFactory; +import com.google.api.services.drive.DriveScopes; +import com.google.api.services.people.v1.PeopleService; +import com.google.api.services.people.v1.PeopleServiceScopes; +import com.google.api.services.people.v1.model.EmailAddress; +import com.google.api.services.people.v1.model.Person; +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.web.XWikiRequest; +import com.xwiki.googleapps.GoogleAppsException; + +class GoogleAppsIdentity implements GoogleAppsConstants +{ + /** + * A map of hash to full redirects. + */ + private final Map storedStates = new HashMap<>(); + + private final Logger log; + + private final Provider contextProvider; + + private final GoogleAppsXWikiObjects gaXwikiObjects; + + private final CookieAuthenticationPersistence cookiePersistence; + + private final JacksonFactory jacksonFactory; + + private final NetHttpTransport httpTransport; + + private FileDataStoreFactory dsFactory; + + GoogleAppsIdentity(Provider contextProvider, + GoogleAppsXWikiObjects gaXwikiObjects, + CookieAuthenticationPersistence cookiePersistence, + Logger log) + { + try { + this.contextProvider = contextProvider; + this.gaXwikiObjects = gaXwikiObjects; + this.jacksonFactory = JacksonFactory.getDefaultInstance(); + this.httpTransport = GoogleNetHttpTransport.newTrustedTransport(); + this.cookiePersistence = cookiePersistence; + this.log = log; + } catch (Exception e) { + e.printStackTrace(); + throw new GoogleAppsException("Trouble at constructing GoogleAppsIdentity", e); + } + } + + String updateUser() + { + try { + if (!gaXwikiObjects.isActive()) { + return FAILEDLOGIN; + } + log.debug("Updating user..."); + Credential credential = authorize(true); + + Person gUser = null; + if (credential != null) { + PeopleService pservice = new PeopleService.Builder(httpTransport, + jacksonFactory, credential) + .setApplicationName(gaXwikiObjects.getConfigAppName()) + .build(); + gUser = pservice.people().get("people/me").setPersonFields("emailAddresses,names,photos").execute(); + // GOOGLEAPPS: User: [displayName:..., emails:[[type:account, value:...]], etag:"...", + // id:...., image:[isDefault:false, url:https://...], kind:plus#person, language:en, + // name:[familyName:..., givenName:...]] + } + log.debug("user on google: " + gUser); + if (gUser == null) { + return NOUSER; + } + + String xwikiUser; + List emails = new ArrayList<>(); + // grab emailaddresses + if (gUser.getEmailAddresses() != null) { + for (EmailAddress address : gUser.getEmailAddresses()) { + emails.add(address.getValue()); + } + } + String email = checkDomain(emails); + if (email == null) { + return FAILEDLOGIN; + } + + String firstName = null; + String lastName = null; + if (gUser.getNames() != null) { + firstName = gUser.getNames().get(0).getGivenName(); + lastName = gUser.getNames().get(0).getFamilyName(); + } + + String googleUserId = (String) gUser.get("resourceName"); + + String photoUrl = extractPhotoUrl(gUser); + xwikiUser = gaXwikiObjects.updateXWikiUser(googleUserId, emails, email, + firstName, lastName, photoUrl); + + // we need to restore the credentials as the user will now be logged-in + storeCredentials(xwikiUser, credential); + + // store the validated xwiki user for the authentication module + // TODO: discuss: Is this not a security risk? + contextProvider.get().getRequest().getSession().setAttribute("googleappslogin", xwikiUser); + return "ok"; + } catch (Exception e) { + log.warn("Problem at updateUser", e); + return NOUSER; + } + } + + private String checkDomain(List emails) + { + String email = null; + String domain = gaXwikiObjects.getConfigDomain(); + if (domain != null && domain.length() > 0) { + domain = domain.trim(); + for (String address : emails) { + if (address.endsWith(domain)) { + email = address; + break; + } + } + if (email == null) { + String userId = getCurrentXWikiUserName(); + getCredentialStore().remove(userId); + log.debug("Wrong domain: Removed credentials for userid " + userId); + return null; + } + } + return emails.isEmpty() ? null : emails.get(0); + } + + private String extractPhotoUrl(Person gUser) + { + if (gaXwikiObjects.getConfigScopeUseAvatar() + && gUser.getPhotos() != null + && gUser.getPhotos().size() > 0 + && gUser.getPhotos().get(0).getUrl() != null) + { + String photoUrl = gUser.getPhotos().get(0).getUrl(); + log.debug("Avatar " + photoUrl); + return photoUrl; + } + return null; + } + + private String getOAuthUrl() + { + try { + XWikiContext context = contextProvider.get(); + + DocumentReference loginPage = new DocumentReference(context.getWikiId(), + XWIKISPACE, XWIKILOGIN); + String u = context.getWiki().getDocument(loginPage, context).getExternalURL("login", + "googleLogin=oauthReturn", context); + return u; + } catch (Exception e) { + throw new GoogleAppsException("Trouble at getting OAuth URL", e); + } + } + + private String getCurrentXWikiUserName() + { + DocumentReference userDoc = contextProvider.get().getUserReference(); + String uName = userDoc == null ? XWIKIGUEST : userDoc.getName(); + if (XWIKIGUEST.equals(uName)) { + uName = uName + "-" + contextProvider.get().getRequest().getSession().hashCode(); + } + return uName; + } + + /** + * Build flow and trigger user authorization request. + * + * @return the configured flow + * @throws GoogleAppsException in case something can't be built + */ + private GoogleAuthorizationCodeFlow getFlow() + { + try { + if (dsFactory == null) { + dsFactory = + new FileDataStoreFactory(gaXwikiObjects.getPermanentDir()); + } + + // create scopes from config + List gScopes = new ArrayList<>(); + gScopes.add(PeopleServiceScopes.USERINFO_EMAIL); + gScopes.add(PeopleServiceScopes.USERINFO_PROFILE); + if (gaXwikiObjects.doesConfigScopeUseDrive()) { + gScopes.add(DriveScopes.DRIVE); + } + + // create flow + return new GoogleAuthorizationCodeFlow.Builder( + httpTransport, + jacksonFactory, gaXwikiObjects.getConfigClientId(), + gaXwikiObjects.getConfigClientSecret(), gScopes) + .setDataStoreFactory(dsFactory) + .setAccessType("online").setApprovalPrompt(AUTOAPPROVAL) + .setClientId(gaXwikiObjects.getConfigClientId()) + .build(); + } catch (Exception e) { + e.printStackTrace(); + throw new GoogleAppsException("Issue at building Google Authorization Flow.", e); + } + } + + /** + * Exchange an authorization code for OAuth 2.0 credentials. + * + * @param authorizationCode Authorization code to exchange for OAuth 2.0 credentials. + * @return OAuth 2.0 credentials. + */ + private Credential exchangeCode(String authorizationCode) + { + try { + GoogleAuthorizationCodeFlow flow = getFlow(); + GoogleTokenResponse tokenResponse = flow + .newTokenRequest(authorizationCode) + .setRedirectUri(getOAuthUrl()) + .execute(); + log.debug("Token: " + tokenResponse); + return flow.createAndStoreCredential(tokenResponse, getCurrentXWikiUserName()); + } catch (Exception ex) { + throw new GoogleAppsException("Trouble at exchanging authorization code", ex); + } + } + + private Map getCredentialStore() + { + final String key = "GoogleAppsCredentialStore"; + HttpSession session = contextProvider.get().getRequest().getSession(true); + Map store = (Map) (session.getAttribute(key)); + if (store == null) { + store = new HashMap<>(); + session.setAttribute(key, store); + } + return store; + } + + private void storeCredentials(String userId, Credential credentials) + { + try { + if (userId.contains(XWIKIGUEST)) { + if (gaXwikiObjects.doesUseCookies()) { + cookiePersistence.setUserId(userId); + } + } + log.debug("Storing credentials for user " + userId + " (" + credentials + ")."); + getCredentialStore().put(userId, credentials); + } catch (Exception e) { + e.printStackTrace(); + throw new GoogleAppsException("Issue at storing credential.", e); + } + } + + private Credential getStoredCredentials(String userId) + { + if (userId == null) { + return null; + } + log.debug("Getting credentials for user " + userId); + return getCredentialStore().get(userId); + } + + /** + * Retrieve credentials using the provided authorization code. + *

+ * This function exchanges the authorization code for an access token and queries the UserInfo API to retrieve the + * user's e-mail address. If a refresh token has been retrieved along with an access token, it is stored in the + * application database using the user's e-mail address as key. If no refresh token has been retrieved, the function + * checks in the application database for one and returns it if found or throws a NoRefreshTokenException with the + * authorization URL to redirect the user to. + * + * @param authorizationCode Authorization code to use to retrieve an access token. + * @return OAuth 2.0 credentials instance containing an access and refresh token. + * @throws GoogleAppsException Unable to load client_secret.json. + */ + private Credential retrieveCredentials(String authorizationCode, boolean redirect) + { + try { + Credential credentials; + String user = getCurrentXWikiUserName(); + + if (authorizationCode != null && authorizationCode.length() > 0) { + log.debug("Trying to get credentials from authorization code: " + authorizationCode); + credentials = exchangeCode(authorizationCode); + if (credentials != null) { + if (credentials.getRefreshToken() != null) { + log.debug("Refresh token has been created."); + } else { + log.debug("Failure to create refresh token"); + } + storeCredentials(user, credentials); + return credentials; + } + } + + log.debug("No credentials found. Checking stored credentials for user " + user); + credentials = getStoredCredentials(user); + if (credentials != null) { + log.debug("Retrieved stored credentials"); + return credentials; + } + log.debug("Could not find stored credentials"); + + log.debug("No credentials retrieved."); + // No refresh token has been retrieved. + if (redirect) { + log.debug("Redirecting to authorization URL."); + contextProvider.get().getResponse().sendRedirect(getAuthorizationURL()); + } + return null; + } catch (Exception e) { + throw new GoogleAppsException("Trouble at retrieving credentials", e); + } + } + + private String getAuthorizationURL() + { + try { + String state = ""; + XWikiContext context = contextProvider.get(); + XWikiRequest request = context.getRequest(); + DocumentReference ref = context.getDoc().getDocumentReference(); + if (!(XWIKILOGIN.equals(ref.getName()) && XWIKISPACE.equals(ref.getLastSpaceReference().getName()))) { + + String finalRedirect = context.getDoc().getURL(VIEWACTION, request.getQueryString(), context); + state = Integer.toHexString(finalRedirect.hashCode()); + storedStates.put(state, finalRedirect); + } + + GoogleAuthorizationCodeRequestUrl urlBuilder = getFlow() + .newAuthorizationUrl() + .setRedirectUri(getOAuthUrl()) + .setState(state).setClientId(gaXwikiObjects.getConfigClientId()) + .setAccessType("offline").setApprovalPrompt(AUTOAPPROVAL); + // Add user email to filter account if the user is logged with multiple account + if (gaXwikiObjects.doesUseCookies()) { + String userId = cookiePersistence.getUserId(); + if (userId != null) { + String userEmail = gaXwikiObjects.getUserEmail(userId); + if (userEmail != null) { + urlBuilder = urlBuilder.set("login_hint", userEmail); + } + } + } + String authurl = urlBuilder.build(); + log.debug("google authentication url : " + authurl); + return authurl; + } catch (Exception ex) { + throw new GoogleAppsException("trouble at getAuthorizationURL", ex); + } + } + + /** + * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. + * + * @param redirect If a redirect can be done + * @return found credential + * @since 3.0 + */ + @Unstable + Credential authorize(boolean redirect) + { + try { + log.debug("In authorize"); + // TODO: useless? GoogleAuthorizationCodeFlow flow = + getFlow(); + XWikiContext context = contextProvider.get(); + XWikiRequest request = context.getRequest(); + String state = request.getParameter("code"); + Credential creds = retrieveCredentials(state, redirect); + log.debug("Got credentials: " + creds); + if (state != null && state.length() > 0) { + String url = storedStates.get(state); + if (url != null) { + log.debug("Redirecting to final destination after authorization: " + url); + context.getResponse().sendRedirect(url); + } + } + return creds; + } catch (Exception e) { + log.warn("Trouble in authorize", e); + throw new GoogleAppsException(e); + } + } +} diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java index b5563c4..9eea849 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java @@ -19,80 +19,41 @@ */ package com.xwiki.googleapps.internal; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; +import java.io.File; import java.util.List; -import java.util.Map; -import java.util.jar.Attributes; -import java.util.jar.Manifest; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; import javax.inject.Singleton; -import javax.servlet.http.HttpSession; -import org.apache.commons.httpclient.util.DateUtil; -import org.apache.commons.lang.RandomStringUtils; -import org.apache.http.HttpEntity; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.tika.Tika; import org.slf4j.Logger; import org.xwiki.component.annotation.Component; -import org.xwiki.component.manager.ComponentLookupException; -import org.xwiki.component.manager.ComponentManager; import org.xwiki.component.phase.Disposable; import org.xwiki.component.phase.Initializable; -import org.xwiki.component.phase.InitializationException; +import org.xwiki.configuration.ConfigurationSource; import org.xwiki.environment.Environment; -import org.xwiki.model.reference.DocumentReference; import org.xwiki.model.reference.DocumentReferenceResolver; -import org.xwiki.model.reference.ObjectReference; import org.xwiki.observation.ObservationManager; -import org.xwiki.query.Query; -import org.xwiki.query.QueryException; import org.xwiki.query.QueryManager; import org.xwiki.stability.Unstable; -import com.google.api.client.auth.oauth2.Credential; -import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; -import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeRequestUrl; -import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse; -import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; -import com.google.api.client.http.InputStreamContent; -import com.google.api.client.http.javanet.NetHttpTransport; -import com.google.api.client.json.jackson2.JacksonFactory; -import com.google.api.client.util.store.FileDataStoreFactory; -import com.google.api.services.drive.Drive; -import com.google.api.services.drive.DriveScopes; -import com.google.api.services.drive.model.File; -import com.google.api.services.drive.model.FileList; -import com.google.api.services.people.v1.PeopleService; -import com.google.api.services.people.v1.PeopleServiceScopes; -import com.google.api.services.people.v1.model.EmailAddress; -import com.google.api.services.people.v1.model.Person; import com.xpn.xwiki.XWiki; import com.xpn.xwiki.XWikiContext; -import com.xpn.xwiki.XWikiException; -import com.xpn.xwiki.doc.XWikiAttachment; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.objects.BaseObject; -import com.xpn.xwiki.web.XWikiRequest; -import com.xpn.xwiki.web.XWikiResponse; import com.xwiki.googleapps.DriveDocMetadata; import com.xwiki.googleapps.GoogleAppsException; import com.xwiki.googleapps.GoogleAppsManager; /** - * Set of methods accessible to the scripts using the GoogleApps functions. + * Set of methods accessible to the scripts using the GoogleApps functions. The manager is the entry point for the set + * of classes of the GoogleApps functions. + *

+ * Just as other classes of this package, it is initialized as a component and thus injected with environment objects + * such as the logger or context-provider. It is then started at its first invocation and starts the connected + * components. This class, exposed through its interface {@GoogleAppsManager} and the authenticator functions in + * {@GoogleAppsAuthServiceImpl} is the only one exposing public APIs. * * @version $Id$ * @since 3.0 @@ -100,67 +61,22 @@ @Component @Singleton public class GoogleAppsManagerImpl - implements GoogleAppsManager, Initializable, Disposable + implements GoogleAppsManager, Initializable, Disposable, GoogleAppsConstants { - private static final String AVATAR = "avatar"; + // initialisation state - private static final String SPACENAME = "GoogleApps"; + private LifeCycle lifeCycleState = LifeCycle.CONSTRUCTED; - private static final String VIEWACTION = "view"; + private GoogleAppsIdentity gaIdentity; - private static final String WIKINAME = "xwiki"; + private GoogleAppsXWikiObjects gaXWikiObjects; - private static final String XWIKISPACE = "XWiki"; - - private static final String XWIKIGUEST = "XWikiGuest"; - - private static final String AUTOAPPROVAL = "auto"; - - private static final String EMAIL = "email"; - - private static final String PASSWORD = "password"; - - private static final String FIRSTNAME = "first_name"; - - private static final String LASTNAME = "last_name"; - - private static final String OAUTH = "OAuth"; - - private static final String FAILEDLOGIN = "failed login"; - - private static final String NOUSER = "no user"; - - private static final String USER = "user"; - - private static final String GOOGLEUSERATT = "googleUser"; - - private static final String ID = "id"; - - private static final String FILENAME = "fileName"; - - private static final String VERSION = "version"; - - private static final String URL = "url"; - - private static final String EXPORTLINK = "exportLink"; - - private static final String EDITLINK = "editLink"; - - private static final String EMBEDLINK = "embedLink"; - - private static final String UPDATECOMMENT = "Updated Google Apps Document metadata"; - - private static final String EXPORTFORMATEQ = "exportFormat="; + private GoogleDriveAccess gaDriveAccess; + // ------ services from the environment @Inject private Provider xwikiContextProvider; - @Inject - private QueryManager queryManager; - - @Inject - private Environment environment; - @Inject @Named("current") private DocumentReferenceResolver documentResolver; @@ -172,106 +88,98 @@ public class GoogleAppsManagerImpl @Inject private Logger log; - @Inject - private ComponentManager componentManager; - @Inject private ObservationManager observationManager; - private GoogleAppsAuthServiceImpl authService; - - private DocumentReference configDocRef; - - private ObjectReference configObjRef; - - private GoogleAppsEventListener eventListener; - - /** - * A map of hash to full redirects. - */ - private Map storedStates = new HashMap<>(); - - private FileDataStoreFactory dsFactory; - - private JacksonFactory jacksonFactory; - - private NetHttpTransport httpTransport; - - private CloseableHttpClient httpclient = HttpClients.createDefault(); - - private Boolean configActiveFlag; - - private Boolean useCookies; - - private Boolean skipLoginPage; - - private Boolean authWithCookies; + @Inject + private QueryManager queryManager; - private String configAppName; + @Inject + private Environment environment; - private String configClientId; + @Inject + @Named("xwikicfg") + private Provider xwikiCfgProvider; - private String configClientSecret; + @Override + public void initialize() + { + log.info("GoogleAppsScriptService initializing (but not yet starting)."); + updateLifeCycle(LifeCycle.INITIALIZED); + } - private String configDomain; + // ------ own objects - private Boolean configScopeUseAvatar; + private void updateLifeCycle(LifeCycle lf) + { + log.info("Lifecycle to " + lf); + lifeCycleState = lf; + } - private Boolean configScopeUseDrive; + private void startIfNeedBe() + { + if (lifeCycleState == LifeCycle.RUNNING) { + return; + } + if (lifeCycleState != LifeCycle.INITIALIZED) { + throw new IllegalStateException("Can't start when in state " + lifeCycleState + "!"); + } + updateLifeCycle(LifeCycle.STARTING); + boolean failed = false; - private Integer configCookiesTTL; + try { + File permanentDir = new File(environment.getPermanentDirectory(), SPACENAME); + gaXWikiObjects = new GoogleAppsXWikiObjects(log, xwikiContextProvider, queryManager, + documentResolver, userResolver, permanentDir); + gaXWikiObjects.startIfNeedBe(observationManager); + + // ----- shared communication tools + ConfigurationSource xwikiCfg = xwikiCfgProvider.get(); + CookieAuthenticationPersistence cookiePersistence = new CookieAuthenticationPersistence( + xwikiCfg, gaXWikiObjects, xwikiContextProvider, log); + + String logoutPattern = xwikiCfg.getProperty("xwiki.authentication.logoutpage", ""); + GoogleAppsAuthService authService = new GoogleAppsAuthService( + gaXWikiObjects, cookiePersistence, logoutPattern, log); + + gaIdentity = new GoogleAppsIdentity(xwikiContextProvider, + gaXWikiObjects, cookiePersistence, log); + tryInittingAuthService(authService); + + gaDriveAccess = + new GoogleDriveAccess(log, gaXWikiObjects, gaIdentity); + } catch (Exception e) { + e.printStackTrace(); + failed = true; + } - private DocumentReference gauthClassRef; + if (!failed) { + log.info("GoogleAppsManagerImpl is now running."); + updateLifeCycle(LifeCycle.RUNNING); + } else { + updateLifeCycle(LifeCycle.INITIALIZED); + } + } - @Override - public void initialize() throws InitializationException + void tryInittingAuthService(GoogleAppsAuthService authService) { - log.info("GoogleAppsScriptService initializing."); - XWikiContext context = xwikiContextProvider.get(); - XWiki xwiki = context.getWiki(); - - readConfigDoc(context); - + XWiki xwiki = getXWiki(); if (xwiki != null) { log.info("Initting authService."); // We do not verify with the context if the plugin is active and if the license is active // this will be done by the GoogleAppsAuthService and UI pages later on, when it is called within a request try { - authService = componentManager.getInstance(GoogleAppsAuthService.class); xwiki.setAuthService(authService); log.info("Succeeded initting authService,"); - } catch (ComponentLookupException e) { + } catch (Exception e) { log.info("Failed initting authService", e); } } if (authService == null) { log.info("Not yet initting authService."); } - - if (eventListener == null) { - eventListener = new GoogleAppsEventListener(this); - observationManager.addListener(eventListener); - } - - try { - jacksonFactory = JacksonFactory.getDefaultInstance(); - httpTransport = GoogleNetHttpTransport.newTrustedTransport(); - } catch (Exception e) { - e.printStackTrace(); - throw new InitializationException("Trouble at initializing", e); - } } - private BaseObject getConfigDoc(XWikiContext context) throws XWikiException - { - configDocRef = getConfigDocRef(); - XWikiDocument doc = context.getWiki().getDocument(configObjRef, context); - BaseObject result = doc.getXObject(configObjRef, false, context); - if (result == null) { - log.warn("Can't access Config document."); - } - return result; - } /** * Evaluates weather the application is active and licensed by looking at the stored documents. Within a request, @@ -299,19 +207,13 @@ public boolean isActive() @Unstable boolean isActive(XWikiContext context) { - log.info("Is active " + this.toString() + " with configClient non-null? " + (configClientId != null)); - if (configActiveFlag == null || configClientId == null || configClientId.length() == 0) { - readConfigDoc(context); - } - if (authService == null) { - try { - initialize(); - } catch (InitializationException e) { - e.printStackTrace(); - } + startIfNeedBe(); + // move this into startIfNeedBe (and itself in xwiO?) + if (gaIdentity == null) { + initialize(); } - if (configActiveFlag != null) { - return configActiveFlag; + if (gaXWikiObjects.isActive() != null) { + return gaXWikiObjects.isActive(); } return false; } @@ -323,7 +225,7 @@ boolean isActive(XWikiContext context) @Unstable public boolean useCookies() { - return useCookies; + return gaXWikiObjects.doesUseCookies(); } /** @@ -333,7 +235,7 @@ public boolean useCookies() @Unstable public boolean skipLoginPage() { - return skipLoginPage; + return gaXWikiObjects.doesSkipLoginPage(); } /** @@ -343,7 +245,7 @@ public boolean skipLoginPage() @Unstable public boolean authWithCookies() { - return authWithCookies; + return gaXWikiObjects.doesAuthWithCookies(); } /** @@ -353,97 +255,17 @@ public boolean authWithCookies() @Unstable public int getConfigCookiesTTL() { - return configCookiesTTL; - } - - void readConfigDoc(XWikiContext contextp) - { - XWikiContext context = contextp; - if (context == null) { - context = xwikiContextProvider.get(); - } - - try { - log.warn("Attempting to fetch Config doc"); - BaseObject config = getConfigDoc(context); - if (config != null) { - configActiveFlag = 0 != config.getIntValue("activate"); - useCookies = 0 != config.getIntValue("useCookies"); - skipLoginPage = 0 != config.getIntValue("skipLoginPage"); - authWithCookies = 0 != config.getIntValue("authWithCookies"); - configAppName = config.getStringValue("appname").trim(); - configClientId = config.getStringValue("clientid").trim(); - configClientSecret = config.getStringValue("secret").trim(); - configDomain = config.getStringValue("domain").trim(); - if (configDomain.length() == 0) { - configDomain = null; - } - List configScopes = Arrays.asList(config.getStringValue("scope").split("\\s")); - configScopeUseAvatar = configScopes.contains(AVATAR); - configScopeUseDrive = configScopes.contains("drive"); - configCookiesTTL = config.getIntValue("cookiesTTL"); - } - } catch (XWikiException e) { - e.printStackTrace(); - if (log != null) { - log.warn("can't fetch Config doc"); - } - } + return gaXWikiObjects.getConfigCookiesTTL(); } /** - * Reads the manifest to find when the JAR file was assembled by maven. - * - * @return the build date. - * @since 3.0 - */ - @Unstable - public Date getBuildTime() - { - Attributes attr = getManifestMainAttributes(); - if (attr == null) { - return null; - } - return new Date(Long.parseLong(attr.getValue("Bnd-LastModified"))); - } - - /** - * Reads the manifest to find the version of the JAR that was built. - * - * @return the build version + * @return if the app is configured to use the Google Drive integration (default: yes). * @since 3.0 */ @Unstable - public String getBuildVersion() - { - Attributes attr = getManifestMainAttributes(); - if (attr == null) { - return null; - } - return attr.getValue("Specification-Version"); - } - - private Attributes getManifestMainAttributes() + public boolean isDriveEnabled() { - try { - Class clazz = getClass(); - String className = clazz.getSimpleName() - + ".class"; - String classPath = clazz.getResource(className).toString(); - if (classPath != null) { - String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) - + "/META-INF/MANIFEST.MF"; - Manifest manifest = new Manifest(new URL(manifestPath).openStream()); - Attributes attr = manifest.getMainAttributes(); - return attr; - } else { - return null; - } - } catch (IOException e) { - String msg = "Can't read build version."; - log.warn(msg, e); - throw new RuntimeException(msg, e); - } + return gaXWikiObjects.doesConfigScopeUseDrive(); } /** @@ -453,6 +275,7 @@ private Attributes getManifestMainAttributes() @Override public void dispose() { + updateLifeCycle(LifeCycle.STOPPING); XWiki xwiki = getXWiki(); // XWiki can be null in the case when XWiki has been started and not accessed (no first request done and thus // no XWiki object initialized) and then stopped. @@ -460,9 +283,10 @@ public void dispose() // Unset the Authentication Service (next time XWiki.getAuthService() is called it'll be re-initialized) xwiki.setAuthService(null); } + updateLifeCycle(LifeCycle.STOPPED); } - private XWiki getXWiki() + XWiki getXWiki() { XWiki result = null; XWikiContext xc = this.xwikiContextProvider.get(); @@ -474,327 +298,6 @@ private XWiki getXWiki() return result; } - /** - * @return if the app is configured to use the Google Drive integration (default: yes). - * @since 3.0 - */ - @Unstable - public boolean isDriveEnabled() - { - return configScopeUseDrive; - } - - private String getOAuthUrl() - { - try { - XWikiContext context = xwikiContextProvider.get(); - DocumentReference oauthReference = new DocumentReference(context.getWikiId(), - SPACENAME, OAUTH); - return getXWiki().getDocument(oauthReference, context).getExternalURL(VIEWACTION, context); - } catch (XWikiException e) { - throw new GoogleAppsException("Trouble at getting OAuth URL", e); - } - } - - private DocumentReference getXWikiUserClassRef() - { - return new DocumentReference(WIKINAME, XWIKISPACE, "XWikiUsers"); - } - - private String getCurrentXWikiUserName() - { - DocumentReference userDoc = xwikiContextProvider.get().getUserReference(); - String uName = userDoc == null ? XWIKIGUEST : userDoc.getName(); - if (XWIKIGUEST.equals(uName)) { - uName = uName + "-" + xwikiContextProvider.get().getRequest().getSession().hashCode(); - } - return uName; - } - - /** - * @param userName the name of the user - * @return A DocumentReference for the given username. - * @since 3.0 - */ - @Unstable - DocumentReference createUserReference(String userName) - { - return userResolver.resolve(userName); - } - - private DocumentReference getGoogleAuthClassReference() - { - if (gauthClassRef == null) { - gauthClassRef = new DocumentReference(WIKINAME, SPACENAME, "GoogleAppsAuthClass"); - } - return gauthClassRef; - } - - private DocumentReference getSyncDocClassReference() - { - if (gauthClassRef == null) { - gauthClassRef = new DocumentReference(WIKINAME, SPACENAME, "SynchronizedDocumentClass"); - } - return gauthClassRef; - } - - DocumentReference getConfigDocRef() - { - if (configDocRef == null) { - configDocRef = new DocumentReference(WIKINAME, - SPACENAME, "GoogleAppsConfig"); - configObjRef = new ObjectReference("GoogleApps.GoogleAppsConfigClass", configDocRef); - } - return configDocRef; - } - - /** - * Build flow and trigger user authorization request. - * - * @return the configured flow - * @throws GoogleAppsException in case something can't be built - */ - private GoogleAuthorizationCodeFlow getFlow() - { - try { - if (dsFactory == null) { - dsFactory = - new FileDataStoreFactory(new java.io.File(environment.getPermanentDirectory(), SPACENAME)); - } - - // create scopes from config - List gScopes = new ArrayList<>(); - gScopes.add(PeopleServiceScopes.USERINFO_EMAIL); - gScopes.add(PeopleServiceScopes.USERINFO_PROFILE); - if (configScopeUseDrive != null && configScopeUseDrive) { - gScopes.add(DriveScopes.DRIVE); - } - - // create flow - return new GoogleAuthorizationCodeFlow.Builder( - httpTransport, - jacksonFactory, configClientId, configClientSecret, gScopes) - .setDataStoreFactory(dsFactory) - .setAccessType("online").setApprovalPrompt(AUTOAPPROVAL) - .setClientId(configClientId) - .build(); - } catch (Exception e) { - e.printStackTrace(); - throw new GoogleAppsException("Issue at building Google Authorization Flow.", e); - } - } - - /** - * Exchange an authorization code for OAuth 2.0 credentials. - * - * @param authorizationCode Authorization code to exchange for OAuth 2.0 credentials. - * @return OAuth 2.0 credentials. - */ - private Credential exchangeCode(String authorizationCode) - { - try { - GoogleAuthorizationCodeFlow flow = getFlow(); - GoogleTokenResponse tokenResponse = flow - .newTokenRequest(authorizationCode) - .setRedirectUri(getOAuthUrl()) - .execute(); - log.info("Token: " + tokenResponse); - return flow.createAndStoreCredential(tokenResponse, getCurrentXWikiUserName()); - } catch (Exception ex) { - throw new GoogleAppsException("Trouble at exchanging authorization code", ex); - } - } - - private Map getCredentialStore() - { - final String key = "GoogleAppsCredentialStore"; - HttpSession session = xwikiContextProvider.get().getRequest().getSession(true); - Map store = (Map) (session.getAttribute(key)); - if (store == null) { - store = new HashMap<>(); - session.setAttribute(key, store); - } - return store; - } - - private void storeCredentials(String userId, Credential credentials) - { - try { - if (userId.contains(XWIKIGUEST)) { - if (useCookies) { - // create a cookie - CookieAuthenticationPersistence cookieTools = - componentManager.getInstance(CookieAuthenticationPersistence.class); - cookieTools.setUserId(userId); - } - } - log.info("Storing credentials for user " + userId); - getCredentialStore().put(userId, credentials); - } catch (Exception e) { - e.printStackTrace(); - throw new GoogleAppsException("Issue at storing credential.", e); - } - } - - private Credential getStoredCredentials(String userId) - { - if (userId == null) { - return null; - } - log.debug("Getting credentials for user " + userId); - return getCredentialStore().get(userId); - } - - /** - * Retrieve credentials using the provided authorization code. - *

- * This function exchanges the authorization code for an access token and queries the UserInfo API to retrieve the - * user's e-mail address. If a refresh token has been retrieved along with an access token, it is stored in the - * application database using the user's e-mail address as key. If no refresh token has been retrieved, the function - * checks in the application database for one and returns it if found or throws a NoRefreshTokenException with the - * authorization URL to redirect the user to. - * - * @param authorizationCode Authorization code to use to retrieve an access token. - * @return OAuth 2.0 credentials instance containing an access and refresh token. - * @throws GoogleAppsException Unable to load client_secret.json. - */ - private Credential retrieveCredentials(String authorizationCode, boolean redirect) - { - try { - Credential credentials; - String user = getCurrentXWikiUserName(); - - if (authorizationCode != null && authorizationCode.length() > 0) { - log.debug("Trying to get credentials from authorization code: " + authorizationCode); - credentials = exchangeCode(authorizationCode); - if (credentials != null) { - String rtoken = credentials.getRefreshToken(); - if (rtoken != null) { - log.debug("Refresh token has been created: " + rtoken); - storeCredentials(user, credentials); - return credentials; - } else { - log.debug("Failure to create refresh token"); - storeCredentials(user, credentials); - return credentials; - } - } - } - - log.debug("No credentials found. Checking stored credentials for user " + user); - credentials = getStoredCredentials(user); - if (credentials != null) { - log.debug("Retrieved stored credentials"); - return credentials; - } - log.debug("Could not find stored credentials"); - - log.debug("No credentials retrieved."); - // No refresh token has been retrieved. - if (redirect) { - log.debug("Redirecting to authorization URL."); - xwikiContextProvider.get().getResponse().sendRedirect(getAuthorizationURL()); - } - return null; - } catch (Exception e) { - throw new GoogleAppsException("Trouble at retrieving credentials", e); - } - } - - private String getAuthorizationURL() - { - try { - String state = ""; - XWikiContext context = xwikiContextProvider.get(); - XWikiRequest request = context.getRequest(); - DocumentReference ref = context.getDoc().getDocumentReference(); - if (!(OAUTH.equals(ref.getName()) && SPACENAME.equals(ref.getLastSpaceReference().getName()))) { - - String finalRedirect = new URL( - new URL(getXWiki().getExternalURL("GoogleApps.Login", VIEWACTION, context)), - context.getDoc().getURL(VIEWACTION, request.getQueryString(), context)).toExternalForm(); - state = Integer.toHexString(finalRedirect.hashCode()); - storedStates.put(state, finalRedirect); - } - - GoogleAuthorizationCodeRequestUrl urlBuilder = getFlow() - .newAuthorizationUrl() - .setRedirectUri(getOAuthUrl()) - .setState(state).setClientId(configClientId) - .setAccessType("offline").setApprovalPrompt(AUTOAPPROVAL); - // Add user email to filter account if the user is logged with multiple account - if (useCookies) { - CookieAuthenticationPersistence cookieTools = - componentManager.getInstance(CookieAuthenticationPersistence.class); - String userId = cookieTools.getUserId(); - if (userId != null) { - XWikiDocument userDoc = getXWiki().getDocument(createUserReference(userId), - xwikiContextProvider.get()); - String userEmail = null; - BaseObject userObj = userDoc.getXObject(getXWikiUserClassRef(), false, - xwikiContextProvider.get()); - // userclass "XWiki.XWikiUsers" - - if (userObj != null) { - userEmail = userDoc.getStringValue(EMAIL); - } - if (userEmail != null) { - urlBuilder = urlBuilder.set("login_hint", userEmail); - } - } - } - String authurl = urlBuilder.build(); - log.debug("google authentication url : " + authurl); - return authurl; - } catch (Exception ex) { - throw new GoogleAppsException("trouble at getAuthorizationURL", ex); - } - } - - /** - * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. - * - * @return found credential - * @since 3.0 - */ - @Unstable - public Credential authorize() - { - return authorize(true); - } - - /** - * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. - * - * @param redirect If a redirect can be done - * @return found credential - * @since 3.0 - */ - @Unstable - public Credential authorize(boolean redirect) - { - try { - log.info("In authorize"); - // useless? - GoogleAuthorizationCodeFlow flow = getFlow(); - XWikiRequest request = xwikiContextProvider.get().getRequest(); - String state = request.getParameter("state"); - XWikiResponse response = xwikiContextProvider.get().getResponse(); - Credential creds = retrieveCredentials(request.getParameter("code"), redirect); - log.info("Got credentials: " + creds); - if (state != null && state.length() > 0) { - String url = storedStates.get(state); - if (url != null) { - log.info("Redirecting to final destination after authorization: " + url); - response.sendRedirect(new URL(new URL(request.getRequestURL().toString()), url).toExternalForm()); - } - } - return creds; - } catch (IOException e) { - throw new GoogleAppsException(e); - } - } - /** * Performs the necessary communication with Google-Services to fetch identity and update the XWiki-user object or * possibly sends a redirect to a Google login screen. @@ -805,345 +308,41 @@ public Credential authorize(boolean redirect) @Unstable public String updateUser() { - try { - if (!isActive()) { - return FAILEDLOGIN; - } - log.info("Updating user..."); - XWikiContext context = xwikiContextProvider.get(); - String xwikiUser; - Credential credential = authorize(); - - Person user = null; - if (credential != null) { - PeopleService pservice = new PeopleService.Builder(httpTransport, - jacksonFactory, credential).setApplicationName(configAppName) - .build(); - user = pservice.people().get("people/me").setPersonFields("emailAddresses,names,photos").execute(); - // GOOGLEAPPS: User: [displayName:..., emails:[[type:account, value:...]], etag:"...", - // id:...., image:[isDefault:false, url:https://...], kind:plus#person, language:en, - // name:[familyName:..., givenName:...]] - } - log.info("user: " + user); - String usersEmailAddress = null; - context.getRequest().setAttribute(GOOGLEUSERATT, user); - if (user == null) { - return NOUSER; - } - if (configDomain != null) { - boolean foundCompatibleDomain = false; - if (user.getEmailAddresses() != null) { - for (EmailAddress address : user.getEmailAddresses()) { - String oneOfEmails = address.getValue(); - if (oneOfEmails.endsWith(configDomain)) { - foundCompatibleDomain = true; - usersEmailAddress = oneOfEmails; - break; - } - } - } - if (!foundCompatibleDomain) { - String userId = getCurrentXWikiUserName(); - getCredentialStore().remove(userId); - log.debug("Wrong domain: Removed credentials for userid " + userId); - return FAILEDLOGIN; - } - } - // this seems undocumented but well working - String id = (String) user.get("resourceName"); - String currentWiki = context.getWikiId(); - try { - // Force main wiki database to create the user as global - context.setMainXWiki(WIKINAME); - if (usersEmailAddress == null) { - if (user.getEmailAddresses() != null && user.getEmailAddresses().size() > 0) { - usersEmailAddress = user.getEmailAddresses().get(0).getValue(); - } else { - usersEmailAddress = ""; - } - } - List wikiUserList = queryManager.createQuery( - "from doc.object(GoogleApps.GoogleAppsAuthClass) as auth where auth.id=:id", - Query.XWQL).bindValue(ID, id).execute(); - if ((wikiUserList == null) || (wikiUserList.size() == 0)) { - wikiUserList = queryManager.createQuery( - "from doc.object(XWiki.XWikiUsers) as user where user.email=:email", - Query.XWQL) - .bindValue(EMAIL, usersEmailAddress).execute(); - } - - if ((wikiUserList == null) || (wikiUserList.size() == 0)) { - // user not found.. need to create new user - xwikiUser = usersEmailAddress.substring(0, usersEmailAddress.indexOf("@")); - // make sure user is unique - xwikiUser = getXWiki().getUniquePageName(XWIKISPACE, xwikiUser, context); - // create user - DocumentReference userDirRef = new DocumentReference( - context.getWikiId(), "Main", "UserDirectory"); - String randomPassword = RandomStringUtils.randomAlphanumeric(8); - Map userAttributes = new HashMap<>(); - - if (user.getNames() != null && user.getNames().size() > 0) { - userAttributes.put(FIRSTNAME, user.getNames().get(0).getGivenName()); - userAttributes.put(LASTNAME, user.getNames().get(0).getFamilyName()); - } - userAttributes.put(EMAIL, usersEmailAddress); - userAttributes.put(PASSWORD, randomPassword); - int isCreated = getXWiki().createUser(xwikiUser, userAttributes, - userDirRef, null, null, "edit", context); - // Add google apps id to the user - if (isCreated == 1) { - log.debug("Creating user " + xwikiUser); - XWikiDocument userDoc = getXWiki() - .getDocument(createUserReference(xwikiUser), context); - BaseObject userObj = userDoc.getXObject(getXWikiUserClassRef()); - - // TODO: is this not redundant when having used createUser (map) ? - if (user.getNames() != null && user.getNames().size() > 0) { - userObj.set(FIRSTNAME, user.getNames().get(0).getGivenName(), context); - userObj.set(LASTNAME, user.getNames().get(0).getFamilyName(), context); - } - userObj.set("active", 1, context); - if (configScopeUseAvatar && user.getPhotos() != null - && user.getPhotos().size() > 0 - && user.getPhotos().get(0).getUrl() != null) - { - String photoUrl = user.getPhotos().get(0).getUrl(); - log.debug("Adding avatar " + photoUrl); - URL u = new URL(photoUrl); - InputStream b = u.openStream(); - String fileName = u.getFile().substring(u.getFile().lastIndexOf('/') + 1); - userDoc.addAttachment(fileName, u.openStream(), context); - userObj.set(AVATAR, fileName, context); - b.close(); - } - - userDoc.createXObject(getGoogleAuthClassReference(), context); - BaseObject gAppsAuthClass = userDoc.getXObject(getGoogleAuthClassReference()); - gAppsAuthClass.set(ID, id, context); - getXWiki().saveDocument(userDoc, "Google Apps login user creation", false, context); - } else { - log.debug("User creation failed"); - return FAILEDLOGIN; - } - } else { - // user found.. we should update it if needed - xwikiUser = (String) (wikiUserList.get(0)); - log.debug("Found user " + xwikiUser); - boolean changed = false; - XWikiDocument userDoc = getXWiki().getDocument(createUserReference(xwikiUser), context); - BaseObject userObj = userDoc.getXObject(getXWikiUserClassRef()); - if (userObj == null) { - log.debug("User found is not a user"); - return FAILEDLOGIN; - } else { - if (!userObj.getStringValue(EMAIL).equals(usersEmailAddress)) { - userObj.set(EMAIL, usersEmailAddress, context); - changed = true; - } - if (user.getNames() != null && user.getNames().size() > 0) { - if (!userObj.getStringValue(FIRSTNAME).equals( - user.getNames().get(0).getGivenName())) - { - userObj.set(FIRSTNAME, user.getNames().get(0).getGivenName(), context); - changed = true; - } - if (!userObj.getStringValue(LASTNAME).equals( - user.getNames().get(0).getFamilyName())) - { - userObj.set(LASTNAME, user.getNames().get(0).getFamilyName(), context); - changed = true; - } - } - if (configScopeUseAvatar && user.getPhotos() != null && user.getPhotos().size() > 0 - && user.getPhotos().get(0).getUrl() != null) - { - String imageUrl = user.getPhotos().get(0).getUrl(); - imageUrl = imageUrl - + (imageUrl.contains("?") ? "&" : '?') - + "sz=512"; - log.debug("Pulling avatar " + imageUrl); - XWikiAttachment attachment = - userObj.getStringValue(AVATAR) == null ? null - : userDoc.getAttachment(userObj.getStringValue(AVATAR)); - HttpGet httpget = new HttpGet(imageUrl); - if (attachment != null) { - httpget.addHeader("If-Modified-Since", - DateUtil.formatDate(attachment.getDate())); - } - CloseableHttpResponse response = httpclient.execute(httpget); - HttpEntity entity = null; - if (response.getStatusLine().getStatusCode() == 200 - && (entity = response.getEntity()) != null) { - String fileName = imageUrl.substring(imageUrl.lastIndexOf('/') + 1); - log.debug("Avatar changed " + fileName); - userObj.set(AVATAR, fileName, context); - userDoc.addAttachment(fileName, entity.getContent(), context); - changed = true; - } - } - - BaseObject googleAppsAuth = userDoc.getXObject(getGoogleAuthClassReference()); - if (googleAppsAuth == null) { - userDoc.createXObject(getGoogleAuthClassReference(), context); - googleAppsAuth = userDoc.getXObject(getGoogleAuthClassReference()); - changed = true; - } - - if (!googleAppsAuth.getStringValue(ID).equals(id)) { - googleAppsAuth.set(ID, id, context); - changed = true; - } - - if (changed) { - log.info("User changed."); - getXWiki().saveDocument(userDoc, "Google Apps login user updated.", context); - } else { - log.info("User unchanged."); - } - } - } - } catch (QueryException qe) { - log.warn("Cannot query for users.", qe); - throw new XWikiException("Can't query for users.", qe); - } finally { - // Restore database - context.setMainXWiki(currentWiki); - } - - // we need to restore the credentials as the user will now be logged-in - storeCredentials(xwikiUser, credential); - - // store the validated xwiki user for the authentication module - context.getRequest().getSession().setAttribute("googleappslogin", xwikiUser); - return "ok"; - } catch (Exception e) { - log.warn("Problem at updateUser", e); - return NOUSER; - } - } - - /** - * Builds and returns an authorized Drive client service. - * - * @return an authorized Drive client service - */ - private Drive getDriveService() - { - Credential credential = authorize(); - return new Drive.Builder( - httpTransport, jacksonFactory, credential) - .setApplicationName(configAppName) - .build(); + return gaIdentity.updateUser(); } /** - * Fetches a list of Google Drive document matching a substring query in the filename. + * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. * - * @param query the expected query (e.g. fullText contains winter ski) - * @param nbResults max number of results - * @return The list of files at Google Drive. + * @param redirect If a redirect can be done + * @return if found a credential * @since 3.0 */ @Unstable - public List listDriveDocumentsWithTypes(String query, int nbResults) + public boolean authorize(boolean redirect) { try { - Drive drive = getDriveService(); - Drive.Files.List req = drive.files().list() - .setFields("items(id,mimeType,title,exportLinks,selfLink,version,alternateLink)") - .setMaxResults(nbResults); - if (query != null && query.length() > 0) { - req.setQ(query); - } - FileList result = req.execute(); - return result.getItems(); - } catch (IOException e) { - throw new GoogleAppsException(e); + return null != gaIdentity.authorize(redirect); + } catch (Exception ex) { + log.warn("Trouble at authorizing", ex); + return false; } } /** * Fetches the google-drive document's representation and stores it as attachment. * - * @param page attach to this page - * @param name attach using this file name - * @param id store object attached to this attachment using this id (for later sync) - * @param mediaType content-type of the file to be fetched (or "unknown"; in this case the - * mediaType is read from Tika. - * @return true if successful + * @param page attach to this page + * @param name attach using this file name + * @param id store object attached to this attachment using this id (for later sync) + * @param mediaType content-type of the file to be fetched (or "unknown"; in this case the mediaType is read from + * Tika. * @since 3.0 */ @Unstable - public boolean retrieveFileFromGoogle(String page, String name, String id, String mediaType) - { - return retrieveFileFromGoogle(getDriveService(), page, name, id, mediaType); - } - - private boolean retrieveFileFromGoogle(Drive driveService, String page, String name, String id, String mediaType) + public void retrieveFileFromGoogle(String page, String name, String id, String mediaType) { - String mt = mediaType; - if ("unknown".equalsIgnoreCase(mediaType) || mediaType == null || !mediaType.contains("/")) { - mt = new Tika().detect(name); - } - - log.info("Retrieving " + name + " to page " + page + ": " + id + "(mediatype " + mt + ")."); - - try { - XWikiDocument adoc = getXWiki().getDocument(documentResolver.resolve(page), xwikiContextProvider.get()); - InputStream downloadStream = driveService.files().export(id, mt).executeMediaAsInputStream(); - saveFileToXWiki(driveService, adoc, id, name, downloadStream, true); - return true; - } catch (Exception e) { - log.info(e.getMessage(), e); - throw new GoogleAppsException("Trouble at retrieving from Google.", e); - } - } - - private void saveFileToXWiki(Drive driveService, XWikiDocument adoc, - String id, String name, InputStream data, boolean redirect) - { - try { - XWikiContext context = xwikiContextProvider.get(); - XWikiAttachment attachment = adoc.addAttachment(name, data, context); - - // ready to save now - adoc.saveAttachmentContent(attachment, context); - - String user = driveService.about().get().execute().getUser().getEmailAddress(); - File docData = driveService.files().get(id).execute(); - String embedLink = docData.getEmbedLink(); - if (embedLink == null) { - embedLink = docData.getAlternateLink(); - } - - getXWiki().saveDocument(adoc, "Updated Attachment From Google Apps", context); - - BaseObject object = adoc.getXObject(getSyncDocClassReference(), FILENAME, name, false); - if (object == null) { - object = adoc.newXObject(getGoogleAuthClassReference(), context); - } - object.set(ID, id, context); - object.set(FILENAME, name, context); - if (context.getRequest().getParameter(URL) != null) { - object.set(EXPORTLINK, context.getRequest().getParameter(URL), context); - } - object.set(VERSION, docData.getVersion().toString(), context); - object.set(EDITLINK, docData.getAlternateLink(), context); - object.set(EMBEDLINK, embedLink, context); - if (object.getStringValue(USER) == null || object.getStringValue(USER).length() == 0) { - object.set(USER, user, context); - } - getXWiki().saveDocument(adoc, UPDATECOMMENT, context); - log.info("Document " + name + " has been saved to XWiki"); - - if (redirect) { - String rurl = adoc.getURL(VIEWACTION, "#Attachments", context); - context.getResponse().sendRedirect(rurl); - } - } catch (Exception e) { - throw new GoogleAppsException("Trouble at saving GoogleDrive file to XWiki.", e); - } + gaDriveAccess.retrieveFileFromGoogle(page, name, id, mediaType); } /** @@ -1155,23 +354,9 @@ private void saveFileToXWiki(Drive driveService, XWikiDocument adoc, * @since 3.0 */ @Unstable - public DriveDocMetadata getGoogleDocument(String pageName, String fileName) + public DriveDocMetadata getSyncDocMetadata(String pageName, String fileName) { - try { - XWikiDocument adoc = getXWiki().getDocument(documentResolver.resolve(pageName), xwikiContextProvider.get()); - BaseObject object = adoc.getXObject(getSyncDocClassReference(), FILENAME, fileName, false); - if (object == null) { - return null; - } else { - DriveDocMetadata gdm = new DriveDocMetadata(); - gdm.id = object.getStringValue(ID); - gdm.editLink = object.getStringValue(EDITLINK); - gdm.exportLink = object.getStringValue(EXPORTLINK); - return gdm; - } - } catch (XWikiException e) { - throw new GoogleAppsException("Can't get Google-Document inside XWiki.", e); - } + return gaXWikiObjects.getGoogleDocumentMetadata(pageName, fileName); } /** @@ -1188,78 +373,15 @@ public DriveDocMetadata getGoogleDocument(String pageName, String fileName) public BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, BaseObject objp, int nb) { try { - BaseObject obj = objp; - Drive drive = getDriveService(); - XWikiContext context = xwikiContextProvider.get(); - String user = drive.about().get().execute().getUser().getEmailAddress(); - File docData = drive.files().get(docId).execute(); - String embedLink = docData.getEmbedLink(); - if (embedLink == null) { - embedLink = docData.getAlternateLink(); - } - if (obj == null) { - obj = doc.newXObject(getSyncDocClassReference(), context); - obj.setNumber(nb); - } - obj.setStringValue(ID, docId); - if (embedLink != null) { - obj.setStringValue(EMBEDLINK, embedLink); - } - obj.setStringValue(EDITLINK, docData.getAlternateLink()); - obj.setStringValue(VERSION, docData.getVersion().toString()); - obj.setStringValue(FILENAME, - docData.getOriginalFilename() != null ? docData.getOriginalFilename() : docData.getTitle()); - obj.setStringValue(USER, user); - getXWiki().saveDocument(doc, "Inserting Google Document", context); - return obj; + DriveDocMetadata ddm = gaDriveAccess.getEmbedData(docId); + // use here and at retrieveFromGoogle + return gaXWikiObjects.createOrUpdateEmbedObject(docId, doc, objp, nb, ddm); } catch (Exception e) { throw new GoogleAppsException("Can't create or update embedded document.", e); } } - /** - * Reads the extension and document name. - * - * @param docName the raw docName - * @param elink the link where to read the extension name - * @return an array with extension and simplified document name - * @since 3.0 - */ - @Unstable - public String[] getExportLink(String docName, String elink) - { - int index = elink.indexOf(EXPORTFORMATEQ) + 13; - String extension = elink.substring(index); - String newDocName = docName - .replaceAll("\\.(doc|docx|odt|xls|xlsx|ods|pptx|svg|png|jpeg|pdf|)$", ""); - newDocName += '.' + extension; - return new String[]{ extension, newDocName }; - } - - private String findExportLink(String name, File entry) - { - String exportLink; - String lastLink = ""; - for (Map.Entry elink : entry.getExportLinks().entrySet()) { - log.info("Checking link: " + elink); - lastLink = elink.getValue(); - int index = lastLink.indexOf(EXPORTFORMATEQ) + 13; - String extension = lastLink.substring(index); - if (name.endsWith('.' + extension)) { - return lastLink; - } - } - int index = lastLink.indexOf(EXPORTFORMATEQ) + 13; - exportLink = lastLink.substring(0, index); - if (name.endsWith(".xls")) { - exportLink += "xlsx"; - } else { - exportLink += name.substring(name.lastIndexOf('.') + 1); - } - return exportLink; - } - /** * Saves the attachment stored in XWiki to the Google drive of the user attached to the current logged-in user. * @@ -1270,72 +392,26 @@ private String findExportLink(String name, File entry) * @since 3.0 */ @Unstable - public Map saveAttachmentToGoogle(String page, String name) + public DriveDocMetadata saveAttachmentToGoogle(String page, String name) { - try { - log.info("Starting saving attachment ${name} from page ${page}"); - XWikiContext context = xwikiContextProvider.get(); - XWikiDocument adoc = getXWiki().getDocument(documentResolver.resolve(page), context); - XWikiAttachment attach = adoc.getAttachment(name); - String ctype = attach.getMimeType(); - - File file = new File(); - file.setTitle(name); - file.setOriginalFilename(name); - InputStreamContent content = new InputStreamContent(ctype, attach.getContentInputStream(context)); - Drive drive = getDriveService(); - String user = drive.about().get().execute().getUser().getEmailAddress(); - Drive.Files.Insert insert = drive.files().insert(file, content); - insert.setConvert(true); - File docData = insert.execute(); - if (docData != null) { - log.info("File inserted " + docData); - String embedLink = docData.getEmbedLink(); - if (embedLink == null) { - embedLink = docData.getAlternateLink(); - } - - BaseObject object = adoc.newXObject(getSyncDocClassReference(), context); - Map r = new HashMap<>(); - object.set(ID, docData.getId(), context); - r.put(ID, docData.getId()); - object.set(FILENAME, name, context); - object.set(EXPORTLINK, findExportLink(name, docData), context); - r.put(EXPORTLINK, findExportLink(name, docData)); - object.set(VERSION, Long.toString(docData.getVersion()), context); - object.set(EDITLINK, docData.getAlternateLink(), context); - r.put(EDITLINK, docData.getAlternateLink()); - object.set(EMBEDLINK, embedLink, context); - object.set(USER, user, context); - - getXWiki().saveDocument(adoc, UPDATECOMMENT, context); - return r; - } else { - log.info("File insert failed"); - return null; - } - } catch (Exception e) { - throw new GoogleAppsException("Couldn't save attachment to Google.", e); - } + return gaDriveAccess.saveAttachmentToGoogle(page, name); } /** - * Reads the google user-info attached to the current user as stored in the request. + * Fetches a list of Google Drive document matching a substring query in the filename. * - * @return the google user-info with keys displayName, emails (array of type,value pairs), etag, id, image (map with - * keys isDefault and url), kind, language, name (map with keys familyName and givenName). + * @param query the exfpected query (e.g. fullText contains winter ski) + * @param nbResults max number of results + * @return The list of objects of Google Drive. * @since 3.0 */ @Unstable - public Map getGoogleUser() + public List listDriveDocuments(String query, int nbResults) { - // e.g.: User: [displayName: name name, - // emails:[[type:account, value:xxx@googlemail.com]], - // etag:"k-5ZH5-al;sdsdkl;-sdsadsd", - // id:948382, - // image:[isDefault:false, url:https://222.googleusercontent.com/-2323/s50/photo.jpg], - // kind:plus#person, language:uu, - // name:[familyName:XXX, givenName:xxx]] - return (Map) (xwikiContextProvider.get().getRequest().getAttribute(GOOGLEUSERATT)); + startIfNeedBe(); + return gaDriveAccess.listDriveDocuments(query, nbResults); } + + enum LifeCycle + { CONSTRUCTED, INITIALIZED, STARTING, RUNNING, STOPPING, STOPPED } } diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsXWikiObjects.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsXWikiObjects.java new file mode 100644 index 0000000..a4a15b3 --- /dev/null +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsXWikiObjects.java @@ -0,0 +1,608 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.googleapps.internal; + +import java.io.File; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.inject.Provider; + +import org.apache.commons.httpclient.util.DateUtil; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.slf4j.Logger; +import org.xwiki.model.reference.DocumentReference; +import org.xwiki.model.reference.DocumentReferenceResolver; +import org.xwiki.model.reference.ObjectReference; +import org.xwiki.observation.ObservationManager; +import org.xwiki.query.Query; +import org.xwiki.query.QueryManager; +import org.xwiki.stability.Unstable; + +import com.xpn.xwiki.XWikiContext; +import com.xpn.xwiki.doc.XWikiAttachment; +import com.xpn.xwiki.doc.XWikiDocument; +import com.xpn.xwiki.objects.BaseObject; +import com.xwiki.googleapps.DriveDocMetadata; +import com.xwiki.googleapps.GoogleAppsException; + +class GoogleAppsXWikiObjects implements GoogleAppsConstants +{ + // environment + private final Logger log; + + private final Provider contextProvider; + + private final QueryManager queryManager; + + private final DocumentReferenceResolver documentResolver; + + private final DocumentReferenceResolver userResolver; + + private final File permanentDir; + + private boolean started; + + private Boolean configActiveFlag; + + private boolean useCookies; + + private boolean skipLoginPage; + + private boolean authWithCookies; + + private String configAppName; + + private String configClientId; + + private String configClientSecret; + + private String configDomain; + + private boolean configScopeUseAvatar; + + private boolean configScopeUseDrive; + + private int configCookiesTTL; + + // internal objects + private DocumentReference configDocRef; + + private ObjectReference configObjRef; + + private GoogleAppsEventListener eventListener; + + private DocumentReference syncdocClassRef; + + private DocumentReference gauthClassRef; + + GoogleAppsXWikiObjects(Logger log, Provider contextProvider, + QueryManager queryManager, DocumentReferenceResolver documentResolver, + DocumentReferenceResolver userResolver, File permanentDir) + { + this.log = log; + this.contextProvider = contextProvider; + this.queryManager = queryManager; + this.documentResolver = documentResolver; + this.userResolver = userResolver; + this.permanentDir = permanentDir; + } + + Boolean isActive() + { + return configActiveFlag; + } + + boolean doesUseCookies() + { + return useCookies; + } + + boolean doesSkipLoginPage() + { + return skipLoginPage; + } + + boolean doesAuthWithCookies() + { + return authWithCookies; + } + + String getConfigAppName() + { + return configAppName; + } + + String getConfigClientId() + { + return configClientId; + } + + String getConfigClientSecret() + { + return configClientSecret; + } + + String getConfigDomain() + { + return configDomain; + } + + boolean getConfigScopeUseAvatar() + { + return configScopeUseAvatar; + } + + boolean doesConfigScopeUseDrive() + { + return configScopeUseDrive; + } + + int getConfigCookiesTTL() + { + return configCookiesTTL; + } + + private BaseObject getConfigDoc(XWikiContext context) + { + try { + configDocRef = getConfigDocRef(); + XWikiDocument doc = context.getWiki().getDocument(configObjRef, context); + BaseObject result = doc.getXObject(configObjRef, false, context); + if (result == null) { + log.warn("Can't access Config document."); + } + return result; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + private DocumentReference getSyncDocClassReference() + { + if (syncdocClassRef == null) { + syncdocClassRef = new DocumentReference(WIKINAME, SPACENAME, "SynchronizedDocumentClass"); + } + return syncdocClassRef; + } + + DocumentReference getConfigDocRef() + { + if (configDocRef == null) { + configDocRef = new DocumentReference(WIKINAME, + SPACENAME, "GoogleAppsConfig"); + configObjRef = new ObjectReference("GoogleApps.GoogleAppsConfigClass", configDocRef); + } + return configDocRef; + } + + private void readConfigDoc() + { + XWikiContext context = contextProvider.get(); + + log.warn("Attempting to fetch Config doc"); + BaseObject config = getConfigDoc(context); + if (config != null) { + configActiveFlag = 0 != config.getIntValue("activate"); + useCookies = 0 != config.getIntValue("useCookies"); + skipLoginPage = 0 != config.getIntValue("skipLoginPage"); + authWithCookies = 0 != config.getIntValue("authWithCookies"); + configAppName = config.getStringValue("appname").trim(); + configClientId = config.getStringValue("clientid").trim(); + configClientSecret = config.getStringValue("secret").trim(); + configDomain = config.getStringValue("domain").trim(); + if (configDomain.length() == 0) { + configDomain = null; + } + List configScopes = Arrays.asList(config.getStringValue("scope").split("\\s")); + configScopeUseAvatar = configScopes.contains(AVATAR); + configScopeUseDrive = configScopes.contains("drive"); + configCookiesTTL = config.getIntValue("cookiesTTL"); + } + } + + void startIfNeedBe(ObservationManager observationManager) + { + if (started) { + return; + } + if (eventListener == null) { + eventListener = new GoogleAppsEventListener(this); + observationManager.addListener(eventListener); + } + if (configActiveFlag == null || configClientId == null) { + readConfigDoc(); + } + started = true; + } + + void restart() + { + readConfigDoc(); + } + + void saveFileToXWiki(String page, + String driveFileId, String name, InputStream data, DriveDocMetadata ddm) + { + try { + XWikiContext context = contextProvider.get(); + XWikiDocument adoc = context.getWiki().getDocument(documentResolver.resolve(page), context); + XWikiAttachment attachment = adoc.addAttachment(name, data, context); + + // ready to save now + adoc.saveAttachmentContent(attachment, context); + + context.getWiki().saveDocument(adoc, "Updated Attachment From Google Apps", context); + + BaseObject object = adoc.getXObject(getSyncDocClassReference(), FILENAME, name, false); + if (object == null) { + object = adoc.newXObject(getSyncDocClassReference(), context); + } + object.set(ID, driveFileId, context); + object.set(FILENAME, name, context); + if (context.getRequest().getParameter(URL) != null) { + object.set(EXPORTLINK, context.getRequest().getParameter(URL), context); + } + object.set(VERSION, ddm.version, context); + object.set(EDITLINK, ddm.editLink, context); + object.set(EMBEDLINK, ddm.embedLink, context); + if (object.getStringValue(USER) == null || object.getStringValue(USER).length() == 0) { + object.set(USER, ddm.user, context); + } + context.getWiki().saveDocument(adoc, UPDATECOMMENT, context); + log.info("Document " + name + " has been saved to XWiki"); + } catch (Exception e) { + throw new GoogleAppsException("Trouble at saving GoogleDrive file to XWiki.", e); + } + } + + DriveDocMetadata getGoogleDocumentMetadata(String pageName, String fileName) + { + try { + XWikiDocument adoc = contextProvider.get().getWiki().getDocument(documentResolver.resolve(pageName), + contextProvider.get()); + BaseObject object = adoc.getXObject(getSyncDocClassReference(), FILENAME, fileName, false); + if (object == null) { + return null; + } else { + DriveDocMetadata gdm = new DriveDocMetadata(); + gdm.id = object.getStringValue(ID); + gdm.editLink = object.getStringValue(EDITLINK); + gdm.exportLink = object.getStringValue(EXPORTLINK); + return gdm; + } + } catch (Exception e) { + throw new GoogleAppsException("Can't get Google-Document inside XWiki.", e); + } + } + + BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, BaseObject o, + int nb, DriveDocMetadata ddm) + { + try { + XWikiContext context = contextProvider.get(); + BaseObject obj = o; + if (obj == null) { + obj = doc.newXObject(getSyncDocClassReference(), context); + obj.setNumber(nb); + } + obj.setStringValue(ID, docId); + if (ddm.embedLink != null) { + obj.setStringValue(EMBEDLINK, ddm.embedLink); + } + + obj.setStringValue(EDITLINK, ddm.editLink); + obj.setStringValue(VERSION, ddm.version); + obj.setStringValue(FILENAME, ddm.fileName); + obj.setStringValue(USER, ddm.user); + context.getWiki().saveDocument(doc, "Inserting Google Document", context); + return obj; + } catch (Exception e) { + throw new GoogleAppsException("Couldn't update object", e); + } + } + + ImmutablePair getAttachment(String name, String page) + { + try { + XWikiContext context = contextProvider.get(); + XWikiDocument adoc = context.getWiki().getDocument(documentResolver.resolve(page), context); + XWikiAttachment attach = adoc.getAttachment(name); + return new ImmutablePair<>( + attach.getContentInputStream(context), attach.getMimeType(context)); + } catch (Exception e) { + throw new GoogleAppsException("Couldn't getAttachment", e); + } + } + + void insertSyncDocObject(String page, String name, DriveDocMetadata ddm) + { + try { + XWikiContext context = contextProvider.get(); + XWikiDocument adoc = context.getWiki().getDocument(documentResolver.resolve(page), context); + BaseObject object = adoc + .newXObject(getSyncDocClassReference(), context); + object.set(ID, ddm.id, context); + object.set(FILENAME, name, context); + object.set(EXPORTLINK, ddm.exportLink, context); + object.set(VERSION, ddm.version, context); + object.set(EDITLINK, ddm.editLink, context); + object.set(EMBEDLINK, ddm.embedLink, context); + object.set(USER, ddm.user, context); + + context.getWiki().saveDocument(adoc, UPDATECOMMENT, context); + } catch (Exception e) { + throw new GoogleAppsException("Couldn't createSyncDocObject", e); + } + } + + String updateXWikiUser(String googleUserId, List emails, String emailP, + String firstName, String lastName, String avatarUrl) + { + XWikiContext context = contextProvider.get(); + String xwikiUser; + String email = emailP; + String currentWiki = context.getWikiId(); + try { + // Force main wiki database to create the user as global + context.setMainXWiki(WIKINAME); + if (email == null) { + if (emails != null && emails.size() > 0) { + email = emails.get(0); + } else { + email = ""; + } + } + List wikiUserList = findExistingUser(googleUserId, email); + + if (wikiUserList == null || wikiUserList.size() == 0) { + xwikiUser = createUser(googleUserId, email, firstName, lastName, avatarUrl, context); + } else { + // user found.. we should update it if needed + xwikiUser = (String) (wikiUserList.get(0)); + if (xwikiUser.startsWith(XWIKISPACE + '.')) { + xwikiUser = xwikiUser.substring(XWIKISPACE.length() + 1); + } + updateUser(xwikiUser, email, firstName, lastName, avatarUrl, googleUserId, context); + } + } finally { + // Restore database + context.setMainXWiki(currentWiki); + } + return xwikiUser; + } + + private List findExistingUser(String googleUserId, String email) + { + try { + List wikiUserList = queryManager.createQuery( + "from doc.object(GoogleApps.GoogleAppsAuthClass) as auth where auth.id=:id", + Query.XWQL).bindValue(ID, googleUserId).execute(); + if ((wikiUserList == null) || (wikiUserList.size() == 0)) { + wikiUserList = queryManager.createQuery( + "from doc.object(XWiki.XWikiUsers) as user where user.email=:email", + Query.XWQL) + .bindValue(EMAIL, email).execute(); + } + return wikiUserList; + } catch (Exception e) { + e.printStackTrace(); + throw new GoogleAppsException(e); + } + } + + private String createUser(String googleUserId, + String email, String firstName, String lastName, String avatarUrl, XWikiContext context) + { + try { + // user not found.. need to create new user + String xwikiUser = email.substring(0, email.indexOf("@")); + // make sure user is unique + xwikiUser = context.getWiki().getUniquePageName(XWIKISPACE, xwikiUser, context); + // create user + DocumentReference userDirRef = new DocumentReference( + context.getWikiId(), "Main", "UserDirectory"); + String randomPassword = + Integer.toString((int) (Math.pow(10, 8) + + Math.floor(Math.random() * Math.pow(10, 7))), 10); + Map userAttributes = new HashMap<>(); + + if (firstName != null) { + userAttributes.put(FIRSTNAME, firstName); + } + if (lastName != null) { + userAttributes.put(LASTNAME, lastName); + } + userAttributes.put(EMAIL, email); + userAttributes.put(PASSWORD, randomPassword); + int isCreated = context.getWiki().createUser(xwikiUser, userAttributes, + userDirRef, null, null, "edit", context); + // Add google apps id to the user + if (isCreated == 1) { + log.debug("Creating user " + xwikiUser); + XWikiDocument userDoc = context.getWiki() + .getDocument(createUserReference(xwikiUser), context); + BaseObject userObj = userDoc.getXObject(getXWikiUserClassRef()); + + userObj.set("active", 1, context); + fetchUserImage(userDoc, userObj, avatarUrl); + // TODO: check first and last names are written + + userDoc.createXObject(getGoogleAuthClassReference(), context); + BaseObject gAppsAuthClass = userDoc.getXObject(getGoogleAuthClassReference()); + gAppsAuthClass.set(ID, googleUserId, context); + context.getWiki().saveDocument(userDoc, "Google Apps login user creation", false, context); + } else { + log.debug("User creation failed"); + xwikiUser = null; + } + return xwikiUser; + } catch (Exception e) { + throw new GoogleAppsException(e); + } + } + + private void updateUser(String xwikiUser, + String email, String firstName, String lastName, String avatarUrl, String googleUserId, + XWikiContext context) + { + + try { + log.debug("Found user " + xwikiUser); + XWikiDocument userDoc = context.getWiki().getDocument(createUserReference(xwikiUser), context); + BaseObject userObj = userDoc.getXObject(getXWikiUserClassRef()); + if (userObj == null) { + log.debug("User found is not a user"); + } else { + boolean changed = updateField(userObj, email, EMAIL, context) + || updateField(userObj, firstName, FIRSTNAME, context) + || updateField(userObj, lastName, LASTNAME, context); + changed = changed || fetchUserImage(userDoc, userObj, avatarUrl); + + BaseObject googleAppsAuth = userDoc.getXObject(getGoogleAuthClassReference()); + if (googleAppsAuth == null) { + userDoc.createXObject(getGoogleAuthClassReference(), context); + googleAppsAuth = userDoc.getXObject(getGoogleAuthClassReference()); + changed = true; + } + + changed = changed || updateField(googleAppsAuth, googleUserId, ID, context); + + if (changed) { + log.info("User changed."); + context.getWiki().saveDocument(userDoc, "Google Apps login user updated.", context); + } else { + log.info("User unchanged."); + } + } + } catch (Exception e) { + e.printStackTrace(); + throw new GoogleAppsException(e); + } + } + + private boolean updateField(BaseObject userObj, String value, String fieldName, XWikiContext context) + { + if (!userObj.getStringValue(fieldName).equals(value)) { + userObj.set(fieldName, value, context); + return true; + } else { + return false; + } + } + + private boolean fetchUserImage(XWikiDocument userDoc, BaseObject userObj, + String imgUrl) + { + try { + if (configScopeUseAvatar && imgUrl != null) { + String imageUrl = imgUrl + + (imgUrl.indexOf('?') > -1 ? "&" : '?') + + "sz=512"; + XWikiAttachment attachment = + userObj.getStringValue(AVATAR) == null ? null + : userDoc.getAttachment(userObj.getStringValue(AVATAR)); + java.net.URL u = new URL(imageUrl); + HttpURLConnection conn = (HttpURLConnection) u.openConnection(); + + if (attachment != null) { + conn.addRequestProperty("If-Modified-Since", + DateUtil.formatDate(attachment.getDate())); + } + + if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { + XWikiContext context = contextProvider.get(); + log.debug("Pulling avatar " + imageUrl); + + String fileName = imageUrl.substring(imageUrl.lastIndexOf('/') + 1); + if (fileName.contains("?")) { + fileName = fileName.substring(0, fileName.indexOf('?')); + } + log.debug("Avatar changed " + fileName); + userObj.set(AVATAR, fileName, context); + userDoc.addAttachment(fileName, conn.getInputStream(), context).setDate( + new Date(conn.getLastModified())); + return true; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + + DocumentReference getXWikiUserClassRef() + { + return new DocumentReference(WIKINAME, XWIKISPACE, "XWikiUsers"); + } + + /** + * @param userName the name of the user + * @return A DocumentReference for the given username. + * @since 3.0 + */ + @Unstable + DocumentReference createUserReference(String userName) + { + return userResolver.resolve(userName); + } + + private DocumentReference getGoogleAuthClassReference() + { + if (gauthClassRef == null) { + gauthClassRef = new DocumentReference(WIKINAME, SPACENAME, "GoogleAppsAuthClass"); + } + return gauthClassRef; + } + + File getPermanentDir() + { + return permanentDir; + } + + public String getUserEmail(String userId) + { + try { + XWikiContext context = contextProvider.get(); + XWikiDocument userDoc = context.getWiki().getDocument(createUserReference(userId), + context); + String userEmail = null; + BaseObject userObj = userDoc.getXObject(getXWikiUserClassRef(), false, + context); + // userclass "XWiki.XWikiUsers" + + if (userObj != null) { + userEmail = userDoc.getStringValue(EMAIL); + } + return userEmail; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } +} + diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleDriveAccess.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleDriveAccess.java new file mode 100644 index 0000000..18f2da9 --- /dev/null +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleDriveAccess.java @@ -0,0 +1,235 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package com.xwiki.googleapps.internal; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.tuple.Pair; +import org.apache.tika.Tika; +import org.slf4j.Logger; +import org.xwiki.stability.Unstable; + +import com.google.api.client.auth.oauth2.Credential; +import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; +import com.google.api.client.http.InputStreamContent; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.services.drive.Drive; +import com.google.api.services.drive.model.File; +import com.google.api.services.drive.model.FileList; +import com.xwiki.googleapps.DriveDocMetadata; +import com.xwiki.googleapps.GoogleAppsException; + +class GoogleDriveAccess implements GoogleAppsConstants +{ + + // ----- communication tools + + private final GoogleAppsIdentity gaIdentity; + + private final GoogleAppsXWikiObjects gaXWikiObjects; + + private final Logger log; + + private final JacksonFactory jacksonFactory; + + private final NetHttpTransport httpTransport; + + GoogleDriveAccess(Logger log, + GoogleAppsXWikiObjects gaXWikiObjects, GoogleAppsIdentity gaIdentity) + { + try { + this.log = log; + this.gaXWikiObjects = gaXWikiObjects; + this.gaIdentity = gaIdentity; + this.jacksonFactory = JacksonFactory.getDefaultInstance(); + this.httpTransport = GoogleNetHttpTransport.newTrustedTransport(); + } catch (Exception e) { + e.printStackTrace(); + throw new GoogleAppsException("Trouble at building GoogleDriveAccess", e); + } + } + + /** + * Builds and returns an authorized Drive client service. + * + * @return an authorized Drive client service + */ + private Drive getDriveService() + { + Credential credential = gaIdentity.authorize(false); + return new Drive.Builder( + httpTransport, jacksonFactory, credential) + .setApplicationName(gaXWikiObjects.getConfigAppName()) + .build(); + } + + /** + * Fetches a list of Google Drive document matching a substring query in the filename. + * + * @param query the expected query (e.g. fullText contains winter ski) + * @param nbResults max number of results + * @return The list of objects documenting the Google Drive documents. + * @since 3.0 + */ + @Unstable + List listDriveDocuments(String query, int nbResults) + { + try { + Drive drive = getDriveService(); + Drive.Files.List req = drive.files().list() + .setFields("items(id,mimeType,title,exportLinks,selfLink,version,alternateLink)") + .setMaxResults(nbResults); + if (query != null && query.length() > 0) { + req.setQ(query); + } + FileList result = req.execute(); + List files = result.getItems(); + List r = new ArrayList<>(files.size()); + for (File file : files) { + r.add(createDriveDocMetadata(file, null)); + } + return r; + } catch (IOException e) { + throw new GoogleAppsException(e); + } + } + + /** + * Fetches the google-drive document's representation and stores it as attachment. + * + * @param page attach to this page + * @param name attach using this file name + * @param id store object attached to this attachment using this id (for later sync) + * @param mediaType content-type of the file to be fetched (or "unknown"; in this case the mediaType is read from + * Tika. + * @since 3.0 + */ + @Unstable + void retrieveFileFromGoogle(String page, String name, String id, String mediaType) + { + Drive driveService = getDriveService(); + String mt = mediaType; + + try { + if ("unknown".equalsIgnoreCase(mediaType) || mediaType == null || !mediaType.contains("/")) { + mt = new Tika().detect(name); + } + log.debug("Retrieving " + name + " to page " + page + ": " + id + "(mediatype " + mt + ")."); + InputStream downloadStream = driveService.files().export(id, mt).executeMediaAsInputStream(); + String user = driveService.about().get().execute().getUser().getEmailAddress(); + File docData = driveService.files().get(id).execute(); + createDriveDocMetadata(docData, user); + gaXWikiObjects.saveFileToXWiki(page, id, name, downloadStream, createDriveDocMetadata(docData, user)); + } catch (Exception e) { + log.info(e.getMessage(), e); + throw new GoogleAppsException("Trouble at retrieving from Google.", e); + } + } + + DriveDocMetadata getEmbedData(String docId) + { + try { + File docData = getDriveService().files().get(docId).execute(); + DriveDocMetadata ddm = new DriveDocMetadata(); + ddm.id = docId; + ddm.embedLink = docData.getEmbedLink(); + if (ddm.embedLink == null) { + ddm.embedLink = docData.getAlternateLink(); + } + ddm.editLink = docData.getAlternateLink(); + ddm.fileName = docData.getOriginalFilename() != null ? docData.getOriginalFilename() : docData.getTitle(); + ddm.user = getDriveService().about().get().execute().getUser().getEmailAddress(); + ddm.version = docData.getVersion().toString(); + return ddm; + } catch (IOException e) { + throw new GoogleAppsException(e); + } + } + + /** + * Saves the attachment stored in XWiki to the Google drive of the user attached to the current logged-in user. + * + * @param page the XWiki page name + * @param name the attachment name + * @return a record with the keys fileName, exportLink, version, editLink, embedLink, and google-user's + * email-address + * @since 3.0 + */ + @Unstable + public DriveDocMetadata saveAttachmentToGoogle(String page, String name) + { + try { + log.debug("Starting saving attachment ${name} from page ${page}"); + Pair attachPair = gaXWikiObjects.getAttachment(name, page); + + File file = new File(); + file.setTitle(name); + file.setOriginalFilename(name); + InputStreamContent content = new InputStreamContent(attachPair.getRight(), attachPair.getLeft()); + Drive drive = getDriveService(); + String user = drive.about().get().execute().getUser().getEmailAddress(); + Drive.Files.Insert insert = drive.files().insert(file, content); + insert.setConvert(true); + File docData = insert.execute(); + if (docData != null) { + log.debug("File inserted " + docData); + DriveDocMetadata ddm = createDriveDocMetadata(docData, user); + gaXWikiObjects.insertSyncDocObject(page, name, ddm); + return ddm; + } else { + log.warn("File insert failed"); + throw new GoogleAppsException("Google did not let us save attachment", new IOException()); + } + } catch (Exception e) { + throw new GoogleAppsException("Couldn't save attachment to Google.", e); + } + } + + private DriveDocMetadata createDriveDocMetadata(File googleFile, String userName) + { + DriveDocMetadata ddm = new DriveDocMetadata(); + ddm.embedLink = googleFile.getEmbedLink() != null + ? googleFile.getEmbedLink() : googleFile.getAlternateLink(); + ddm.editLink = googleFile.getAlternateLink(); + ddm.version = Long.toString(googleFile.getVersion()); + ddm.fileName = googleFile.getOriginalFilename(); + if (ddm.fileName == null) { + ddm.fileName = googleFile.getTitle(); + } + ddm.id = googleFile.getId(); + ddm.user = userName; + if (googleFile.getExportLinks() != null) { + for (String elink : googleFile.getExportLinks().values()) { + int index = elink.indexOf(EXPORTFORMATEQ) + 13; + String extension = elink.substring(index); + String newFileName = ddm.fileName + .replaceAll("\\.(doc|docx|odt|xls|xlsx|ods|pptx|svg|png|jpeg|pdf|)$", ""); + newFileName += '.' + extension; + ddm.addExportAlternative(extension, newFileName, elink); + } + } + ddm.exportLink = googleFile.getDownloadUrl(); + return ddm; + } +} diff --git a/api/src/main/resources/META-INF/components.txt b/api/src/main/resources/META-INF/components.txt index 695843a..974804e 100644 --- a/api/src/main/resources/META-INF/components.txt +++ b/api/src/main/resources/META-INF/components.txt @@ -1,4 +1,2 @@ com.xwiki.googleapps.internal.GoogleAppsManagerImpl -com.xwiki.googleapps.internal.CookieAuthenticationPersistenceImpl -com.xwiki.googleapps.internal.GoogleAppsAuthServiceImpl com.xwiki.googleapps.GoogleAppsScriptService diff --git a/ui/pom.xml b/ui/pom.xml index 8c952fe..c607523 100644 --- a/ui/pom.xml +++ b/ui/pom.xml @@ -81,6 +81,20 @@ INSERT_TEXT src/main/resources/GoogleApps/GoogleAppsConfigClass.js + + GoogleApps/DriveMacro.xml + /xwikidoc/object[className[text()='XWiki.WikiMacroClass']]/property/code + INSERT_TEXT + src/main/resources/GoogleApps/DriveMacro.groovy + + + GoogleApps/LoginUIExtension.xml + /xwikidoc/object[className[text()='XWiki.UIExtensionClass']]/property/content + INSERT_TEXT + src/main/resources/GoogleApps/LoginUIExtension.vm + + + diff --git a/ui/src/main/resources/GoogleApps/DriveMacro.groovy b/ui/src/main/resources/GoogleApps/DriveMacro.groovy new file mode 100644 index 0000000..3a66148 --- /dev/null +++ b/ui/src/main/resources/GoogleApps/DriveMacro.groovy @@ -0,0 +1,96 @@ +{{groovy}} +def mainReference = services.model.createDocumentReference('', 'GoogleApps','OAuth') +if (!services.licensing.licensor.hasLicensureForEntity(mainReference)) { + println """ + {{error}}{{translation key='googleapps.error.noValidLicense' /}}{{/error}} + """ +} else { + def id = xcontext.macro.params.id; + def width = xcontext.macro.params.width; + def height = xcontext.macro.params.height; + def nb = Integer.parseInt(xcontext.macro.params.nb); + def obj = doc.getObject("GoogleApps.SynchronizedDocumentClass", nb) + def escapetool = new org.xwiki.velocity.tools.EscapeTool(); + def force = false; + def googleApps = services.googleApps + + if(!googleApps.active || !googleApps.driveEnabled) { + println services.localization.render('googleapps.error.driveDisabled') + } else { + // adding stylesheet + xwiki.ssx.use("GoogleApps.DriveMacro"); + + if (xcontext.macro.params.authentication) { + if (googleApps.authorize(false)) { + def url = googleApps.getAuthorizationURL() + println services.localization.render("googleapps.macro.maybeReqAuth", ["[[", ">>url:${url}]]"]); + } + } + + if (nb==null) + nb = 0; + def embednb = (!request.nb) ? 0 : Integer.parseInt(request.nb) + def query = request.getParameter("query${nb}") + + if (request.update=="1" && nb==embednb) + force = true; + + if (request.embed=="1") { + if (embednb==nb) { + obj = googleApps.createOrUpdateEmbedObject(request.id, doc, obj, nb); + doc.use(obj); + } + } + + if (id!=null) { + println id; + } else if (obj!=null && !force) { + doc.use(obj); + def embedLink = doc.getValue("embedLink") + def editLink = doc.getValue("editLink") + def swidth = (width.endsWith("%")) ? width : width + "px"; + print """(% class="drive-links" style="width: ${swidth};" %)(((""" + print """[[Change>>||queryString="update=1&nb=${nb}"]]""" + if (editLink && editLink.startsWith("http")) + print """ - [[Edit>>url:${editLink}]]""" + println ")))" + println """{{html clean=false}}{{/html}}""" + } else { + def tquery = "" + if (query) + tquery = escapetool.xml(query) + println """ + +{{translation key='googleapps.macro.mainHint'/}} + + {{html clean=false wiki=true}} +
+ {{translation key='googleapps.macro.insert'/}} + + + + +
+ {{/html}} + """ + if (query && embednb==nb) { + def squery = "'" + query + "'" + def results = googleApps.listDriveDocuments("fullText contains ${squery}", 10) + def nbres = results.size(); + + println "${nbres} documents found: " + for (entry in results) { + def docName = entry.title; + def embedLink = entry.embedLink; + if (embedLink==null) + embedLink = entry.alternateLink; + if (embedLink==null) + println """* ${docName}: """ + services.localization.render('googleapps.macro.canBeEmbedded') + else + println """* ${docName}: [[{{translation key='googleapps.macro.nowEmbed'/}}>>||queryString="embed=1&nb=${nb}&id=${entry.id}"]]""" + } + } + } + } +} +{{/groovy}} \ No newline at end of file diff --git a/ui/src/main/resources/GoogleApps/DriveMacro.xml b/ui/src/main/resources/GoogleApps/DriveMacro.xml index 6e0e603..596c1ef 100644 --- a/ui/src/main/resources/GoogleApps/DriveMacro.xml +++ b/ui/src/main/resources/GoogleApps/DriveMacro.xml @@ -145,7 +145,7 @@ margin-bottom: 0; padding-bottom: 0; } - + CSS @@ -275,102 +275,7 @@ XWiki.WikiMacroClass 41e5d116-4e82-408a-a49e-eac3af94af90 - {{groovy}} -def mainReference = services.model.createDocumentReference('', 'GoogleApps','OAuth') -if (!services.licensing.licensor.hasLicensureForEntity(mainReference)) { - println """ - {{error}}{{translation key='googleapps.error.noValidLicense' /}}{{/error}} - """ -} else { -def id = xcontext.macro.params.id; -def width = xcontext.macro.params.width; -def height = xcontext.macro.params.height; -def nb = Integer.parseInt(xcontext.macro.params.nb); -def obj = doc.getObject("GoogleApps.SynchronizedDocumentClass", nb) -def escapetool = new org.xwiki.velocity.tools.EscapeTool(); -def force = false; -def googleApps = services.googleApps - -if(!googleApps.active || !googleApps.driveEnabled) { - println services.localization.render('googleapps.error.driveDisabled') -} else { - // adding stylesheet - xwiki.ssx.use("GoogleApps.DriveMacro"); - - if (xcontext.macro.params.authentication) { - if (googleApps.authorize(false)==null) { - def url = googleApps.getAuthorizationURL() - println services.localization.render("googleapps.macro.maybeReqAuth", ["[[", ">>url:${url}]]"]); - } - } - - if (nb==null) - nb = 0; - def embednb = (!request.nb) ? 0 : Integer.parseInt(request.nb) - def query = request.getParameter("query${nb}") - - if (request.update=="1" && nb==embednb) - force = true; - - if (request.embed=="1") { - if (embednb==nb) { - obj = googleApps.createOrUpdateEmbedObject(request.id, doc, obj, nb); - doc.use(obj); - } - } - - if (id!=null) { - println id; - } else if (obj!=null && !force) { - doc.use(obj); - def embedLink = doc.getValue("embedLink") - def editLink = doc.getValue("editLink") - def swidth = (width.endsWith("%")) ? width : width + "px"; - print """(% class="drive-links" style="width: ${swidth};" %)(((""" - print """[[Change>>||queryString="update=1&nb=${nb}"]]""" - if (editLink && editLink.startsWith("http")) - print """ - [[Edit>>url:${editLink}]]""" - println ")))" - println """{{html clean=false}}<iframe src="${embedLink}" width="${width}" height="${height}"></iframe>{{/html}}""" - } else { - def tquery = "" - if (query) - tquery = escapetool.xml(query) - println """ - -{{translation key='googleapps.macro.mainHint'/}} - - {{html clean=false wiki=true}} - <form action="" method="get"> - {{translation key='googleapps.macro.insert'/}} - <input type="hidden" name="update" value="1" /> - <input type="hidden" name="nb" value="${nb}" /> - <input type="text" name="query${nb}" value="${tquery}" /> - <input type="submit" value="Search" /> - </form> - {{/html}} - """ - if (query && embednb==nb) { - def squery = "'" + query + "'" - def results = googleApps.listDriveDocumentsWithTypes("fullText contains ${squery}", 10) - def nbres = results.size(); - - println "${nbres} documents found: " - for (entry in results) { - def docName = entry.title; - def embedLink = entry.embedLink; - if (embedLink==null) - embedLink = entry.alternateLink; - if (embedLink==null) - println """* ${docName}: """ + services.localization.render('googleapps.macro.canBeEmbedded') - else - println """* ${docName}: [[{{translation key='googleapps.macro.nowEmbed'/}}>>||queryString="embed=1&nb=${nb}&id=${entry.id}"]]""" - } - } - } - } -} -{{/groovy}} + diff --git a/ui/src/main/resources/GoogleApps/DriveMacroTest.xml b/ui/src/main/resources/GoogleApps/DriveMacroTest.xml index 88c674d..0e8d2b6 100644 --- a/ui/src/main/resources/GoogleApps/DriveMacroTest.xml +++ b/ui/src/main/resources/GoogleApps/DriveMacroTest.xml @@ -39,15 +39,15 @@ false xwiki/2.1 true - {{velocity}}## + {{velocity}} {{translation key='googleapps.macro.testIntro'/}} - #if($doc.hasAccessLevel("edit"))## + #if($doc.hasAccessLevel("edit")) {{drive width="600" height="400" authentication="1" /}} #else - #if($doc.getObject(""))## + #if($doc.getObject("")) {{drive width="600" height="400" authentication="1" /}} - #else## + #else {{translation key='googleapps.macro.cantTest'/}} #end #end diff --git a/ui/src/main/resources/GoogleApps/EditInGoogleApps.xml b/ui/src/main/resources/GoogleApps/EditInGoogleApps.xml index 72c9c5e..1192bcf 100644 --- a/ui/src/main/resources/GoogleApps/EditInGoogleApps.xml +++ b/ui/src/main/resources/GoogleApps/EditInGoogleApps.xml @@ -42,25 +42,25 @@ {{velocity}} #set($googleApps = $services.googleApps) #if($googleApps.active && $googleApps.driveEnabled) - #set($gdoc = $googleApps.getGoogleDocument($request.page, $request.name)) + #set($gdoc = $googleApps.getSyncDocMetadata($request.page, $request.name)) #if($request.confirm) - {{translation key='googleapps.editInGA.uploading'/}} - #set($gdoc = $googleApps.saveAttachmentToGoogle($request.page, $request.name)) + {{translation key='googleapps.editInGA.uploading'/}} + #set($gdoc = $googleApps.saveAttachmentToGoogle($request.page, $request.name)) #end #if($gdoc) - {{translation key='googleapps.editInGA.isInGDrive'/}} + {{translation key='googleapps.editInGA.isInGDrive'/}} - (% class="buttonwrapper" %) - {{html clean=false}}<a href="${gdoc.editLink}" target="_blank">$services.localization.render("googleapps.edit.editingoogle.button")</a>{{/html}} + (% class="buttonwrapper" %) + {{html clean=false}}<a href="${gdoc.editLink}" target="_blank">$services.localization.render("googleapps.edit.editingoogle.button")</a>{{/html}} - $services.localization.render("googleapps.edit.desc") + $services.localization.render("googleapps.edit.desc") - (% class="buttonwrapper" %)[[$services.localization.render("googleapps.edit.retrieve.button")>>RetrieveFromGoogleApps||queryString="page=${escapetool.url($request.page)}&name=${escapetool.url($request.name)}&url=${escapetool.url($gdoc.exportLink)}&id=${gdoc.id}"]] + (% class="buttonwrapper" %)[[$services.localization.render("googleapps.edit.retrieve.button")>>RetrieveFromGoogleApps||queryString="page=${escapetool.url($request.page)}&name=${escapetool.url($request.name)}&url=${escapetool.url($gdoc.exportLink)}&id=${gdoc.id}"]] #else - {{translation key='googleapps.editInGA.notYetInGDrive'/}} + {{translation key='googleapps.editInGA.notYetInGDrive'/}} - (% class="buttonwrapper" %)[[$services.localization.render("googleapps.edit.confirm")>>||queryString="page=${escapetool.url($request.page)}&name=${escapetool.url($request.name)}&confirm=1"]] + (% class="buttonwrapper" %)[[$services.localization.render("googleapps.edit.confirm")>>||queryString="page=${escapetool.url($request.page)}&name=${escapetool.url($request.name)}&confirm=1"]] #end #else {{translation key='googleapps.error.driveDisabled'/}} diff --git a/ui/src/main/resources/GoogleApps/GoogleAppsConfigSheet.vm b/ui/src/main/resources/GoogleApps/GoogleAppsConfigSheet.vm index 55385e6..6c54115 100644 --- a/ui/src/main/resources/GoogleApps/GoogleAppsConfigSheet.vm +++ b/ui/src/main/resources/GoogleApps/GoogleAppsConfigSheet.vm @@ -8,10 +8,12 @@ $xwiki.ssx.use('GoogleApps.GoogleAppsConfigClass') #set($className="GoogleApps.GoogleAppsConfigClass") #set($propNamePrefix="${configDoc.fullName}_${className}_0") #set($obj = $configDoc.getObject($className)) + ## shorthand for t(ranslation) and tp(translation with parameters). #macro (t $name) $services.localization.render("GoogleApps.GoogleAppsConfigClass_${name}") #end + #macro (tp $name $params) #set ($transNamePrefix = 'GoogleApps.GoogleAppsConfigClass_') #if ($name.startsWith("googleapps")) @@ -21,6 +23,7 @@ $xwiki.ssx.use('GoogleApps.GoogleAppsConfigClass') ## convert to link-text for the simple wiki syntax of links $stringtool.replacePattern($msg, '\[\[([^>]*)>>([^\]]*)\]\]', '$1') #end + #macro (displayInput $property) #set ($output = $doc.display($property, 'edit', $obj)) #set ($output = $stringtool.removeStart($output, '{{html clean="false" wiki="false"}}')) diff --git a/ui/src/main/resources/GoogleApps/ImportFromGoogleApps.xml b/ui/src/main/resources/GoogleApps/ImportFromGoogleApps.xml index 6cf5d93..46573ae 100644 --- a/ui/src/main/resources/GoogleApps/ImportFromGoogleApps.xml +++ b/ui/src/main/resources/GoogleApps/ImportFromGoogleApps.xml @@ -45,29 +45,30 @@ {{translation key='googleapps.importGA.explain' parameters='${pagetitle},${pagedoc.fullName}'/}} {{html clean==false wiki=true}} -<form action="" method="get"> +<form action="" method="get"> {{translation key='googleapps.importGA.search'/}} -<input type="text" name="query" value="$!{request.query}" /> -<input type="hidden" name="page" value="$!{request.page}" /> -<input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" /> -<input type="submit" value="Search" /> -</form> +<input type="text" name="query" value="$!{request.query}" /> +<input type="hidden" name="page" value="$!{request.page}" /> +<input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" /> +<input type="submit" value="Search" /> +</form> {{/html}} #if($request.query) #set($googleApps = $services.googleApps) #set($squery = "'" + $request.query + "'") -#set($result = $googleApps.listDriveDocumentsWithTypes("fullText contains ${squery}", 20)) +#set($result = $googleApps.listDriveDocuments("fullText contains ${squery}", 20)) #if(!$result.empty) #foreach($entry in $result) - #set($docName = $entry.title) -* $docName #foreach($elink in $entry.exportLinks) - #set($exportData = $googleApps.getExportLink($docName, $elink)) - #set($stype = $exportData[0]) - #set($newDocName = $exportData[1]) - [[${stype}>>RetrieveFromGoogleApps||queryString="page=${escapetool.url($request.page)}&name=${escapetool.url($newDocName)}&url=${escapetool.url($elink)}&editLink=${escapetool.url($entry.alternateLink)}&version=${entry.version}&mediaType=${stype}&id=${entry.id}"]] #end + #set($docName = $entry.fileName) +* $docName: #foreach($alternative in $entry.exportLinksAlternatives) + #set($elink = $alternative.exportUrl) + #set($stype = $alternative.extension) + #set($newFileName = $alternative.newFileName) + [[${stype}>>RetrieveFromGoogleApps||queryString="page=${escapetool.url($request.page)}&name=${escapetool.url($newFileName)}&url=${escapetool.url($elink)}&editLink=${escapetool.url($entry.editLink)}&version=${entry.version}&id=${entry.id}"]] #end + #if($entry.downloadUrl) [[${stype}>>RetrieveFromGoogleApps||queryString="page=${escapetool.url($request.page)}&name=${escapetool.url($newFileName)}&url=${escapetool.url($downloadUrl)}&editLink=${escapetool.url($entry.editLink)}&version=${entry.version}&id=${entry.id}"]]#end - #end +#end #else {{translation key='googleapps.importGA.empty'/}} #end diff --git a/ui/src/main/resources/GoogleApps/Install.xml b/ui/src/main/resources/GoogleApps/Install.xml index e1e4155..335eddd 100644 --- a/ui/src/main/resources/GoogleApps/Install.xml +++ b/ui/src/main/resources/GoogleApps/Install.xml @@ -41,14 +41,14 @@ true {{velocity}} #if(!$xwiki.hasAdminRights()) -You are running this script as a non admin. It will have no effect. Login as admin. + You are running this script as a non admin. It will have no effect. Login as admin. #else -This script automatically set the owner of the pages in the Google Apps Application. This will allow the priviledged scripts included in them to work. + This script automatically set the owner of the pages in the Google Apps Application. This will allow the priviledged scripts included in them to work. #end #if($request.confirm=="1") -Assigning programming rights to the following pages: + Assigning programming rights to the following pages: #else -[[Confirm assigning programming rights to the following pages:>>$doc.fullName?confirm=1]] + [[Confirm assigning programming rights to the following pages:>>$doc.fullName?confirm=1]] #end #foreach($item in $xwiki.searchDocuments("where doc.web='GoogleApps'")) @@ -61,11 +61,11 @@ Assigning programming rights to the following pages: #set($ok = $transdoc.use("XWiki.XWikiPreferences")) #set($transprefs = $transdoc.getValue("documentBundles")) #if($transprefs.indexOf("GoogleApps.Translations")==-1) -#if($request.confirm=="1") -#set($transprefs = "${transprefs},GoogleApps.Translations") -#set($ok = $transdoc.set("documentBundles", $transprefs)) -#set($ok = $transdoc.save()) -#end + #if($request.confirm=="1") + #set($transprefs = "${transprefs},GoogleApps.Translations") + #set($ok = $transdoc.set("documentBundles", $transprefs)) + #set($ok = $transdoc.save()) + #end * Added translation bundle to XWiki Preferences #end {{/velocity}} diff --git a/ui/src/main/resources/GoogleApps/JSExtension.xml b/ui/src/main/resources/GoogleApps/JSExtension.xml index 3436030..59a3d77 100644 --- a/ui/src/main/resources/GoogleApps/JSExtension.xml +++ b/ui/src/main/resources/GoogleApps/JSExtension.xml @@ -121,7 +121,7 @@ #set($googleApps = $services.googleApps) #if($googleApps.active && $googleApps.driveEnabled) -var listener = function(event) { + var listener = function(event) { if (event.memo.id == 'Attachments') { var buttons = $$(".xwikibuttonlinks") for (i=0;i<=buttons.length;i++) diff --git a/ui/src/main/resources/GoogleApps/Login.xml b/ui/src/main/resources/GoogleApps/Login.xml deleted file mode 100644 index d9e1e44..0000000 --- a/ui/src/main/resources/GoogleApps/Login.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - GoogleApps - Login - - - 0 - GoogleApps.WebHome - xwiki:XWiki.Admin - xwiki:XWiki.Admin - - xwiki:XWiki.Admin - 1.1 - $services.localization.render("googleapps.login.title") - - - - false - xwiki/2.1 - true - {{velocity}} -#set($googleApps = $services.googleApps) -#if($googleApps.active) - #set($result = $googleApps.updateUser()) - #if($result=="failed login") - #set($user = $googleApps.getGoogleUser()) - #set($email = $user.emails[0].value) - ${services.localization.render("googleapps.login.domainerror1")} - - ${services.localization.render("googleapps.login.domainerror2")} $!{email}. - ${services.localization.render("googleapps.login.domainerror3")} - - #elseif($result=="ok") - ${services.localization.render("googleapps.login.redirectmessage")} - - #if($request.xredirect && $request.xredirect!="") - $response.sendRedirect($request.xredirect) - #else - $response.sendRedirect($xwiki.getURL("Main.WebHome")) - #end - #else ## $result is "no user" - ${services.localization.render("googleapps.login.message")} - - ${services.localization.render("googleapps.login.error")} - #end -#else - ${services.localization.render("googleapps.error.extensionDisabled")} -#end -{{/velocity}} - diff --git a/ui/src/main/resources/GoogleApps/LoginExtension.xml b/ui/src/main/resources/GoogleApps/LoginExtension.xml deleted file mode 100644 index 3ad779c..0000000 --- a/ui/src/main/resources/GoogleApps/LoginExtension.xml +++ /dev/null @@ -1,248 +0,0 @@ - - - - - - GoogleApps - LoginExtension - - - 0 - GoogleApps.WebHome - xwiki:XWiki.Admin - xwiki:XWiki.Admin - - xwiki:XWiki.Admin - 1.1 - Login Javascript Extension - - - - false - xwiki/2.1 - true - - - XWiki.JavaScriptExtension - - - - - - - - - 0 - 0 - select - 0 - cache - 5 - Caching policy - 0 - - ,| - 1 - 0 - long|short|default|forbid - com.xpn.xwiki.objects.classes.StaticListClass - - - 0 - code - 2 - Code - 20 - 50 - 0 - com.xpn.xwiki.objects.classes.TextAreaClass - - - 0 - name - 1 - Name - 30 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - 0 - select - yesno - parse - 4 - Parse content - 0 - com.xpn.xwiki.objects.classes.BooleanClass - - - 0 - 0 - select - 0 - use - 3 - Use this extension - 0 - - ,| - 1 - 0 - currentPage|onDemand|always - com.xpn.xwiki.objects.classes.StaticListClass - - - GoogleApps.LoginExtension - 0 - XWiki.JavaScriptExtension - 03df4bd2-ff8e-4084-8694-f7cf390352e5 - - long - - - #set($url = $xwiki.getURL("GoogleApps.Login", "view")) - -function loginWithXWiki() { - jQuery(".panel .panel-body dl").show() - return false; -} - -require(['jquery', 'xwiki-events-bridge', 'xwiki-meta'], function($, xm) { - $(document).ready(function(event, data) { - if (XWiki.contextaction == "login" || XWiki.contextaction == "loginsubmit" ) { - jQuery('<div id="googleapps-login-choice">' - + '<div class="col-xs-12" style="margin-bottom: 20px; padding: 20px;">' - + '<a href="${url}?' + location.href.substring(location.href.indexOf("?")) - + '" class="btn btn-primary col-xs-5" href="">${escapetool.javascript($services.localization.render("googleapps.login.withgoogle"))}</a>' - + '<div class="col-xs-2"></div>' - + '<a href="javascript:void(0)" onclick="return loginWithXWiki()" class="btn btn-primary col-xs-5" href="">${escapetool.javascript($services.localization.render("googleapps.login.withxwiki"))}</a></div>' - + '<div style="clear: both;"></div></div>').insertBefore(jQuery(".panel .panel-body dl")) - } - if (XWiki.contextaction != "loginsubmit") { - jQuery(".panel .panel-body dl").hide(); - } - }); // end document ready - -}); // end requirejs - - - - LoginExtension - - - 1 - - - onDemand - - - - - XWiki.UIExtensionClass - - - - - - - - - 0 - content - 3 - Extension Content - 10 - 40 - 0 - com.xpn.xwiki.objects.classes.TextAreaClass - - - 0 - extensionPointId - 1 - Extension Point ID - 30 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - 0 - name - 2 - Extension ID - 30 - 0 - com.xpn.xwiki.objects.classes.StringClass - - - 0 - parameters - 4 - Extension Parameters - 10 - 40 - 0 - com.xpn.xwiki.objects.classes.TextAreaClass - - - 0 - 0 - select - 0 - scope - 5 - Extension Scope - 0 - - ,| - 1 - 0 - wiki=Current Wiki|user=Current User|global=Global - com.xpn.xwiki.objects.classes.StaticListClass - - - GoogleApps.LoginExtension - 0 - XWiki.UIExtensionClass - d929f4a6-d7a7-412c-acb9-e77c9bc3038a - - {{velocity}} -#if("${doc}" == "XWiki.XWikiLogin") - $xwiki.jsx.use('xwiki:GoogleApps.LoginExtension') -#end -{{/velocity}} - - - org.xwiki.platform.template.header.after - - - xwiki:GoogleApps.Login - - - - - - global - - - - diff --git a/ui/src/main/resources/GoogleApps/LoginUIExtension.vm b/ui/src/main/resources/GoogleApps/LoginUIExtension.vm new file mode 100644 index 0000000..825838e --- /dev/null +++ b/ui/src/main/resources/GoogleApps/LoginUIExtension.vm @@ -0,0 +1,99 @@ +{{velocity}}{{html clean="false" wiki="false"}} + #if ($context.action=="login" && $doc.fullName=="XWiki.XWikiLogin") + #set($googleApps = $services.googleApps) + #if($googleApps.active) + #if ($request.googleLogin == "oauthReturn") + + #set($result = $googleApps.updateUser()) + #set($prfx = "failed login:") + #if ($result.startsWith($prfx)) + #set($email = $result.substring($prfx.length())) + #set($errorMsg = " ${services.localization.render('googleapps.login.domainerror1')} ") + #set($errorMsg = " ${errorMsg} ${services.localization.render('googleapps.login.domainerror2')} $!{email}.") + #set($errorMsg = " ${errorMsg} ${services.localization.render('googleapps.login.domainerror3')}") + #elseif ($result=="ok") + #set($successM = ${services.localization.render("googleapps.login.redirectmessage")}) + #if ($request.xredirect && $request.xredirect!="") + $response.sendRedirect($request.xredirect) + #else + $response.sendRedirect($xwiki.getURL("Main.WebHome")) + #end + #else ## $result is "no user" + #set($errorMsg = ${services.localization.render('googleapps.login.message')}) + #set($errorMsg = " ${errorMsg} ${services.localization.render('googleapps.login.error')}") + #end + #elseif ($request.googleLogin == "start") + + #set($succesMsg = $services.localization.render("googleapps.login.oauth.message")) + #set($success = $googleApps.authorize(true)) + #if($success) + #if($request.state) + #set($successMsg = $services.localization.render("googleapps.login.oauth.successwithredirect")) + #else + #set($successMsg = $services.localization.render("googleapps.login.oauth.success")) + #end + #else + #set($errorMsg = $services.localization.render("googleapps.login.oauth.failedGoogleRequest")) + #end + #else + + #end + + #if ($successMsg || $errorMsg) + + + #end + + + + + #else + + #end ## googleApps is active + #end ## on login page +{{/html}}{{/velocity}} \ No newline at end of file diff --git a/ui/src/main/resources/GoogleApps/LoginUIExtension.xml b/ui/src/main/resources/GoogleApps/LoginUIExtension.xml new file mode 100644 index 0000000..c280eca --- /dev/null +++ b/ui/src/main/resources/GoogleApps/LoginUIExtension.xml @@ -0,0 +1,127 @@ + + + + + GoogleApps + LoginUIExtension + + + 0 + GoogleApps.WebHome + xwiki:XWiki.Admin + xwiki:XWiki.Admin + xwiki:XWiki.Admin + 1.1 + + <comment/> + <minorEdit>false</minorEdit> + <syntaxId>xwiki/2.1</syntaxId> + <hidden>true</hidden> + <content/> + <object><!-- TODO: replace with one such class from XWiki 8 or 9 --> + <name>GoogleApps.LoginUIExtension</name> + <number>0</number> + <className>XWiki.UIExtensionClass</className> + <guid>31588dab-0888-42ee-be22-fb6747740768</guid> + <class> + <name>XWiki.UIExtensionClass</name> + <customClass/> + <customMapping/> + <defaultViewSheet/> + <defaultEditSheet/> + <defaultWeb/> + <nameField/> + <validationScript/> + <content> + <disabled>0</disabled> + <editor>Text</editor> + <name>content</name> + <number>3</number> + <prettyName>Extension Content</prettyName> + <rows>10</rows> + <size>40</size> + <unmodifiable>0</unmodifiable> + <classType>com.xpn.xwiki.objects.classes.TextAreaClass</classType> + </content> + <extensionPointId> + <disabled>0</disabled> + <name>extensionPointId</name> + <number>1</number> + <prettyName>Extension Point ID</prettyName> + <size>30</size> + <unmodifiable>0</unmodifiable> + <classType>com.xpn.xwiki.objects.classes.StringClass</classType> + </extensionPointId> + <name> + <disabled>0</disabled> + <name>name</name> + <number>2</number> + <prettyName>Extension ID</prettyName> + <size>30</size> + <unmodifiable>0</unmodifiable> + <classType>com.xpn.xwiki.objects.classes.StringClass</classType> + </name> + <parameters> + <contenttype>PureText</contenttype> + <disabled>0</disabled> + <editor>PureText</editor> + <name>parameters</name> + <number>4</number> + <prettyName>Extension Parameters</prettyName> + <rows>10</rows> + <size>40</size> + <unmodifiable>0</unmodifiable> + <classType>com.xpn.xwiki.objects.classes.TextAreaClass</classType> + </parameters> + <scope> + <cache>0</cache> + <disabled>0</disabled> + <displayType>select</displayType> + <multiSelect>0</multiSelect> + <name>scope</name> + <number>5</number> + <prettyName>Extension Scope</prettyName> + <relationalStorage>0</relationalStorage> + <separator> </separator> + <separators>|, </separators> + <size>1</size> + <unmodifiable>0</unmodifiable> + <values>wiki=Current Wiki|user=Current User|global=Global</values> + <classType>com.xpn.xwiki.objects.classes.StaticListClass</classType> + </scope> + </class> + <property> + <content></content> + </property> + <property> + <extensionPointId>org.xwiki.platform.topmenu.left</extensionPointId> + </property> + <property> + <name>com.xwiki.googleapps.application-googleapps.loginUIX</name> + </property> + <property> + <parameters></parameters> + </property> + <property> + <scope></scope> + </property> + </object> +</xwikidoc> \ No newline at end of file diff --git a/ui/src/main/resources/GoogleApps/OAuth.xml b/ui/src/main/resources/GoogleApps/OAuth.xml index 52b4b7b..0db295e 100644 --- a/ui/src/main/resources/GoogleApps/OAuth.xml +++ b/ui/src/main/resources/GoogleApps/OAuth.xml @@ -44,8 +44,8 @@ $services.localization.render("googleapps.login.oauth.message") #set($googleApps = $services.googleApps) -#set($creds = $googleApps.authorize()) -#if($creds) +#set($success = $googleApps.authorize(true)) +#if($success) #if($request.state) $services.localization.render("googleapps.login.oauth.success") #else diff --git a/ui/src/main/resources/GoogleApps/RetrieveFromGoogleApps.xml b/ui/src/main/resources/GoogleApps/RetrieveFromGoogleApps.xml index e6a3d14..b9b52cb 100644 --- a/ui/src/main/resources/GoogleApps/RetrieveFromGoogleApps.xml +++ b/ui/src/main/resources/GoogleApps/RetrieveFromGoogleApps.xml @@ -42,14 +42,19 @@ <content>{{velocity}} #set($googleApps = $services.googleApps) #if($request.url || $request.mediaType) - #set($mediaType = $request.mediaType) - #if("$!{mediaType}"=="")#set($mediaType="unknown")#end - #set($ok = $googleApps.retrieveFileFromGoogle($request.page, $request.name, $request.id, $mediaType)) - #if(!$ok) -$services.localization.render("googleapps.retrieve.fail") $request.id - #end -#else -$services.localization.render("googleapps.retrieve.nodocument") + #set($mediaType = $request.mediaType) + #if("$!{mediaType}"=="")#set($mediaType="unknown")#end + #try() + #set($ok = $googleApps.retrieveFileFromGoogle($request.page, $request.name, $request.id, $mediaType)) + #set($url = $xwiki.getDocument($request.page).getURL("view", "#Attachments")) + $response.sendRedirect($url) + #end + #if ("$!exception" != '') + $services.localization.render("googleapps.retrieve.fail") $request.id + #displayException($exception) + #else + redirecting... + #end #end {{/velocity}}</content> </xwikidoc> diff --git a/ui/src/main/resources/GoogleApps/TestDocumentList.xml b/ui/src/main/resources/GoogleApps/TestDocumentList.xml index 7128758..f0b4ceb 100644 --- a/ui/src/main/resources/GoogleApps/TestDocumentList.xml +++ b/ui/src/main/resources/GoogleApps/TestDocumentList.xml @@ -52,7 +52,7 @@ {{translation key='googleapps.testDocList.about'/}} #set($googleApps = $services.googleApps) -#set($doclist = $googleApps.listDriveDocumentsWithTypes("",10)) +#set($doclist = $googleApps.listDriveDocuments("",10)) #foreach($item in $doclist) * [[$item.title $item.id>>$item.editLink]] #end diff --git a/ui/src/main/resources/GoogleApps/Translations.de.properties b/ui/src/main/resources/GoogleApps/Translations.de.properties index e885ca2..83e13b4 100644 --- a/ui/src/main/resources/GoogleApps/Translations.de.properties +++ b/ui/src/main/resources/GoogleApps/Translations.de.properties @@ -64,7 +64,6 @@ googleapps.import.nextresults.button=Weitere Ergebnisse googleapps.retrieve.title=Aus Google Apps abrufen googleapps.retrieve.fail=Fehler beim Abrufen aus Google Apps -googleapps.retrieve.nodocument=Kein Dokument kann abgerufen werden. googleapps.login.title=Google Apps Anmeldung googleapps.login.withgoogle=Mit Google anmelden @@ -118,6 +117,7 @@ googleapps.webHome.5=Siehe das {0}Drive Macro Test{1}, um es zu testen. ################################################## googleapps.error.authConfigNotFound=Die Authentifizierungskonfiguration, die die Anemdlung durhc Google erlauben sollte, ist nicht gefunden worden. Es ist wahrscheinlich, dass die Benutzer, die versuchen sich durch Google anuzmelden, nicht angemelddet werden. googleapps.error.authConfigShould=Die {0}Installationsanweisungen{1} schlagen vor, die folgende Zeilen in xwiki.cfg einzufügen +googleapps.retrieve.nodocument=Kein Dokument kann abgerufen werden. ################################################## ## until 2.4-rc-1 diff --git a/ui/src/main/resources/GoogleApps/Translations.de.xml b/ui/src/main/resources/GoogleApps/Translations.de.xml index 08a0b4f..46a69d0 100644 --- a/ui/src/main/resources/GoogleApps/Translations.de.xml +++ b/ui/src/main/resources/GoogleApps/Translations.de.xml @@ -35,6 +35,6 @@ <comment/> <minorEdit>false</minorEdit> <syntaxId>plain/1.0</syntaxId> - <hidden>false</hidden> + <hidden>true</hidden> <content/> </xwikidoc> diff --git a/ui/src/main/resources/GoogleApps/Translations.fr.properties b/ui/src/main/resources/GoogleApps/Translations.fr.properties index df5a767..2891c20 100644 --- a/ui/src/main/resources/GoogleApps/Translations.fr.properties +++ b/ui/src/main/resources/GoogleApps/Translations.fr.properties @@ -63,7 +63,6 @@ googleapps.import.nextresults.button=Résultats suivants googleapps.retrieve.title=Récuperer document de Google Apps googleapps.retrieve.fail=Échec de la récupération du document de Google Apps -googleapps.retrieve.nodocument=Aucun document donné pour la récupération. googleapps.login.title=Authentification Google Apps googleapps.login.withgoogle=Connexion avec Google @@ -74,7 +73,6 @@ googleapps.login.redirectmessage=Vous allez être redirigé vers la page demand googleapps.login.oauth.message=Authentication Google en cours googleapps.login.oauth.success=Vous avez été authentifié avec succès. googleapps.login.oauth.successwithredirect=Vous avez été authentifié avec succès. Vous allez être redirigé vers votre page. -googleapps.login.oauth.notauthenticated=Vous allez être redirigé vers Google pour l'authentification. googleapps.login.domainerror1=Vous n'êtes pas autorisé à vous connecter à cette instance XWiki. googleapps.login.domainerror2=Votre compte Google actuel est googleapps.login.domainerror3=Merci de vous déconnecter de ce compte et réessayer l'authentification. @@ -117,6 +115,8 @@ googleapps.webHome.5=Voyez la apge {0}Drive Macro Test{1} pour tester la macro. ################################################## googleapps.error.authConfigNotFound=La configuration de "l'authentificateur" permettant l'idenfitication par Google n'as pas été trouvée; il est probable que les utilisateurs qui tentent de s'inscrire par le biais de Google ne le feront pas. googleapps.error.authConfigShould=Les {0}instructions d''installation{1} suggèrent d''ajouter les lignes suivantes à xwiki.cfg +googleapps.retrieve.nodocument=Aucun document donné pour la récupération. +googleapps.login.oauth.notauthenticated=Vous allez être redirigé vers Google pour l'authentification. ################################################## ## until 2.4-rc-1 diff --git a/ui/src/main/resources/GoogleApps/Translations.fr.xml b/ui/src/main/resources/GoogleApps/Translations.fr.xml index c25bc53..11d66df 100644 --- a/ui/src/main/resources/GoogleApps/Translations.fr.xml +++ b/ui/src/main/resources/GoogleApps/Translations.fr.xml @@ -35,6 +35,6 @@ <comment/> <minorEdit>false</minorEdit> <syntaxId>plain/1.0</syntaxId> - <hidden>false</hidden> + <hidden>true</hidden> <content/> </xwikidoc> diff --git a/ui/src/main/resources/GoogleApps/Translations.properties b/ui/src/main/resources/GoogleApps/Translations.properties index 38823eb..8d012e2 100644 --- a/ui/src/main/resources/GoogleApps/Translations.properties +++ b/ui/src/main/resources/GoogleApps/Translations.properties @@ -64,7 +64,6 @@ googleapps.import.nextresults.button=Next results googleapps.retrieve.title=Retrieve document from Google Apps googleapps.retrieve.fail=Failed to retrieve document from Google Apps -googleapps.retrieve.nodocument=No document has been given for retrieval. googleapps.login.title=Google Apps Login googleapps.login.withgoogle=Login with Google @@ -75,7 +74,7 @@ googleapps.login.redirectmessage=You are being redirected to your requested page googleapps.login.oauth.message=Google Authentication in progress googleapps.login.oauth.success=Successful authentication. googleapps.login.oauth.successwithredirect=Successful authentication. You are being redirected to your page. -googleapps.login.oauth.notauthenticated=You will be redirected to Google for Authentication. +googleapps.login.oauth.failedGoogleRequest=Cannot recognize your Google token, please try again. googleapps.login.domainerror1=This Google account does not allow you to login to this XWiki, because it does not match the expected domain name. googleapps.login.domainerror2=Your current Google account is googleapps.login.domainerror3=Please disconnect from this account, and try to login again. @@ -118,6 +117,8 @@ googleapps.webHome.5=See {0}Drive Macro Test{1} to try it out. ################################################## googleapps.error.authConfigNotFound=The authenticator configuration to enable Google identification has not been found; it is likely that users that attempt to login through Google will not do so. googleapps.error.authConfigShould=The {0}installation instructions{1} advise to add the following line to the file xwiki.cfg +googleapps.retrieve.nodocument=No document has been given for retrieval. +googleapps.login.oauth.notauthenticated=You will be redirected to Google for Authentication. ################################################## ## until 2.4-rc-1 diff --git a/ui/src/main/resources/GoogleApps/WebHome.xml b/ui/src/main/resources/GoogleApps/WebHome.xml index 3f24805..f21a398 100644 --- a/ui/src/main/resources/GoogleApps/WebHome.xml +++ b/ui/src/main/resources/GoogleApps/WebHome.xml @@ -46,21 +46,21 @@ #if (!$services.licensing.licensor.hasLicensureForEntity($mainReference)) {{error}}#getMissingLicenseMessage('googleapps.extension.name'){{/error}} #else -$services.localization.render('googleapps.webHome.1') -$services.localization.render('googleapps.webHome.2') -## You can test the integration in test-document-list -$services.localization.render('googleapps.webHome.3', ["[[",">>TestDocumentList]]"]) + $services.localization.render('googleapps.webHome.1') + $services.localization.render('googleapps.webHome.2') + ## You can test the integration in test-document-list + $services.localization.render('googleapps.webHome.3', ["[[",">>TestDocumentList]]"]) -#if($hasGlobalAdmin) -#set($url=$xwiki.getURL('XWiki.XWikiPreferences', 'admin', 'editor=globaladmin&section=googleapps')) -## you can modify the config here -$services.localization.render('googleapps.webHome.4', ["[[",">>path:$url]]"]) -## you can try it in driveMacroTest -$services.localization.render('googleapps.webHome.5', ["[[",">>DriveMacroTest]]"]) -#end + #if($hasGlobalAdmin) + #set($url=$xwiki.getURL('XWiki.XWikiPreferences', 'admin', 'editor=globaladmin&section=googleapps')) + ## you can modify the config here + $services.localization.render('googleapps.webHome.4', ["[[",">>path:$url]]"]) + ## you can try it in driveMacroTest + $services.localization.render('googleapps.webHome.5', ["[[",">>DriveMacroTest]]"]) + #end #end -[[GoogleApps Application version ${services.googleApps.buildVersion} built on ${services.googleApps.buildTime}>>https://store.xwiki.com/xwiki/bin/view/Extension/GoogleAppsIntegration]]. +[[=> More about the GoogleApps Application>>https://store.xwiki.com/xwiki/bin/view/Extension/GoogleAppsIntegration]]. {{/velocity}} </content> </xwikidoc> From c823813b37e425e9bccba1bc8cc85150b5333610 Mon Sep 17 00:00:00 2001 From: Paul Libbrecht <paul@hoplahup.net> Date: Tue, 16 Jun 2020 22:59:51 +0200 Subject: [PATCH 14/17] Comments' fixes: mostly cleaned up code a bit (see https://github.com/xwikisas/application-googleapps/pull/39). --- api/pom.xml | 2 +- .../com/xwiki/googleapps/DriveDocMetadata.java | 2 +- .../com/xwiki/googleapps/GoogleAppsManager.java | 7 ------- .../internal/GoogleAppsManagerImpl.java | 10 ---------- .../resources/GoogleApps/GoogleAppsConfigClass.js | 4 ++-- .../resources/GoogleApps/GoogleAppsConfigSheet.vm | 15 --------------- 6 files changed, 4 insertions(+), 36 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 3855914..384182d 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -55,7 +55,7 @@ <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> - <scope>compile</scope> + <scope>provided</scope> </dependency> </dependencies> </project> diff --git a/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java b/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java index d32f437..f4823fe 100644 --- a/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java +++ b/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java @@ -114,7 +114,7 @@ public List<ExportAlternative> getExportLinksAlternatives() } /** - * Insert one of the possible export alternative's info. + * Inserts one of the information about one of the export-alternatives. * * @param extension understood the file type. * @param newFileName the filename when this file is stored on a desktop with this type diff --git a/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java b/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java index c7ee7cc..953b666 100644 --- a/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java +++ b/api/src/main/java/com/xwiki/googleapps/GoogleAppsManager.java @@ -52,13 +52,6 @@ public interface GoogleAppsManager @Unstable boolean isDriveEnabled(); - /** - * @return if the app is configured to use the Google Drive integration (default: yes). - * @since 3.0 - */ - @Unstable - int getConfigCookiesTTL(); - /** * Inspects the stored information to see if an authorization or a redirect needs to be pronounced. * diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java index 9eea849..ed6bd05 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java @@ -248,16 +248,6 @@ public boolean authWithCookies() return gaXWikiObjects.doesAuthWithCookies(); } - /** - * @return how long (in seconds) the cookies should be valid - * @since 3.0 - */ - @Unstable - public int getConfigCookiesTTL() - { - return gaXWikiObjects.getConfigCookiesTTL(); - } - /** * @return if the app is configured to use the Google Drive integration (default: yes). * @since 3.0 diff --git a/ui/src/main/resources/GoogleApps/GoogleAppsConfigClass.js b/ui/src/main/resources/GoogleApps/GoogleAppsConfigClass.js index 87c1e06..5bd1ea7 100644 --- a/ui/src/main/resources/GoogleApps/GoogleAppsConfigClass.js +++ b/ui/src/main/resources/GoogleApps/GoogleAppsConfigClass.js @@ -13,8 +13,8 @@ require(['jquery'], function (jQuery) { // reactivating - function reactivate(elts) { - jQuery(elts).each(function () { + function reactivate(elements) { + jQuery(elements).each(function () { if (typeof (this.wasDisabled)) { jQuery(jQuery(this).closest('dl')).find('label').css('color','black'); jQuery(this).prop('disabled', this.wasDisabled); diff --git a/ui/src/main/resources/GoogleApps/GoogleAppsConfigSheet.vm b/ui/src/main/resources/GoogleApps/GoogleAppsConfigSheet.vm index 6c54115..d055a98 100644 --- a/ui/src/main/resources/GoogleApps/GoogleAppsConfigSheet.vm +++ b/ui/src/main/resources/GoogleApps/GoogleAppsConfigSheet.vm @@ -50,22 +50,15 @@ $services.localization.render('googleapps.config.explanation') #tp ('communicate.hint2', ['[[', '>>https://store.xwiki.com/xwiki/bin/view/Extension/GoogleAppsIntegration#installation]]'])</span> </dt> <dd></dd> - </dl> - - <dl> <dt><label for="${propNamePrefix}_clientid">#t ('clientid')</label> <span class="xHint">#t ("clientid.hint")</span></dt> <dd> #displayInput ('clientid')</dd> - </dl> - <dl> <dt><label for="${propNamePrefix}_secret">#t ('secret')</label> <span class="xHint">#t("secret.hint")</span></dt> <dd> #displayInput ("secret")</dd> - </dl> ## checkboxes for scope, needs JS - <dl> <dt><label>#t ('scope')</label> <span class="xHint">#t ('scope.hint')</span></dt> <dd> @@ -79,9 +72,7 @@ $services.localization.render('googleapps.config.explanation') <input type="checkbox" name="scope_avatar">#t ('scope_avatar')</label> <input type="hidden" name="GoogleApps.GoogleAppsConfigClass_0_scope" value="${obj.getValue('scope')}"/> </dd> - </dl> - <dl> <dt><label for="${propNamePrefix}_appname">#t ('appname')</label> <span class="xHint">#t ('appname.hint')</span></dt> <dd> #displayInput ('appname')</dd> @@ -96,9 +87,7 @@ $services.localization.render('googleapps.config.explanation') <span class="xHint">#t("domain.hint")</span></dt> <dd> #displayInput ("domain")<br> <span id="googleapps-domain-livehint" class="xHint"> </span></dd> - </dl> - <dl> <dt>#displayInput ('useCookies') <label for="${propNamePrefix}_useCookies">#t ('useCookies')</label> <span class="xHint">#t ('useCookies.hint')</span></dt> <dd> @@ -107,14 +96,10 @@ $services.localization.render('googleapps.config.explanation') <label for="${propNamePrefix}_skipLoginPage">#t ('skipLoginPage')</label> <span class="xHint">#t ('skipLoginPage.hint')</span></dt> <dd></dd> - </dl> - <dl> <dt>#displayInput ('authWithCookies') <label for="${propNamePrefix}_authWithCookies">#t ('authWithCookies')</label> <span class="xHint">#t ('authWithCookies.hint')</span></dt> <dd></dd> - </dl> - <dl> <dt><label for="${propNamePrefix}_cookiesTTL">#t("cookiesTTL")</label> <span class="xHint">#t ('cookiesTTL.hint')</span></dt> <dd>#displayInput ('cookiesTTL')</dd> From 643fb3cedbde093272030e9fdef3a092afb365b3 Mon Sep 17 00:00:00 2001 From: Paul Libbrecht <paul@hoplahup.net> Date: Thu, 18 Jun 2020 17:08:20 +0200 Subject: [PATCH 15/17] Comments' fixes: do not fix version in the POM and do not make half beans. --- api/pom.xml | 1 - .../xwiki/googleapps/DriveDocMetadata.java | 69 +++++++++++++++---- .../CookieAuthenticationPersistence.java | 5 -- .../internal/GoogleAppsConstants.java | 4 +- .../internal/GoogleAppsEventListener.java | 2 - .../internal/GoogleAppsIdentity.java | 2 - .../internal/GoogleAppsManagerImpl.java | 21 +----- .../internal/GoogleAppsXWikiObjects.java | 38 +++++----- .../internal/GoogleDriveAccess.java | 41 +++++------ 9 files changed, 101 insertions(+), 82 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 384182d..9ff0520 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -54,7 +54,6 @@ <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> - <version>3.0.1</version> <scope>provided</scope> </dependency> </dependencies> diff --git a/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java b/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java index f4823fe..2b219dd 100644 --- a/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java +++ b/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java @@ -36,42 +36,42 @@ public class DriveDocMetadata /** * Google's internal id to find the document again. */ - public String id; + private String id; /** * URL to direct the user to for editing. */ - public String editLink; + private String editLink; /** * URL to pull from in order to fetch the document. */ - public String exportLink; + private String exportLink; /** * URL to use to show an embedded view. */ - public String embedLink; + private String embedLink; /** * A stringified version number. */ - public String version; + private String version; /** * The name of the file in case it is an uploaded file. */ - public String fileName; + private String fileName; /** * The email-address of the user with which this document's connection was created. */ - public String user; + private String user; /** * A list of export possibilities. */ - public List<ExportAlternative> exportLinksAlternatives = new LinkedList<>(); + private List<ExportAlternative> exportLinksAlternatives = new LinkedList<>(); /** * @return the internal Google Id of the document. @@ -81,6 +81,11 @@ public String getId() return id; } + public void setId(String id) + { + this.id = id; + } + /** * @return the version number */ @@ -89,6 +94,11 @@ public String getVersion() return version; } + public void setVersion(String version) + { + this.version = version; + } + /** * @return the URL to direct the user to for editing. */ @@ -97,6 +107,11 @@ public String getEditLink() return editLink; } + public void setEditLink(String link) + { + this.editLink = link; + } + /** * @return the URL to pull from in order to fetch the document. */ @@ -105,6 +120,11 @@ public String getExportLink() return exportLink; } + public void setExportLink(String link) + { + this.exportLink = link; + } + /** * @return a list of export alternatives. */ @@ -116,7 +136,7 @@ public List<ExportAlternative> getExportLinksAlternatives() /** * Inserts one of the information about one of the export-alternatives. * - * @param extension understood the file type. + * @param extension the filename extension (understood as a name of the file-type) * @param newFileName the filename when this file is stored on a desktop with this type * @param exportUrl the url to pull from. */ @@ -140,6 +160,11 @@ public String getFileName() return fileName; } + public void setFileName(String fileName) + { + this.fileName = fileName; + } + /** * @return the same as {#getFileName}. */ @@ -156,6 +181,26 @@ public String toString() return "id " + id + " edit: " + editLink + " export " + exportLink; } + public String getEmbedLink() + { + return embedLink; + } + + public void setEmbedLink(String link) + { + this.embedLink = link; + } + + public String getUser() + { + return user; + } + + public void setUser(String emailAddress) + { + this.user = emailAddress; + } + /** * A class to denote export possibilities of a drive file. */ @@ -164,17 +209,17 @@ public static class ExportAlternative /** * a short nickname of the file type, typically the file-ending. */ - public String extension; + private String extension; /** * the revised filename if exported to this extension. */ - public String newFileName; + private String newFileName; /** * the URL to pull from. */ - public String exportUrl; + private String exportUrl; /** * @return a short nickname of the file type, typically the file-ending. diff --git a/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistence.java b/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistence.java index b2d74fd..abd29ec 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistence.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistence.java @@ -33,7 +33,6 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.xwiki.configuration.ConfigurationSource; -import org.xwiki.stability.Unstable; import com.xpn.xwiki.XWikiContext; import com.xwiki.googleapps.GoogleAppsException; @@ -94,7 +93,6 @@ public class CookieAuthenticationPersistence /** * Builds a configured object. */ - CookieAuthenticationPersistence(ConfigurationSource xwikiCfg, GoogleAppsXWikiObjects gaXwikiObjects, Provider<XWikiContext> contextProvider, Logger logger) { @@ -130,7 +128,6 @@ public class CookieAuthenticationPersistence * * @since 3.0 */ - @Unstable void clear() { this.setUserId("XWikiGuest"); @@ -142,7 +139,6 @@ void clear() * @return the login name found, or null. * @since 3.0 */ - @Unstable String getUserId() { logger.info("retrieve cookie " + cookiePrefix + AUTHENTICATION_COOKIE); @@ -159,7 +155,6 @@ String getUserId() * @param userUid the user-name (without xwiki. prefix) * @since 3.0 */ - @Unstable void setUserId(String userUid) { Cookie cookie = new Cookie(cookiePrefix + AUTHENTICATION_COOKIE, encryptText(userUid)); diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsConstants.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsConstants.java index 4ba0a37..a175fb3 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsConstants.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsConstants.java @@ -19,7 +19,9 @@ */ package com.xwiki.googleapps.internal; -/** A set of string constants used across the classes of the app. +/** + * A set of string constants used across the classes of the app. + * * @version $Id$ * @since 3.0 */ diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java index e000f07..0f51e7c 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java @@ -27,7 +27,6 @@ import org.xwiki.model.reference.DocumentReference; import org.xwiki.observation.EventListener; import org.xwiki.observation.event.Event; -import org.xwiki.stability.Unstable; import com.xpn.xwiki.doc.XWikiDocument; @@ -37,7 +36,6 @@ * @version $Id$ * @since 3.0 */ -@Unstable class GoogleAppsEventListener implements EventListener { private final GoogleAppsXWikiObjects gaXWikiObjects; diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsIdentity.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsIdentity.java index 8eff5c0..ea2fe2d 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsIdentity.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsIdentity.java @@ -29,7 +29,6 @@ import org.slf4j.Logger; import org.xwiki.model.reference.DocumentReference; -import org.xwiki.stability.Unstable; import com.google.api.client.auth.oauth2.Credential; import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; @@ -404,7 +403,6 @@ private String getAuthorizationURL() * @return found credential * @since 3.0 */ - @Unstable Credential authorize(boolean redirect) { try { diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java index ed6bd05..bd3d026 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java @@ -25,10 +25,8 @@ import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; -import javax.inject.Singleton; import org.slf4j.Logger; -import org.xwiki.component.annotation.Component; import org.xwiki.component.phase.Disposable; import org.xwiki.component.phase.Initializable; import org.xwiki.configuration.ConfigurationSource; @@ -36,7 +34,6 @@ import org.xwiki.model.reference.DocumentReferenceResolver; import org.xwiki.observation.ObservationManager; import org.xwiki.query.QueryManager; -import org.xwiki.stability.Unstable; import com.xpn.xwiki.XWiki; import com.xpn.xwiki.XWikiContext; @@ -58,8 +55,6 @@ * @version $Id$ * @since 3.0 */ -@Component -@Singleton public class GoogleAppsManagerImpl implements GoogleAppsManager, Initializable, Disposable, GoogleAppsConstants { @@ -180,7 +175,6 @@ void tryInittingAuthService(GoogleAppsAuthService authService) } } - /** * Evaluates weather the application is active and licensed by looking at the stored documents. Within a request, * this method should always be the first to be called so that the config-object is read and other properties are @@ -189,7 +183,6 @@ void tryInittingAuthService(GoogleAppsAuthService authService) * @return True if documents were readable, and the is licensed and active; false otherwise. * @since 3.0 */ - @Unstable public boolean isActive() { return isActive(xwikiContextProvider.get()); @@ -204,7 +197,6 @@ public boolean isActive() * @return True if documents were readable, and the is licensed and active; false otherwise. * @since 3.0 */ - @Unstable boolean isActive(XWikiContext context) { startIfNeedBe(); @@ -222,7 +214,6 @@ boolean isActive(XWikiContext context) * @return true if the app is configured to use cookies to store the association to the Google user. * @since 3.0 */ - @Unstable public boolean useCookies() { return gaXWikiObjects.doesUseCookies(); @@ -232,7 +223,6 @@ public boolean useCookies() * @return true if the app is configured to simply use the cookie and thus recognize the user based on cookie * @since 3.0 */ - @Unstable public boolean skipLoginPage() { return gaXWikiObjects.doesSkipLoginPage(); @@ -242,7 +232,6 @@ public boolean skipLoginPage() * @return true if the app is configured to use cookies to store the association to the Google user * @since 3.0 */ - @Unstable public boolean authWithCookies() { return gaXWikiObjects.doesAuthWithCookies(); @@ -252,7 +241,6 @@ public boolean authWithCookies() * @return if the app is configured to use the Google Drive integration (default: yes). * @since 3.0 */ - @Unstable public boolean isDriveEnabled() { return gaXWikiObjects.doesConfigScopeUseDrive(); @@ -295,7 +283,6 @@ XWiki getXWiki() * @return "failed login" if failed, {@NOUSER} (can be attempted to Google-OAuth), or "ok" if successful * @since 3.0 */ - @Unstable public String updateUser() { return gaIdentity.updateUser(); @@ -308,7 +295,6 @@ public String updateUser() * @return if found a credential * @since 3.0 */ - @Unstable public boolean authorize(boolean redirect) { try { @@ -329,7 +315,6 @@ public boolean authorize(boolean redirect) * Tika. * @since 3.0 */ - @Unstable public void retrieveFileFromGoogle(String page, String name, String id, String mediaType) { gaDriveAccess.retrieveFileFromGoogle(page, name, id, mediaType); @@ -343,7 +328,6 @@ public void retrieveFileFromGoogle(String page, String name, String id, String m * @return information about the corresponding Google Drive document * @since 3.0 */ - @Unstable public DriveDocMetadata getSyncDocMetadata(String pageName, String fileName) { return gaXWikiObjects.getGoogleDocumentMetadata(pageName, fileName); @@ -359,7 +343,6 @@ public DriveDocMetadata getSyncDocMetadata(String pageName, String fileName) * @return the created or actualized document * @since 3.0 */ - @Unstable public BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, BaseObject objp, int nb) { try { @@ -381,7 +364,6 @@ public BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, Bas * email-address * @since 3.0 */ - @Unstable public DriveDocMetadata saveAttachmentToGoogle(String page, String name) { return gaDriveAccess.saveAttachmentToGoogle(page, name); @@ -395,7 +377,6 @@ public DriveDocMetadata saveAttachmentToGoogle(String page, String name) * @return The list of objects of Google Drive. * @since 3.0 */ - @Unstable public List<DriveDocMetadata> listDriveDocuments(String query, int nbResults) { startIfNeedBe(); @@ -403,5 +384,5 @@ public List<DriveDocMetadata> listDriveDocuments(String query, int nbResults) } enum LifeCycle - { CONSTRUCTED, INITIALIZED, STARTING, RUNNING, STOPPING, STOPPED } + {CONSTRUCTED, INITIALIZED, STARTING, RUNNING, STOPPING, STOPPED} } diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsXWikiObjects.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsXWikiObjects.java index a4a15b3..8599c24 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsXWikiObjects.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsXWikiObjects.java @@ -267,11 +267,11 @@ void saveFileToXWiki(String page, if (context.getRequest().getParameter(URL) != null) { object.set(EXPORTLINK, context.getRequest().getParameter(URL), context); } - object.set(VERSION, ddm.version, context); - object.set(EDITLINK, ddm.editLink, context); - object.set(EMBEDLINK, ddm.embedLink, context); + object.set(VERSION, ddm.getVersion(), context); + object.set(EDITLINK, ddm.getEditLink(), context); + object.set(EMBEDLINK, ddm.getEmbedLink(), context); if (object.getStringValue(USER) == null || object.getStringValue(USER).length() == 0) { - object.set(USER, ddm.user, context); + object.set(USER, ddm.getUser(), context); } context.getWiki().saveDocument(adoc, UPDATECOMMENT, context); log.info("Document " + name + " has been saved to XWiki"); @@ -290,9 +290,9 @@ DriveDocMetadata getGoogleDocumentMetadata(String pageName, String fileName) return null; } else { DriveDocMetadata gdm = new DriveDocMetadata(); - gdm.id = object.getStringValue(ID); - gdm.editLink = object.getStringValue(EDITLINK); - gdm.exportLink = object.getStringValue(EXPORTLINK); + gdm.setId(object.getStringValue(ID)); + gdm.setEditLink(object.getStringValue(EDITLINK)); + gdm.setExportLink(object.getStringValue(EXPORTLINK)); return gdm; } } catch (Exception e) { @@ -311,14 +311,14 @@ BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, BaseObject obj.setNumber(nb); } obj.setStringValue(ID, docId); - if (ddm.embedLink != null) { - obj.setStringValue(EMBEDLINK, ddm.embedLink); + if (ddm.getEmbedLink() != null) { + obj.setStringValue(EMBEDLINK, ddm.getEmbedLink()); } - obj.setStringValue(EDITLINK, ddm.editLink); - obj.setStringValue(VERSION, ddm.version); - obj.setStringValue(FILENAME, ddm.fileName); - obj.setStringValue(USER, ddm.user); + obj.setStringValue(EDITLINK, ddm.getEditLink()); + obj.setStringValue(VERSION, ddm.getVersion()); + obj.setStringValue(FILENAME, ddm.getFileName()); + obj.setStringValue(USER, ddm.getUser()); context.getWiki().saveDocument(doc, "Inserting Google Document", context); return obj; } catch (Exception e) { @@ -346,13 +346,13 @@ void insertSyncDocObject(String page, String name, DriveDocMetadata ddm) XWikiDocument adoc = context.getWiki().getDocument(documentResolver.resolve(page), context); BaseObject object = adoc .newXObject(getSyncDocClassReference(), context); - object.set(ID, ddm.id, context); + object.set(ID, ddm.getId(), context); object.set(FILENAME, name, context); - object.set(EXPORTLINK, ddm.exportLink, context); - object.set(VERSION, ddm.version, context); - object.set(EDITLINK, ddm.editLink, context); - object.set(EMBEDLINK, ddm.embedLink, context); - object.set(USER, ddm.user, context); + object.set(EXPORTLINK, ddm.getExportLink(), context); + object.set(VERSION, ddm.getVersion(), context); + object.set(EDITLINK, ddm.getEditLink(), context); + object.set(EMBEDLINK, ddm.getEmbedLink(), context); + object.set(USER, ddm.getUser(), context); context.getWiki().saveDocument(adoc, UPDATECOMMENT, context); } catch (Exception e) { diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleDriveAccess.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleDriveAccess.java index 18f2da9..6f50f1d 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleDriveAccess.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleDriveAccess.java @@ -40,7 +40,7 @@ import com.xwiki.googleapps.DriveDocMetadata; import com.xwiki.googleapps.GoogleAppsException; -class GoogleDriveAccess implements GoogleAppsConstants +public class GoogleDriveAccess implements GoogleAppsConstants { // ----- communication tools @@ -152,15 +152,16 @@ DriveDocMetadata getEmbedData(String docId) try { File docData = getDriveService().files().get(docId).execute(); DriveDocMetadata ddm = new DriveDocMetadata(); - ddm.id = docId; - ddm.embedLink = docData.getEmbedLink(); - if (ddm.embedLink == null) { - ddm.embedLink = docData.getAlternateLink(); + ddm.setId(docId); + ddm.setEmbedLink(docData.getEmbedLink()); + if (ddm.getEmbedLink() == null) { + ddm.setEmbedLink(docData.getAlternateLink()); } - ddm.editLink = docData.getAlternateLink(); - ddm.fileName = docData.getOriginalFilename() != null ? docData.getOriginalFilename() : docData.getTitle(); - ddm.user = getDriveService().about().get().execute().getUser().getEmailAddress(); - ddm.version = docData.getVersion().toString(); + ddm.setEditLink(docData.getAlternateLink()); + ddm.setFileName( + docData.getOriginalFilename() != null ? docData.getOriginalFilename() : docData.getTitle()); + ddm.setUser(getDriveService().about().get().execute().getUser().getEmailAddress()); + ddm.setVersion(docData.getVersion().toString()); return ddm; } catch (IOException e) { throw new GoogleAppsException(e); @@ -209,27 +210,27 @@ public DriveDocMetadata saveAttachmentToGoogle(String page, String name) private DriveDocMetadata createDriveDocMetadata(File googleFile, String userName) { DriveDocMetadata ddm = new DriveDocMetadata(); - ddm.embedLink = googleFile.getEmbedLink() != null - ? googleFile.getEmbedLink() : googleFile.getAlternateLink(); - ddm.editLink = googleFile.getAlternateLink(); - ddm.version = Long.toString(googleFile.getVersion()); - ddm.fileName = googleFile.getOriginalFilename(); - if (ddm.fileName == null) { - ddm.fileName = googleFile.getTitle(); + ddm.setEmbedLink(googleFile.getEmbedLink() != null + ? googleFile.getEmbedLink() : googleFile.getAlternateLink()); + ddm.setEditLink(googleFile.getAlternateLink()); + ddm.setVersion(Long.toString(googleFile.getVersion())); + ddm.setFileName(googleFile.getOriginalFilename()); + if (ddm.getFileName() == null) { + ddm.setFileName(googleFile.getTitle()); } - ddm.id = googleFile.getId(); - ddm.user = userName; + ddm.setId(googleFile.getId()); + ddm.setUser(userName); if (googleFile.getExportLinks() != null) { for (String elink : googleFile.getExportLinks().values()) { int index = elink.indexOf(EXPORTFORMATEQ) + 13; String extension = elink.substring(index); - String newFileName = ddm.fileName + String newFileName = ddm.getFileName() .replaceAll("\\.(doc|docx|odt|xls|xlsx|ods|pptx|svg|png|jpeg|pdf|)$", ""); newFileName += '.' + extension; ddm.addExportAlternative(extension, newFileName, elink); } } - ddm.exportLink = googleFile.getDownloadUrl(); + ddm.setExportLink(googleFile.getDownloadUrl()); return ddm; } } From d8109496a41c3042e419161da0acfd15b56fe3c6 Mon Sep 17 00:00:00 2001 From: Paul Libbrecht <paul@hoplahup.net> Date: Fri, 26 Jun 2020 09:40:35 +0200 Subject: [PATCH 16/17] Removing complex constructors in favor of internal components. While this forces just about any reference to internal objects to be referenced to by a provider, it allows a more focussed constructions. --- .../xwiki/googleapps/DriveDocMetadata.java | 36 +++++++ .../CookieAuthenticationPersistence.java | 43 +++++--- .../internal/GoogleAppsAuthService.java | 39 ++++--- .../internal/GoogleAppsEventListener.java | 16 +-- .../internal/GoogleAppsIdentity.java | 100 ++++++++++-------- .../internal/GoogleAppsManagerImpl.java | 98 ++++++----------- .../internal/GoogleAppsXWikiObjects.java | 70 ++++++++---- .../internal/GoogleDriveAccess.java | 48 ++++++--- .../main/resources/META-INF/components.txt | 6 ++ 9 files changed, 272 insertions(+), 184 deletions(-) diff --git a/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java b/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java index 2b219dd..c5c056b 100644 --- a/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java +++ b/api/src/main/java/com/xwiki/googleapps/DriveDocMetadata.java @@ -81,6 +81,10 @@ public String getId() return id; } + /** + * Sets the Google ID of the document having requested this document. + * @param id The id of the document. + * */ public void setId(String id) { this.id = id; @@ -94,6 +98,10 @@ public String getVersion() return version; } + /** + * Set a designation of the version of the document. + * @param version The version (a number, expectedly) + */ public void setVersion(String version) { this.version = version; @@ -107,6 +115,10 @@ public String getEditLink() return editLink; } + /** + * Sets the link where this document can be edited. + * @param link The link where it can be edited. + */ public void setEditLink(String link) { this.editLink = link; @@ -120,6 +132,10 @@ public String getExportLink() return exportLink; } + /** + * Sets the link from which this document can be exported (and thus saved to XWiki). + * @param link The link where it can be exported. + */ public void setExportLink(String link) { this.exportLink = link; @@ -160,6 +176,10 @@ public String getFileName() return fileName; } + /** + * Sets the name of the file when it was uploaded. + * @param fileName The name of the file. + */ public void setFileName(String fileName) { this.fileName = fileName; @@ -181,21 +201,37 @@ public String toString() return "id " + id + " edit: " + editLink + " export " + exportLink; } + /** + * The link of the iframe where it can be embedded. + * @return The link. + */ public String getEmbedLink() { return embedLink; } + /** + * Sets the link with which the document can be embedded. + * @param link The link. + */ public void setEmbedLink(String link) { this.embedLink = link; } + /** + * The Google user that was used to include this document. + * @return The google user email. + */ public String getUser() { return user; } + /** + * The Google user that was used to include this document. + * @param emailAddress The user email. + */ public void setUser(String emailAddress) { this.user = emailAddress; diff --git a/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistence.java b/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistence.java index abd29ec..3c66622 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistence.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/CookieAuthenticationPersistence.java @@ -26,12 +26,17 @@ import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.SecretKeySpec; +import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Provider; +import javax.inject.Singleton; import javax.servlet.http.Cookie; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; +import org.xwiki.component.annotation.Component; +import org.xwiki.component.phase.Initializable; import org.xwiki.configuration.ConfigurationSource; import com.xpn.xwiki.XWikiContext; @@ -47,7 +52,9 @@ * @version $Id$ * @since 3.0 */ -public class CookieAuthenticationPersistence +@Component(roles = CookieAuthenticationPersistence.class) +@Singleton +public class CookieAuthenticationPersistence implements Initializable { private static final String AUTHENTICATION_CONFIG_PREFIX = "xwiki.authentication"; @@ -72,29 +79,35 @@ public class CookieAuthenticationPersistence private static final String UNDERSCORE = "_"; - private final Logger logger; + @Inject + private Logger logger; - private final GoogleAppsXWikiObjects gaXwikiObjects; + @Inject + private Provider<GoogleAppsXWikiObjects> gaXwikiObjects; - private final String cookiePrefix; + private String cookiePrefix; - private final String cookiePath; + private String cookiePath; - private final String[] cookieDomains; + private String[] cookieDomains; - private final Cipher encryptionCipher; + private Cipher encryptionCipher; - private final Cipher decryptionCipher; + private Cipher decryptionCipher; - private final String encryptionKey; + private String encryptionKey; - private final Provider<XWikiContext> contextProvider; + @Inject + private Provider<XWikiContext> contextProvider; + + @Inject + @Named("xwikicfg") + private ConfigurationSource xwikiCfg; /** * Builds a configured object. */ - CookieAuthenticationPersistence(ConfigurationSource xwikiCfg, - GoogleAppsXWikiObjects gaXwikiObjects, Provider<XWikiContext> contextProvider, Logger logger) + public void initialize() { this.cookiePrefix = xwikiCfg.getProperty(COOKIE_PREFIX_PROPERTY, ""); this.cookiePath = xwikiCfg.getProperty(COOKIE_PATH_PROPERTY, "/"); @@ -117,10 +130,6 @@ public class CookieAuthenticationPersistence } catch (Exception e) { throw new GoogleAppsException("Unable to initialize ciphers", e); } - - this.logger = logger; - this.gaXwikiObjects = gaXwikiObjects; - this.contextProvider = contextProvider; } /** @@ -158,7 +167,7 @@ String getUserId() void setUserId(String userUid) { Cookie cookie = new Cookie(cookiePrefix + AUTHENTICATION_COOKIE, encryptText(userUid)); - cookie.setMaxAge(gaXwikiObjects.getConfigCookiesTTL()); + cookie.setMaxAge(gaXwikiObjects.get().getConfigCookiesTTL()); cookie.setPath(cookiePath); String cookieDomain = getCookieDomain(); if (cookieDomain != null) { diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthService.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthService.java index 30a775c..39dc347 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthService.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsAuthService.java @@ -23,10 +23,17 @@ import java.security.Principal; import java.util.regex.Pattern; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; import javax.servlet.http.HttpSession; import org.securityfilter.realm.SimplePrincipal; import org.slf4j.Logger; +import org.xwiki.component.annotation.Component; +import org.xwiki.component.phase.Initializable; +import org.xwiki.configuration.ConfigurationSource; import org.xwiki.container.servlet.filters.SavedRequestManager; import org.xwiki.model.reference.DocumentReference; import org.xwiki.text.StringUtils; @@ -45,26 +52,34 @@ * @version $Id$ * @since 3.0 */ -public class GoogleAppsAuthService extends XWikiAuthServiceImpl +@Component(roles = GoogleAppsAuthService.class) +@Singleton +public class GoogleAppsAuthService extends XWikiAuthServiceImpl implements Initializable { private static final String XWIKISPACE = "XWiki."; - private final Logger log; + @Inject + private Logger log; - private final GoogleAppsXWikiObjects gaXwikiObjects; + @Inject + private GoogleAppsXWikiObjects gaXwikiObjects; - private final CookieAuthenticationPersistence cookiePersistance; + @Inject + private CookieAuthenticationPersistence cookiePersistance; - private final Pattern logoutRequestMatcher; + @Inject + @Named("xwikicfg") + private Provider<ConfigurationSource> xwikiCfg; - GoogleAppsAuthService(GoogleAppsXWikiObjects gaXwikiObjects, - CookieAuthenticationPersistence cookiePersistance, - String logoutPattern, Logger log) + private Pattern logoutRequestMatcher; + + /** + * Reads the configuration. + */ + public void initialize() { - this.log = log; - this.gaXwikiObjects = gaXwikiObjects; - this.logoutRequestMatcher = Pattern.compile(logoutPattern); - this.cookiePersistance = cookiePersistance; + this.logoutRequestMatcher = Pattern.compile( + xwikiCfg.get().getProperty("xwiki.authentication.logoutpage", "")); } /** diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java index 0f51e7c..10e4ee7 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsEventListener.java @@ -22,8 +22,12 @@ import java.util.Arrays; import java.util.List; +import javax.inject.Inject; +import javax.inject.Singleton; + import org.xwiki.bridge.event.ApplicationReadyEvent; import org.xwiki.bridge.event.DocumentUpdatedEvent; +import org.xwiki.component.annotation.Component; import org.xwiki.model.reference.DocumentReference; import org.xwiki.observation.EventListener; import org.xwiki.observation.event.Event; @@ -36,14 +40,12 @@ * @version $Id$ * @since 3.0 */ -class GoogleAppsEventListener implements EventListener +@Component(roles = GoogleAppsEventListener.class) +@Singleton +public class GoogleAppsEventListener implements EventListener { - private final GoogleAppsXWikiObjects gaXWikiObjects; - - GoogleAppsEventListener(GoogleAppsXWikiObjects gaXWikiObjects) - { - this.gaXWikiObjects = gaXWikiObjects; - } + @Inject + private GoogleAppsXWikiObjects gaXWikiObjects; /** * The name of the event listener. diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsIdentity.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsIdentity.java index ea2fe2d..a7e77a2 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsIdentity.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsIdentity.java @@ -24,10 +24,13 @@ import java.util.List; import java.util.Map; +import javax.inject.Inject; import javax.inject.Provider; +import javax.inject.Singleton; import javax.servlet.http.HttpSession; import org.slf4j.Logger; +import org.xwiki.component.annotation.Component; import org.xwiki.model.reference.DocumentReference; import com.google.api.client.auth.oauth2.Credential; @@ -47,49 +50,60 @@ import com.xpn.xwiki.web.XWikiRequest; import com.xwiki.googleapps.GoogleAppsException; -class GoogleAppsIdentity implements GoogleAppsConstants +/** + * Set of objects to recognize and read the identity of the user. + * @since 3.0 + * @version $Id$ + */ +@Component(roles = GoogleAppsIdentity.class) +@Singleton +public class GoogleAppsIdentity implements GoogleAppsConstants { /** * A map of hash to full redirects. */ private final Map<String, String> storedStates = new HashMap<>(); - private final Logger log; + @Inject + private Logger log; + + @Inject + private Provider<XWikiContext> contextProvider; - private final Provider<XWikiContext> contextProvider; + @Inject + private Provider<GoogleAppsXWikiObjects> gaXwikiObjects; - private final GoogleAppsXWikiObjects gaXwikiObjects; + @Inject + private Provider<CookieAuthenticationPersistence> cookiePersistence; - private final CookieAuthenticationPersistence cookiePersistence; - private final JacksonFactory jacksonFactory; + private JacksonFactory jacksonFactory; - private final NetHttpTransport httpTransport; + private NetHttpTransport httpTransport; private FileDataStoreFactory dsFactory; - GoogleAppsIdentity(Provider<XWikiContext> contextProvider, - GoogleAppsXWikiObjects gaXwikiObjects, - CookieAuthenticationPersistence cookiePersistence, - Logger log) - { - try { - this.contextProvider = contextProvider; - this.gaXwikiObjects = gaXwikiObjects; - this.jacksonFactory = JacksonFactory.getDefaultInstance(); - this.httpTransport = GoogleNetHttpTransport.newTrustedTransport(); - this.cookiePersistence = cookiePersistence; - this.log = log; - } catch (Exception e) { - e.printStackTrace(); - throw new GoogleAppsException("Trouble at constructing GoogleAppsIdentity", e); + + + private void initIfNeedBe() { + if (this.jacksonFactory==null) { + try { + this.jacksonFactory = JacksonFactory.getDefaultInstance(); + this.httpTransport = GoogleNetHttpTransport.newTrustedTransport(); + this.dsFactory = new FileDataStoreFactory(gaXwikiObjects.get().getPermanentDir()); + } catch (Exception e) { + e.printStackTrace(); + throw new GoogleAppsException("Trouble at constructing GoogleAppsIdentity", e); + } } } + String updateUser() { + initIfNeedBe(); try { - if (!gaXwikiObjects.isActive()) { + if (!gaXwikiObjects.get().isActive()) { return FAILEDLOGIN; } log.debug("Updating user..."); @@ -99,7 +113,7 @@ String updateUser() if (credential != null) { PeopleService pservice = new PeopleService.Builder(httpTransport, jacksonFactory, credential) - .setApplicationName(gaXwikiObjects.getConfigAppName()) + .setApplicationName(gaXwikiObjects.get().getConfigAppName()) .build(); gUser = pservice.people().get("people/me").setPersonFields("emailAddresses,names,photos").execute(); // GOOGLEAPPS: User: [displayName:..., emails:[[type:account, value:...]], etag:"...", @@ -134,7 +148,7 @@ String updateUser() String googleUserId = (String) gUser.get("resourceName"); String photoUrl = extractPhotoUrl(gUser); - xwikiUser = gaXwikiObjects.updateXWikiUser(googleUserId, emails, email, + xwikiUser = gaXwikiObjects.get().updateXWikiUser(googleUserId, emails, email, firstName, lastName, photoUrl); // we need to restore the credentials as the user will now be logged-in @@ -153,7 +167,7 @@ String updateUser() private String checkDomain(List<String> emails) { String email = null; - String domain = gaXwikiObjects.getConfigDomain(); + String domain = gaXwikiObjects.get().getConfigDomain(); if (domain != null && domain.length() > 0) { domain = domain.trim(); for (String address : emails) { @@ -174,7 +188,7 @@ private String checkDomain(List<String> emails) private String extractPhotoUrl(Person gUser) { - if (gaXwikiObjects.getConfigScopeUseAvatar() + if (gaXwikiObjects.get().getConfigScopeUseAvatar() && gUser.getPhotos() != null && gUser.getPhotos().size() > 0 && gUser.getPhotos().get(0).getUrl() != null) @@ -203,6 +217,7 @@ private String getOAuthUrl() private String getCurrentXWikiUserName() { + initIfNeedBe(); DocumentReference userDoc = contextProvider.get().getUserReference(); String uName = userDoc == null ? XWIKIGUEST : userDoc.getName(); if (XWIKIGUEST.equals(uName)) { @@ -219,28 +234,24 @@ private String getCurrentXWikiUserName() */ private GoogleAuthorizationCodeFlow getFlow() { + initIfNeedBe(); try { - if (dsFactory == null) { - dsFactory = - new FileDataStoreFactory(gaXwikiObjects.getPermanentDir()); - } - // create scopes from config List<String> gScopes = new ArrayList<>(); gScopes.add(PeopleServiceScopes.USERINFO_EMAIL); gScopes.add(PeopleServiceScopes.USERINFO_PROFILE); - if (gaXwikiObjects.doesConfigScopeUseDrive()) { + if (gaXwikiObjects.get().doesConfigScopeUseDrive()) { gScopes.add(DriveScopes.DRIVE); } // create flow return new GoogleAuthorizationCodeFlow.Builder( httpTransport, - jacksonFactory, gaXwikiObjects.getConfigClientId(), - gaXwikiObjects.getConfigClientSecret(), gScopes) + jacksonFactory, gaXwikiObjects.get().getConfigClientId(), + gaXwikiObjects.get().getConfigClientSecret(), gScopes) .setDataStoreFactory(dsFactory) .setAccessType("online").setApprovalPrompt(AUTOAPPROVAL) - .setClientId(gaXwikiObjects.getConfigClientId()) + .setClientId(gaXwikiObjects.get().getConfigClientId()) .build(); } catch (Exception e) { e.printStackTrace(); @@ -256,6 +267,7 @@ private GoogleAuthorizationCodeFlow getFlow() */ private Credential exchangeCode(String authorizationCode) { + initIfNeedBe(); try { GoogleAuthorizationCodeFlow flow = getFlow(); GoogleTokenResponse tokenResponse = flow @@ -271,6 +283,7 @@ private Credential exchangeCode(String authorizationCode) private Map<String, Credential> getCredentialStore() { + initIfNeedBe(); final String key = "GoogleAppsCredentialStore"; HttpSession session = contextProvider.get().getRequest().getSession(true); Map<String, Credential> store = (Map<String, Credential>) (session.getAttribute(key)); @@ -285,8 +298,8 @@ private void storeCredentials(String userId, Credential credentials) { try { if (userId.contains(XWIKIGUEST)) { - if (gaXwikiObjects.doesUseCookies()) { - cookiePersistence.setUserId(userId); + if (gaXwikiObjects.get().doesUseCookies()) { + cookiePersistence.get().setUserId(userId); } } log.debug("Storing credentials for user " + userId + " (" + credentials + ")."); @@ -376,13 +389,13 @@ private String getAuthorizationURL() GoogleAuthorizationCodeRequestUrl urlBuilder = getFlow() .newAuthorizationUrl() .setRedirectUri(getOAuthUrl()) - .setState(state).setClientId(gaXwikiObjects.getConfigClientId()) + .setState(state).setClientId(gaXwikiObjects.get().getConfigClientId()) .setAccessType("offline").setApprovalPrompt(AUTOAPPROVAL); // Add user email to filter account if the user is logged with multiple account - if (gaXwikiObjects.doesUseCookies()) { - String userId = cookiePersistence.getUserId(); + if (gaXwikiObjects.get().doesUseCookies()) { + String userId = cookiePersistence.get().getUserId(); if (userId != null) { - String userEmail = gaXwikiObjects.getUserEmail(userId); + String userEmail = gaXwikiObjects.get().getUserEmail(userId); if (userEmail != null) { urlBuilder = urlBuilder.set("login_hint", userEmail); } @@ -403,8 +416,9 @@ private String getAuthorizationURL() * @return found credential * @since 3.0 */ - Credential authorize(boolean redirect) + public Credential authorize(boolean redirect) { + initIfNeedBe(); try { log.debug("In authorize"); // TODO: useless? GoogleAuthorizationCodeFlow flow = diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java index bd3d026..cb49142 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsManagerImpl.java @@ -19,21 +19,16 @@ */ package com.xwiki.googleapps.internal; -import java.io.File; import java.util.List; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Provider; +import javax.inject.Singleton; import org.slf4j.Logger; +import org.xwiki.component.annotation.Component; import org.xwiki.component.phase.Disposable; import org.xwiki.component.phase.Initializable; -import org.xwiki.configuration.ConfigurationSource; -import org.xwiki.environment.Environment; -import org.xwiki.model.reference.DocumentReferenceResolver; -import org.xwiki.observation.ObservationManager; -import org.xwiki.query.QueryManager; import com.xpn.xwiki.XWiki; import com.xpn.xwiki.XWikiContext; @@ -55,6 +50,8 @@ * @version $Id$ * @since 3.0 */ +@Component +@Singleton public class GoogleAppsManagerImpl implements GoogleAppsManager, Initializable, Disposable, GoogleAppsConstants { @@ -62,39 +59,25 @@ public class GoogleAppsManagerImpl private LifeCycle lifeCycleState = LifeCycle.CONSTRUCTED; - private GoogleAppsIdentity gaIdentity; - - private GoogleAppsXWikiObjects gaXWikiObjects; - - private GoogleDriveAccess gaDriveAccess; - - // ------ services from the environment - @Inject - private Provider<XWikiContext> xwikiContextProvider; - + // own components @Inject - @Named("current") - private DocumentReferenceResolver<String> documentResolver; + private Provider<GoogleAppsIdentity> gaIdentity; @Inject - @Named("user") - private DocumentReferenceResolver<String> userResolver; + private Provider<GoogleAppsXWikiObjects> gaXWikiObjects; @Inject - private Logger log; - - @Inject - private ObservationManager observationManager; + private Provider<GoogleDriveAccess> gaDriveAccess; @Inject - private QueryManager queryManager; + private Provider<GoogleAppsAuthService> authService; + // ------ services from the environment @Inject - private Environment environment; + private Provider<XWikiContext> xwikiContextProvider; @Inject - @Named("xwikicfg") - private Provider<ConfigurationSource> xwikiCfgProvider; + private Logger log; @Override public void initialize() @@ -123,26 +106,9 @@ private void startIfNeedBe() boolean failed = false; try { - File permanentDir = new File(environment.getPermanentDirectory(), SPACENAME); - gaXWikiObjects = new GoogleAppsXWikiObjects(log, xwikiContextProvider, queryManager, - documentResolver, userResolver, permanentDir); - gaXWikiObjects.startIfNeedBe(observationManager); - - // ----- shared communication tools - ConfigurationSource xwikiCfg = xwikiCfgProvider.get(); - CookieAuthenticationPersistence cookiePersistence = new CookieAuthenticationPersistence( - xwikiCfg, gaXWikiObjects, xwikiContextProvider, log); - - String logoutPattern = xwikiCfg.getProperty("xwiki.authentication.logoutpage", ""); - GoogleAppsAuthService authService = new GoogleAppsAuthService( - gaXWikiObjects, cookiePersistence, logoutPattern, log); - - gaIdentity = new GoogleAppsIdentity(xwikiContextProvider, - gaXWikiObjects, cookiePersistence, log); - tryInittingAuthService(authService); - - gaDriveAccess = - new GoogleDriveAccess(log, gaXWikiObjects, gaIdentity); + gaXWikiObjects.get().startIfNeedBe(); + + tryInittingAuthService(); } catch (Exception e) { e.printStackTrace(); failed = true; @@ -156,7 +122,7 @@ private void startIfNeedBe() } } - void tryInittingAuthService(GoogleAppsAuthService authService) + void tryInittingAuthService() { XWiki xwiki = getXWiki(); if (xwiki != null) { @@ -164,7 +130,7 @@ void tryInittingAuthService(GoogleAppsAuthService authService) // We do not verify with the context if the plugin is active and if the license is active // this will be done by the GoogleAppsAuthService and UI pages later on, when it is called within a request try { - xwiki.setAuthService(authService); + xwiki.setAuthService(authService.get()); log.info("Succeeded initting authService,"); } catch (Exception e) { log.info("Failed initting authService", e); @@ -204,8 +170,8 @@ boolean isActive(XWikiContext context) if (gaIdentity == null) { initialize(); } - if (gaXWikiObjects.isActive() != null) { - return gaXWikiObjects.isActive(); + if (gaXWikiObjects.get().isActive() != null) { + return gaXWikiObjects.get().isActive(); } return false; } @@ -216,7 +182,7 @@ boolean isActive(XWikiContext context) */ public boolean useCookies() { - return gaXWikiObjects.doesUseCookies(); + return gaXWikiObjects.get().doesUseCookies(); } /** @@ -225,7 +191,7 @@ public boolean useCookies() */ public boolean skipLoginPage() { - return gaXWikiObjects.doesSkipLoginPage(); + return gaXWikiObjects.get().doesSkipLoginPage(); } /** @@ -234,7 +200,7 @@ public boolean skipLoginPage() */ public boolean authWithCookies() { - return gaXWikiObjects.doesAuthWithCookies(); + return gaXWikiObjects.get().doesAuthWithCookies(); } /** @@ -243,7 +209,7 @@ public boolean authWithCookies() */ public boolean isDriveEnabled() { - return gaXWikiObjects.doesConfigScopeUseDrive(); + return gaXWikiObjects.get().doesConfigScopeUseDrive(); } /** @@ -285,7 +251,7 @@ XWiki getXWiki() */ public String updateUser() { - return gaIdentity.updateUser(); + return gaIdentity.get().updateUser(); } /** @@ -298,7 +264,7 @@ public String updateUser() public boolean authorize(boolean redirect) { try { - return null != gaIdentity.authorize(redirect); + return null != gaIdentity.get().authorize(redirect); } catch (Exception ex) { log.warn("Trouble at authorizing", ex); return false; @@ -317,7 +283,7 @@ public boolean authorize(boolean redirect) */ public void retrieveFileFromGoogle(String page, String name, String id, String mediaType) { - gaDriveAccess.retrieveFileFromGoogle(page, name, id, mediaType); + gaDriveAccess.get().retrieveFileFromGoogle(page, name, id, mediaType); } /** @@ -330,7 +296,7 @@ public void retrieveFileFromGoogle(String page, String name, String id, String m */ public DriveDocMetadata getSyncDocMetadata(String pageName, String fileName) { - return gaXWikiObjects.getGoogleDocumentMetadata(pageName, fileName); + return gaXWikiObjects.get().getGoogleDocumentMetadata(pageName, fileName); } /** @@ -347,9 +313,9 @@ public BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, Bas { try { - DriveDocMetadata ddm = gaDriveAccess.getEmbedData(docId); + DriveDocMetadata ddm = gaDriveAccess.get().getEmbedData(docId); // use here and at retrieveFromGoogle - return gaXWikiObjects.createOrUpdateEmbedObject(docId, doc, objp, nb, ddm); + return gaXWikiObjects.get().createOrUpdateEmbedObject(docId, doc, objp, nb, ddm); } catch (Exception e) { throw new GoogleAppsException("Can't create or update embedded document.", e); } @@ -366,7 +332,7 @@ public BaseObject createOrUpdateEmbedObject(String docId, XWikiDocument doc, Bas */ public DriveDocMetadata saveAttachmentToGoogle(String page, String name) { - return gaDriveAccess.saveAttachmentToGoogle(page, name); + return gaDriveAccess.get().saveAttachmentToGoogle(page, name); } /** @@ -380,9 +346,9 @@ public DriveDocMetadata saveAttachmentToGoogle(String page, String name) public List<DriveDocMetadata> listDriveDocuments(String query, int nbResults) { startIfNeedBe(); - return gaDriveAccess.listDriveDocuments(query, nbResults); + return gaDriveAccess.get().listDriveDocuments(query, nbResults); } enum LifeCycle - {CONSTRUCTED, INITIALIZED, STARTING, RUNNING, STOPPING, STOPPED} + { CONSTRUCTED, INITIALIZED, STARTING, RUNNING, STOPPING, STOPPED } } diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsXWikiObjects.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsXWikiObjects.java index 8599c24..4217a57 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsXWikiObjects.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleAppsXWikiObjects.java @@ -29,11 +29,17 @@ import java.util.List; import java.util.Map; +import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Provider; +import javax.inject.Singleton; import org.apache.commons.httpclient.util.DateUtil; import org.apache.commons.lang3.tuple.ImmutablePair; import org.slf4j.Logger; +import org.xwiki.component.annotation.Component; +import org.xwiki.component.phase.Initializable; +import org.xwiki.environment.Environment; import org.xwiki.model.reference.DocumentReference; import org.xwiki.model.reference.DocumentReferenceResolver; import org.xwiki.model.reference.ObjectReference; @@ -49,23 +55,50 @@ import com.xwiki.googleapps.DriveDocMetadata; import com.xwiki.googleapps.GoogleAppsException; -class GoogleAppsXWikiObjects implements GoogleAppsConstants +/** + * The objects representing the configuration of the application as well as methods to + * connect to XWiki for the manipulation of users and attachments. + * + * @version $Id$ + * @since 3.0 + */ +@Component(roles = GoogleAppsXWikiObjects.class) +@Singleton +public class GoogleAppsXWikiObjects implements GoogleAppsConstants, Initializable { // environment - private final Logger log; + @Inject + private Logger log; + + @Inject + private Provider<XWikiContext> contextProvider; + + @Inject + private QueryManager queryManager; + + @Inject + @Named("current") + private DocumentReferenceResolver<String> documentResolver; - private final Provider<XWikiContext> contextProvider; + @Inject + @Named("user") + private DocumentReferenceResolver<String> userResolver; - private final QueryManager queryManager; + @Inject + private Provider<GoogleAppsEventListener> eventListener; - private final DocumentReferenceResolver<String> documentResolver; + @Inject + private Environment environment; - private final DocumentReferenceResolver<String> userResolver; + @Inject + private Provider<ObservationManager> observationManager; - private final File permanentDir; + + private File permanentDir; private boolean started; + // configuration properties private Boolean configActiveFlag; private boolean useCookies; @@ -93,22 +126,16 @@ class GoogleAppsXWikiObjects implements GoogleAppsConstants private ObjectReference configObjRef; - private GoogleAppsEventListener eventListener; - private DocumentReference syncdocClassRef; private DocumentReference gauthClassRef; - GoogleAppsXWikiObjects(Logger log, Provider<XWikiContext> contextProvider, - QueryManager queryManager, DocumentReferenceResolver<String> documentResolver, - DocumentReferenceResolver<String> userResolver, File permanentDir) + /** + * Initializes the internal objects. + */ + public void initialize() { - this.log = log; - this.contextProvider = contextProvider; - this.queryManager = queryManager; - this.documentResolver = documentResolver; - this.userResolver = userResolver; - this.permanentDir = permanentDir; + this.permanentDir = new File(environment.getPermanentDirectory(), SPACENAME); } Boolean isActive() @@ -225,14 +252,13 @@ private void readConfigDoc() } } - void startIfNeedBe(ObservationManager observationManager) + void startIfNeedBe() { if (started) { return; } if (eventListener == null) { - eventListener = new GoogleAppsEventListener(this); - observationManager.addListener(eventListener); + observationManager.get().addListener(eventListener.get()); } if (configActiveFlag == null || configClientId == null) { readConfigDoc(); @@ -584,7 +610,7 @@ File getPermanentDir() return permanentDir; } - public String getUserEmail(String userId) + String getUserEmail(String userId) { try { XWikiContext context = contextProvider.get(); diff --git a/api/src/main/java/com/xwiki/googleapps/internal/GoogleDriveAccess.java b/api/src/main/java/com/xwiki/googleapps/internal/GoogleDriveAccess.java index 6f50f1d..a9488a7 100644 --- a/api/src/main/java/com/xwiki/googleapps/internal/GoogleDriveAccess.java +++ b/api/src/main/java/com/xwiki/googleapps/internal/GoogleDriveAccess.java @@ -24,9 +24,15 @@ import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; +import javax.inject.Provider; +import javax.inject.Singleton; + import org.apache.commons.lang3.tuple.Pair; import org.apache.tika.Tika; import org.slf4j.Logger; +import org.xwiki.component.annotation.Component; +import org.xwiki.component.phase.Initializable; import org.xwiki.stability.Unstable; import com.google.api.client.auth.oauth2.Credential; @@ -40,28 +46,36 @@ import com.xwiki.googleapps.DriveDocMetadata; import com.xwiki.googleapps.GoogleAppsException; -public class GoogleDriveAccess implements GoogleAppsConstants +/** + * Tools to access the web-services of the Google Apps. + * + * @version $Id$ + * @since 3.0 + */ +@Component(roles = GoogleDriveAccess.class) +@Singleton +public class GoogleDriveAccess implements GoogleAppsConstants, Initializable { // ----- communication tools + @Inject + private Provider<GoogleAppsIdentity> gaIdentity; - private final GoogleAppsIdentity gaIdentity; - - private final GoogleAppsXWikiObjects gaXWikiObjects; + @Inject + private Provider<GoogleAppsXWikiObjects> gaXWikiObjects; - private final Logger log; + @Inject + private Logger log; - private final JacksonFactory jacksonFactory; + private JacksonFactory jacksonFactory; - private final NetHttpTransport httpTransport; + private NetHttpTransport httpTransport; - GoogleDriveAccess(Logger log, - GoogleAppsXWikiObjects gaXWikiObjects, GoogleAppsIdentity gaIdentity) + /** Initializes the communication objects. + * */ + public void initialize() { try { - this.log = log; - this.gaXWikiObjects = gaXWikiObjects; - this.gaIdentity = gaIdentity; this.jacksonFactory = JacksonFactory.getDefaultInstance(); this.httpTransport = GoogleNetHttpTransport.newTrustedTransport(); } catch (Exception e) { @@ -77,10 +91,10 @@ public class GoogleDriveAccess implements GoogleAppsConstants */ private Drive getDriveService() { - Credential credential = gaIdentity.authorize(false); + Credential credential = gaIdentity.get().authorize(false); return new Drive.Builder( httpTransport, jacksonFactory, credential) - .setApplicationName(gaXWikiObjects.getConfigAppName()) + .setApplicationName(gaXWikiObjects.get().getConfigAppName()) .build(); } @@ -140,7 +154,7 @@ void retrieveFileFromGoogle(String page, String name, String id, String mediaTyp String user = driveService.about().get().execute().getUser().getEmailAddress(); File docData = driveService.files().get(id).execute(); createDriveDocMetadata(docData, user); - gaXWikiObjects.saveFileToXWiki(page, id, name, downloadStream, createDriveDocMetadata(docData, user)); + gaXWikiObjects.get().saveFileToXWiki(page, id, name, downloadStream, createDriveDocMetadata(docData, user)); } catch (Exception e) { log.info(e.getMessage(), e); throw new GoogleAppsException("Trouble at retrieving from Google.", e); @@ -182,7 +196,7 @@ public DriveDocMetadata saveAttachmentToGoogle(String page, String name) { try { log.debug("Starting saving attachment ${name} from page ${page}"); - Pair<InputStream, String> attachPair = gaXWikiObjects.getAttachment(name, page); + Pair<InputStream, String> attachPair = gaXWikiObjects.get().getAttachment(name, page); File file = new File(); file.setTitle(name); @@ -196,7 +210,7 @@ public DriveDocMetadata saveAttachmentToGoogle(String page, String name) if (docData != null) { log.debug("File inserted " + docData); DriveDocMetadata ddm = createDriveDocMetadata(docData, user); - gaXWikiObjects.insertSyncDocObject(page, name, ddm); + gaXWikiObjects.get().insertSyncDocObject(page, name, ddm); return ddm; } else { log.warn("File insert failed"); diff --git a/api/src/main/resources/META-INF/components.txt b/api/src/main/resources/META-INF/components.txt index 974804e..4bdb925 100644 --- a/api/src/main/resources/META-INF/components.txt +++ b/api/src/main/resources/META-INF/components.txt @@ -1,2 +1,8 @@ com.xwiki.googleapps.internal.GoogleAppsManagerImpl com.xwiki.googleapps.GoogleAppsScriptService +com.xwiki.googleapps.internal.CookieAuthenticationPersistence +com.xwiki.googleapps.internal.GoogleAppsAuthService +com.xwiki.googleapps.internal.GoogleAppsEventListener +com.xwiki.googleapps.internal.GoogleAppsIdentity +com.xwiki.googleapps.internal.GoogleAppsXWikiObjects +com.xwiki.googleapps.internal.GoogleDriveAccess \ No newline at end of file From 0952fbd18056fb0342460f5c4b118b9b381ee8f7 Mon Sep 17 00:00:00 2001 From: Paul Libbrecht <paul@hoplahup.net> Date: Mon, 6 Jul 2020 23:54:32 +0200 Subject: [PATCH 17/17] The redirect URL is not OAuth anymore. --- ui/src/main/resources/GoogleApps/OAuth.xml | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/ui/src/main/resources/GoogleApps/OAuth.xml b/ui/src/main/resources/GoogleApps/OAuth.xml index 0db295e..ab2ca4d 100644 --- a/ui/src/main/resources/GoogleApps/OAuth.xml +++ b/ui/src/main/resources/GoogleApps/OAuth.xml @@ -41,19 +41,8 @@ <hidden>true</hidden> <content> {{velocity}} -$services.localization.render("googleapps.login.oauth.message") - -#set($googleApps = $services.googleApps) -#set($success = $googleApps.authorize(true)) -#if($success) -#if($request.state) -$services.localization.render("googleapps.login.oauth.success") -#else -$services.localization.render("googleapps.login.oauth.successwithredirect") -#end -#else -$services.localization.render("googleapps.login.oauth.notauthenticated") -#end + This page is not used anymore in Google Apps. Please remove any link to it. + Please replace the redirect URLs at the Google Console to be /login/XWiki/XWikiLogin . {{/velocity}}</content> </xwikidoc>