Overview
The Google Workspace Connector provides functionality to retrieve files from Google Drive (formerly G Suite) and register them in the Fess index.
This feature requires the fess-ds-gsuite plugin.
Changes in 15.9
The connector was substantially reworked in Fess 15.9. Read this section before upgrading an existing data store configuration.
Warning
crawl_target now defaults to shared_drives, and every value other than legacy requires impersonate_user. An existing configuration that is upgraded unchanged therefore fails at startup with a DataStoreException instead of running.
This is deliberate: the previous behaviour only ever reached the files that were explicitly shared with the service account, so the alternative would be a crawl that silently indexes nothing. Either set impersonate_user to a domain administrator account, or set crawl_target=legacy to keep the previous behaviour.
Behaviour Changes
| Change | What you have to do |
|---|---|
crawl_target defaults to shared_drives and requires impersonate_user | Set impersonate_user, or set crawl_target=legacy. Otherwise the crawl fails at startup. |
The default OAuth scope narrowed from https://www.googleapis.com/auth/drive to https://www.googleapis.com/auth/drive.readonly | Update the domain-wide delegation entry in the Google Workspace admin console, which lists the scopes explicitly. |
crawl_target=users and crawl_target=both additionally require https://www.googleapis.com/auth/admin.directory.user.readonly | Add the scope both to the scopes parameter and to the delegation entry. This is validated at startup. |
The indexed URL is now webViewLink (the browser-openable link) instead of the download link | Run a full re-crawl to pick up the new URLs. |
default_permissions is now a fallback, not an addition | A document with a resolvable ACL indexes that ACL only, no longer the union of the ACL and default_permissions. The result is strictly less permissive. |
| Link-only sharing no longer grants a search role | A domain or anyone permission with allowFileDiscovery=false means “anyone with the link”, which Drive itself does not make discoverable by search. |
| A document whose ACL resolves to nothing is skipped instead of being indexed with no roles | Set default_permissions to keep indexing such documents. Previously they were visible to every user, because an empty role list disables the permission filter. |
fields no longer defaults to * but to an explicit field list | A crawl script that references an unusual field now reads null. Set fields=* to restore the previous projection. |
| Google Docs are exported as Markdown instead of plain text, and Google Sheets as TSV instead of CSV | The indexed text of every Google Doc now contains Markdown syntax characters. Run a full re-crawl. |
refresh_token_interval is ignored | Token refresh is handled by the authentication library. An existing configuration keeps working, and a warning is logged. |
| Google Forms and Google Sites are indexed as metadata only | They have no export format in the Drive API. Previously every one of them produced a crawl error. |
New Capabilities
crawl_targetselects what is crawled: the service account’s own view (legacy), every shared drive in the domain (shared_drives), every directory user’s My Drive (users), or both (both). See Crawl Target.Shared drive items now get the correct ACL. See Permissions and Access Control.
Incremental crawling through the Drive change feed. See Incremental Crawling.
Rate limiting with an exponential back-off that honours
Retry-After, and a failing shared drive or user that no longer aborts the whole crawl. See Rate Limiting and Retries.proxy_usernameandproxy_passwordfor an authenticating proxy.
Supported Services
Google Drive (My Drive, Shared Drives)
Google Docs, Spreadsheets, Slides, Drawings, Apps Script
Google Forms and Google Sites (metadata only; they have no export format)
Prerequisites
Plugin installation is required
A Google Cloud Platform project must be created
A service account must be created and credentials obtained
Domain-wide delegation must be configured for Google Workspace
Unless
crawl_target=legacyis used, a Google Workspace administrator account to impersonate is required
Plugin Installation
Method 1: Place JAR file directly
Method 2: Install from admin console
Open “System” -> “Plugins”
Upload the JAR file
Restart Fess
Configuration
Configure in the admin console under “Crawler” -> “Data Store” -> “Create New”.
Basic Settings
| Item | Example |
|---|---|
| Name | Company Google Drive |
| Handler Name | GoogleDriveDataStore |
| Enabled | On |
Parameter Configuration
Parameter List
| Parameter | Required | Description |
|---|---|---|
private_key | Yes | Service account private key (PEM format, newlines as \n) |
private_key_id | Yes | Private key ID |
client_email | Yes | Service account email address |
impersonate_user | Conditional | The Google Workspace account impersonated through domain-wide delegation. Required unless crawl_target=legacy; the crawl fails at startup without it. shared_drives and both enumerate the shared drives with domain administrator access, so this account must be a domain administrator. |
crawl_target | No | What to crawl: legacy, shared_drives, users or both. Default: shared_drives. See Crawl Target. |
scopes | No | OAuth scopes, comma-separated. Default: https://www.googleapis.com/auth/drive.readonly. crawl_target=users and crawl_target=both additionally require https://www.googleapis.com/auth/admin.directory.user.readonly. |
user_query | No | Admin SDK query used to narrow down the users enumerated by crawl_target=users and crawl_target=both. Default: unset (every user of the customer). |
query | No | Google Drive API search query string. Not applied to the change feed used by incremental crawling. |
corpora | No | Corpora to search. Default: allDrives. Only consumed by crawl_target=legacy, so it has no effect under the default target: shared_drives lists each drive with drive and users lists each My Drive with user, both fixed. |
spaces | No | Spaces to search (Google Drive API spaces parameter, e.g. drive, appDataFolder). Default: unset (API default). Used by crawl_target=legacy and users; ignored for shared_drives. |
fields | No | File fields to request from the Google Drive API. Default: an explicit field list, not *. It covers every field the script context, the ACL resolution, the index URL and the incremental crawl need; a field outside the list reads as null in the crawl script. Set fields=* to request every field, as in previous versions. |
default_permissions | No | Permissions used when the Drive ACL of a document resolves to nothing (comma-separated, e.g. {role}drive-users). This is a fallback, not an addition: a document with a resolvable ACL indexes that ACL only. |
max_size | No | Maximum file size to index (bytes). Default: 10000000 (approx. 10MB) |
number_of_threads | No | Number of parallel processing threads. Default: 1 |
incremental | No | Whether to crawl through the Drive change feed instead of listing everything. Default: false. It is read straight from the parameter field of the data store configuration, before the crawl starts. See Incremental Crawling. |
Advanced Parameters
| Parameter | Description |
|---|---|
domain_permission_format | Role format applied to a type=domain Drive permission. {domain} is replaced with the domain name. Default: {group}{domain} |
thread_pool_timeout_seconds | How long to wait for the worker threads to drain at the end of a crawl (seconds). Default: 60 |
page_size | Page size for files.list and changes.list. Default: 1000; values above 1000 are clamped. |
permission_page_size | Page size for permissions.list and drives.list. Default: 100; values above 100 are clamped. |
max_cached_content_size | Maximum size (in bytes) of content kept in memory; content larger than this is spooled to a temporary file. Default: 1048576 (1MB). |
max_retries | Maximum number of retries for a throttled or transient Drive API failure. Default: 5 |
retry_initial_interval_ms | Initial back-off interval before the first retry (milliseconds). Default: 1000 |
max_backoff_ms | Upper bound of a single back-off wait (milliseconds). Default: 32000 |
read_timeout | HTTP read timeout (milliseconds). Default: 20000 |
connect_timeout | HTTP connection timeout (milliseconds). Default: 20000 |
proxy_host | Proxy server hostname. The proxy is used only when both proxy_host and proxy_port are set; either one alone has no effect. |
proxy_port | Proxy server port number. See proxy_host. |
proxy_username | User name for an authenticating proxy. When set, a Proxy-Authorization header is added to every request. See Limitations for what this does and does not authenticate. |
proxy_password | Password for an authenticating proxy |
ignore_folder | Whether to skip folders. Default: true |
ignore_error | Whether to continue processing on errors. Default: true |
supported_mimetypes | MIME types to index (regex, comma-separated). Default: .* (all types) |
include_pattern | Regex pattern for URLs to include in the index |
exclude_pattern | Regex pattern for URLs to exclude |
refresh_token_interval | Ignored since 15.9. Access tokens are refreshed by the authentication library. An existing setting keeps working and a warning is logged. |
Note
private_key, private_key_id, client_email, proxy_username and proxy_password are removed from the script evaluation context, so a crawl script cannot index them and no search result can disclose them.
Note
When incremental crawling is enabled, the connector writes start_page_tokens and crawl_signature back into the parameter field of the data store configuration. They are managed by the connector and appear alongside the parameters you set; leave them alone. Editing or deleting them makes the next run crawl every scope in full.
Crawl Target
A service account has no Drive of its own and belongs to no Google group, so a crawl that authenticates as the service account itself only ever reaches the files that were explicitly shared with the service account’s address. crawl_target therefore selects whose view of Drive is crawled.
| Value | Description |
|---|---|
legacy | The service account’s own view, as in previous versions. impersonate_user is not required. Only the files explicitly shared with the service account are found. |
shared_drives | Default. Every shared drive in the domain is enumerated, and each one is listed separately. |
users | Every user in the directory is enumerated through the Admin SDK, and the My Drive of each one is listed by impersonating that user. |
both | shared_drives followed by users. A file that appears in several scopes is indexed once. |
The following is validated when the crawl starts, and an invalid combination raises a DataStoreException instead of running:
crawl_targetmust be one oflegacy,shared_drives,usersorboth.impersonate_usermust be set unlesscrawl_target=legacy.scopesmust containhttps://www.googleapis.com/auth/admin.directory.user.readonlywhencrawl_targetisusersorboth.
Note
shared_drives and both enumerate the shared drives with domain administrator access, so the account named by impersonate_user must be a Google Workspace domain administrator. This listing decides the whole scope of the crawl, so a permanent failure aborts the crawl rather than being reported and skipped – a crawl that enumerated no drive must not be able to report success while indexing nothing.
Incremental Crawling
Setting incremental=true makes each scope – one shared drive, or the view of one impersonated user – read the Drive change feed instead of listing everything. A scope with no stored token is listed in full and its change feed is anchored for the next run.
Warning
delete_old_docs is forced to false for every incremental run, and an explicit delete_old_docs=true is overridden rather than honoured (a warning is logged). The stale document sweep deletes every document of the configuration that the current crawl did not touch, which assumes a full crawl; an incremental run only touches the documents that changed, so the sweep would delete the rest of the index.
To drop documents that vanished from Drive, schedule a separate data store configuration with incremental=false.
The start page tokens are persisted only when the crawl finished and the worker threads drained. A crawl that was stopped leaves the tokens untouched and the next run reads the same changes again.
The tokens are also discarded, and every scope crawled in full, when the configuration that decides what a scope yields changed – that is, any of crawl_target, impersonate_user, user_query, query, corpora or spaces. A stored token only describes the population it was taken over, and resuming it after such a change would leave a permanent hole in the index.
Rate Limiting and Retries
A throttled or transient Drive API failure is retried with an exponential back-off, bounded by max_retries, retry_initial_interval_ms and max_backoff_ms. A Retry-After header wins over the exponential wait, but is clamped by max_backoff_ms so that a mistaken header cannot stall the crawl for hours. Only the delta-seconds form of Retry-After is honoured; an HTTP-date falls back to the exponential wait.
429, 500, 502, 503 and 504 are always retried. A 403 is retried only when it is a rate limit error; any other 403 is an authorization failure, which retrying cannot fix, and is reported immediately.
A file listing that could not be finished no longer aborts the whole crawl: the remaining shared drives and users are still crawled, and the failure is written to the crawler log and to the failure URL list in the admin console.
Script Configuration
Available Fields
| Field | Description |
|---|---|
file.name | File name |
file.description | File description |
file.contents | File text content |
file.mimetype | File MIME type |
file.filetype | File type |
file.created_time | Creation date/time |
file.modified_time | Last modified date/time |
file.web_view_link | Link to open in browser |
file.url | File URL. This is webViewLink; when a file has none, https://drive.google.com/open?id=<file id> is used instead. |
file.thumbnail_link | Thumbnail link (valid for short period) |
file.size | File size (bytes) |
file.roles | Access permissions |
Note
Only the fields listed in the fields parameter are populated. A field that is not requested reads null in the script. Set fields=* to request every field, as in previous versions.
For details, see the Google Drive Files API.
Text Extraction of Native Google Types
A native Google type cannot be downloaded and has to be exported. The export target is chosen from the export formats that the Drive API actually reports, not from a fixed table, and an export is bounded at 10MB.
| Type | Exported as |
|---|---|
| Google Docs | Markdown (text/markdown), falling back to plain text and then HTML |
| Google Sheets | TSV (text/tab-separated-values), falling back to CSV |
| Google Slides | Plain text |
| Google Drawings | PNG. There is no text to index, so the metadata is indexed on its own. |
| Apps Script | The exported JSON bundle, from which the script sources are indexed |
| Google Forms, Google Sites | Not exportable. The metadata is indexed and no error is reported. |
Note
Because Google Docs are now exported as Markdown, the indexed text of every Google Doc contains Markdown syntax characters. A full re-crawl is required for the change to reach documents that were already indexed.
Note
The export targets are read from the Drive API once per crawl. If that call fails, the connector falls back to the conversions Drive has always supported – plain text for Google Docs and CSV for Google Sheets – and logs a warning.
Google Cloud Platform Configuration
1. Create a Project
Access https://console.cloud.google.com/:
Create a new project
Enter a project name
Select organization and location
2. Enable Google Drive API
In “APIs & Services” -> “Library”:
Search for “Google Drive API”
Click “Enable”
When
crawl_targetisusersorboth, also enable “Admin SDK API”
3. Create a Service Account
In “APIs & Services” -> “Credentials”:
Select “Create credentials” -> “Service account”
Enter a service account name (e.g., fess-crawler)
Click “Create and continue”
Skip role assignment
Click “Done”
4. Create Service Account Key
For the created service account:
Click on the service account
Open the “Keys” tab
Click “Add key” -> “Create new key”
Select JSON format
Save the downloaded JSON file
5. Enable Domain-wide Delegation
In the service account settings:
Check “Enable domain-wide delegation”
Click “Save”
Copy the “OAuth 2 Client ID”
Credential Configuration
Retrieve Information from JSON File
The downloaded JSON file:
Set the following information in parameters:
private_key_id->private_key_idprivate_key->private_key(keep newlines as\n)client_email->client_email
Private Key Format
private_key preserves newlines as \n:
Usage Examples
Crawl Every User’s My Drive
Parameters:
To narrow the users down, add an Admin SDK query:
Keep the Previous Behaviour
crawl_target=legacy keeps the pre-15.9 traversal, in which only the files explicitly shared with the service account are found. impersonate_user is not required.
Parameters:
Crawl with Permissions
Parameters:
Script:
default_permissions is only used for a document whose Drive ACL resolves to nothing.
Crawl Only Specific File Types
Google Docs only:
Troubleshooting
The Crawl Fails to Start
Symptom: The crawl ends immediately with a DataStoreException
Resolution:
parameter 'crawl_target' must be one of ...: the value ofcrawl_targetis notlegacy,shared_drives,usersorboth.parameter 'impersonate_user' is required when 'crawl_target' is not 'legacy': setimpersonate_userto a domain administrator account, or setcrawl_target=legacy.parameter 'scopes' must include 'https://www.googleapis.com/auth/admin.directory.user.readonly': add that scope toscopesand to the domain-wide delegation entry.
This is the expected outcome of upgrading an existing configuration unchanged. See Changes in 15.9.
Authentication Error
Symptom: 401 Unauthorized or 403 Forbidden
Check:
Verify service account credentials are correct:
Is
private_keyformatted with\nfor newlines?Is
private_key_idcorrect?Is
client_emailcorrect?
Verify Google Drive API is enabled
Verify domain-wide delegation is configured
Verify authorization in Google Workspace admin console
Verify OAuth scope is correct (
https://www.googleapis.com/auth/drive.readonly, plushttps://www.googleapis.com/auth/admin.directory.user.readonlyforcrawl_target=usersorboth)
Domain-wide Delegation Error
Symptom: Not Authorized to access this resource/api
Resolution:
Verify authorization in Google Workspace admin console:
Is the Client ID registered correctly?
Are the OAuth scopes correct? The delegation entry lists them explicitly, so the scope narrowing introduced in 15.9 requires updating it.
Verify domain-wide delegation is enabled for the service account
Verify that the account named by
impersonate_useris a domain administrator whencrawl_targetisshared_drivesorboth
Cannot Retrieve Files
Symptom: Crawl succeeds but 0 files found
Check:
Verify
crawl_targetis what you intend. Withlegacy, only the files explicitly shared with the service account are found, because a service account has no Drive of its own and belongs to no group.Verify files exist in Google Drive
Verify service account has read permissions
Verify domain-wide delegation is configured correctly
Verify access to target user’s Drive is possible
Documents Are Skipped
Symptom: Skipped ... because no permission could be resolved in the crawler log
Resolution:
The Drive ACL of the document resolved to no search role at all, so it was skipped rather than indexed. Indexing a document with no role disables the Fess permission filter for it and makes it visible to every user, which is why it is skipped instead. A skipped document is not a crawl failure, so it appears only in the crawler log and not in the failure URL list.
Set
default_permissionsto index such documents under a fallback permissionVerify that the account named by
impersonate_useris a domain administrator, so that the shared drive ACLs can be readCheck whether the document is shared by link only. A
domainoranyonepermission withallowFileDiscovery=falsegrants no search role, because Drive itself does not make such a document discoverable by search.
API Quota Error
Symptom: 403 Rate Limit Exceeded or 429 Too Many Requests
Resolution:
Such a failure is retried automatically with an exponential back-off. Raise
max_retriesormax_backoff_msif the crawl still fails.Lower
number_of_threadsto reduce the request rateCheck quota in Google Cloud Platform
Increase crawl interval
Request quota increase if needed
Private Key Format Error
Symptom: Invalid private key format
Resolution:
Verify newlines are correctly formatted as \n:
Large Number of Files
Symptom: Crawl takes too long or times out
Resolution:
Enable
incremental=trueso that only the changes since the previous run are crawledSplit shared drives and users into separate data store configurations rather than using
crawl_target=bothNarrow the scope with
query,user_queryorsupported_mimetypesDistribute load with scheduled crawling
Adjust crawl interval
Permissions and Access Control
How Drive Permissions Become Fess Roles
The ACL of a document is resolved in three steps, so that the number of extra API calls stays proportional to the number of shared drives rather than to the number of files:
the inline permissions returned by the file listing, which cost nothing extra;
for an item of a shared drive, whose inline permissions the Drive API does not populate, the ACL of the shared drive itself. It is fetched once per drive with domain administrator access and cached;
for an item that carries its own additional permissions, those permissions.
Each Drive permission becomes a Fess search role:
| Drive permission | Search role |
|---|---|
user | The search role of that user’s email address. The file owners are always added this way. |
group | The search role of that group’s email address. Google group membership is never expanded; Fess is expected to resolve it on the user side through SSO or LDAP. |
domain | domain_permission_format with {domain} replaced by the domain name. Default: {group}{domain} |
anyone | The guest role |
Any of the above with allowFileDiscovery=false, or a deleted permission | No role. Link-only sharing is not discoverable by search in Drive itself either. |
When the result is empty, default_permissions is used instead – as a fallback, not as an addition. When default_permissions is unset too, the document is skipped.
Reflect Google Drive Sharing Permissions
Reflect Google Drive sharing settings in Fess permissions:
Parameters:
Script:
file.roles contains Google Drive sharing information.
Limitations
Drive’s “removed” change signal covers a loss of access as well as a deletion. With
crawl_target=usersorboth, revoking one user’s access to a document drops it from the index even though another user can still read it. It comes back on the next change to that file, or on the next full crawl.When a scope falls back to a full crawl during an incremental run, the stale document sweep stays suppressed, so documents that were deleted from Drive while a scope was unanchored remain in the index. The remedy is a separate
incremental=falseconfiguration whose full crawl sweeps them.Propagating a deletion assumes the indexed URL contains the Drive file ID, which holds for
webViewLinkand for the fallback URL. A crawl script that rewritesurlto a value the file ID does not appear in will not have deletions propagated to it.The change feed is not filtered by
query. Withqueryset andincremental=true, a changed file that does not match the query is still indexed.crawl_target=bothon a large domain issues roughly2 + (number of shared drives) + (number of users)listing sequences. Splitting shared drives and users into separate data store configurations is the practical mitigation.proxy_usernameandproxy_passwordare sent as aProxy-Authorizationrequest header, which only authenticates a plain HTTP request. All Google API traffic is HTTPS, and an HTTPS connection through an authenticating proxy is established by aCONNECTexchange that the JDK drives throughjava.net.Authenticatorrather than through a request header. Such an environment needs the JVM option-Djdk.http.auth.tunneling.disabledSchemes=and anAuthenticatorinstead.
Reference Information
Data Store Connector Overview - Data Store Connector Overview
Microsoft 365 Connector - Microsoft 365 Connector
Box Connector - Box Connector
Data Store Crawling - Data Store Configuration Guide