diff --git a/de/15.9/config/datastore/ds-overview.rst b/de/15.9/config/datastore/ds-overview.rst index 5d9ff6eb..e01c9dc3 100644 --- a/de/15.9/config/datastore/ds-overview.rst +++ b/de/15.9/config/datastore/ds-overview.rst @@ -59,6 +59,9 @@ Kollaborationstools * - :doc:`ds-slack` - fess-ds-slack - Crawlt Slack-Nachrichten und -Dateien + * - :doc:`ds-sharepoint` + - fess-ds-sharepoint + - Crawlt On-Premises-SharePoint-Server-Sites Entwicklungs- und Betriebstools ------------------------------- diff --git a/de/15.9/config/datastore/ds-sharepoint.rst b/de/15.9/config/datastore/ds-sharepoint.rst new file mode 100644 index 00000000..d603f5cc --- /dev/null +++ b/de/15.9/config/datastore/ds-sharepoint.rst @@ -0,0 +1,952 @@ +=========================== +SharePoint Server-Konnektor +=========================== + +Übersicht +========= + +Der SharePoint Server-Konnektor ruft Dokumentbibliothek-Dateien und Listenelemente aus einer +On-Premises-Installation von **SharePoint Server** (2013, 2016, 2019 oder Subscription Edition) +über dessen REST/OData-API ab (bei 2013 über dessen XML/Atom-API) und registriert sie im +|Fess|-Index. + +Für diese Funktion ist das Plugin ``fess-ds-sharepoint`` erforderlich. + +.. note:: + + Wenn Sie stattdessen SharePoint Online (Microsoft 365) crawlen möchten, verwenden Sie + :doc:`ds-microsoft365` und nicht diesen Konnektor. Die OAuth-Unterstützung dieses Konnektors + deckt ausschließlich die anwendungsspezifische Authentifizierung (application-only) von Azure + ACS ab und bietet keine Integration mit der Microsoft Graph API. + +Unterstützte Versionen: SharePoint Server 2013 / 2016 / 2019 / Subscription Edition (SE) + +Unterstützte Inhalte +==================== + +- Dokumentbibliothek-Dateien +- Listenelemente +- Anhänge von Listenelementen + +Voraussetzungen +=============== + +1. Die Installation des Plugins ist erforderlich +2. Das Crawl-Konto benötigt Lesezugriff auf die zu crawlenden Sites, Listen und + Dokumentbibliotheken +3. Wählen Sie genau eine Authentifizierungsmethode – NTLM, Kerberos (SPNEGO) oder OAuth (ACS) – + und halten Sie deren Anmeldedaten bereit + +Plugin-Installation +------------------- + +Installieren Sie es über die Administrationsoberfläche unter "System" -> "Plugin": + +1. Laden Sie ``fess-ds-sharepoint-X.X.X.jar`` herunter +2. Platzieren Sie es unter ``$FESS_HOME/app/WEB-INF/lib`` (oder + ``/usr/share/fess/app/WEB-INF/lib``) +3. Starten Sie |Fess| neu + +Details finden Sie unter :doc:`../../admin/plugin-guide`. + +Konfiguration +============= + +Konfigurieren Sie diesen Konnektor über die Administrationsoberfläche unter "Crawler" -> +"Datenspeicher" -> "Neu erstellen". + +Grundeinstellungen +------------------ + +.. list-table:: + :header-rows: 1 + :widths: 25 75 + + * - Einstellung + - Beispielwert + * - Name + - SharePoint + * - Handler-Name + - SharePointDataStore + * - Aktiviert + - Ein + +Parameter-Einstellungen +----------------------- + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.doclib_path=/Shared Documents + +Parameterliste +~~~~~~~~~~~~~~ + +**URL / Site** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Parameter + - Erforderlich + - Beschreibung + * - ``url`` + - Ja + - Basis-URL des SharePoint-Servers, z. B. ``http://sharepoint.example.com/`` + * - ``site.name`` + - Bedingt + - Name der Site-Sammlung, die unter ``/sites//`` gecrawlt wird. Nicht + erforderlich, wenn ``site.path`` gesetzt ist + * - ``site.path`` + - Nein + - Serverrelativer verwalteter Pfad der Site (z. B. ``/teams/eng``; für die + Root-Site-Sammlung ``/`` verwenden). Wenn gesetzt, wird dieser Wert unverändert anstelle + des fest codierten ``/sites/``-Präfixes verwendet, und ``site.name`` ist dann nicht mehr + erforderlich + * - ``site.list_id`` + - Nein + - Eine einzelne Liste anhand ihrer GUID crawlen (Listen-Crawl-Modus) + * - ``site.list_name`` + - Nein + - Eine einzelne Liste anhand ihres Anzeigenamens crawlen (Listen-Crawl-Modus) + * - ``site.doclib_path`` + - Nein + - Pfad der Dokumentbibliothek unterhalb der Site (Dokumentbibliothek-Crawl-Modus), z. B. + ``/Shared Documents`` + * - ``site.exclude_list`` + - Nein + - Kommagetrennte Regex-Muster für auszuschließende Listen-Entitätstypnamen. Gilt nur für + einen Crawl der gesamten Site + * - ``site.exclude_folder`` + - Nein + - Kommagetrennte Regex-Muster für auszuschließende Titel von Ordnern der obersten Ebene. + Gilt nur für einen Crawl der gesamten Site + * - ``site.crawl_subsites`` + - Nein + - Rekursiv in die Subsites der Site absteigen (Standard: ``false``). Siehe `Subsites und + verwaltete Pfade`_ + * - ``site.max_depth`` + - Nein + - Wie viele Subsite-Ebenen ``site.crawl_subsites`` rekursiv durchlaufen darf (Standard: + ``10``); die Root-Site hat Tiefe 0 + +**Authentifizierung** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Parameter + - Erforderlich + - Beschreibung + * - ``auth.ntlm.user`` + - Nein + - NTLM-Benutzername. Durch das Setzen wird NTLM aktiviert (``DOMAIN\user`` funktioniert) + * - ``auth.ntlm.password`` + - Nein + - NTLM-Passwort + * - ``auth.ntlm.domain`` + - Nein + - Windows-Domäne, die als eigenes NTLM-Feld gesendet wird + * - ``auth.ntlm.workstation`` + - Nein + - Workstation-Name, der bei der NTLM-Aushandlung gesendet wird + * - ``auth.kerberos.principal`` + - Nein + - Client-Principal, geschrieben als ``user@REALM``. Durch das Setzen wird Kerberos/SPNEGO + aktiviert + * - ``auth.kerberos.keytab`` + - Nein + - Pfad zu einer Keytab-Datei mit einem Schlüssel für den Principal. Schließt sich mit + ``auth.kerberos.password`` gegenseitig aus + * - ``auth.kerberos.password`` + - Nein + - Das Passwort des Principals, wird nur verwendet, wenn keine Keytab-Datei gesetzt ist + * - ``auth.kerberos.strip_port`` + - Nein + - Entfernt den Port aus dem Service Principal Name (Standard: ``true``) + * - ``auth.kerberos.use_canonical_hostname`` + - Nein + - Löst den Zielhost vor dem Erstellen des Service Principal Name in seinen kanonischen + Namen auf (Standard: ``false``) + * - ``auth.kerberos.krb5_conf`` + - Nein + - Pfad zu einer ``krb5.conf``. Wird nur angewendet, wenn ``java.security.krb5.conf`` noch + nicht gesetzt ist + * - ``auth.kerberos.debug`` + - Nein + - Aktiviert die Debug-Ausgabe von ``Krb5LoginModule`` (Standard: ``false``) + * - ``auth.oauth.client_id`` + - Nein + - Azure-ACS-OAuth-Client-ID für die anwendungsspezifische Authentifizierung. Durch das + Setzen wird OAuth aktiviert + * - ``auth.oauth.client_secret`` + - Nein + - OAuth-Client-Secret + * - ``auth.oauth.tenant`` + - Nein + - Mandantenname ohne ``.sharepoint.com`` + * - ``auth.oauth.realm`` + - Nein + - Azure-AD-Realm/Verzeichnis-ID + +Es darf **genau eines** von ``auth.kerberos.principal``, ``auth.ntlm.user`` und +``auth.oauth.client_id`` gesetzt werden. Siehe `Authentifizierung`_ weiter unten. + +**Liste** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Parameter + - Erforderlich + - Beschreibung + * - ``list.items.number_per_page`` + - Nein + - Seitengröße für ``GetListItems`` (Standard: ``100``) + * - ``list.item.content.include_fields`` + - Nein + - Kommagetrennte Feldnamen; wenn gesetzt, werden nur diese Listenelement-Felder zu + ``content`` zusammengefügt + * - ``list.item.content.exclude_fields`` + - Nein + - Kommagetrennte Feldnamenmuster (jeweils als Regex behandelt), die zusätzlich zu einer + umfangreichen fest eingebauten Menge an Standardfeldern von ``content`` ausgeschlossen + werden + * - ``list.is_sub_page`` + - Nein + - Behandelt Listenelemente als SitePages-/Wiki-Unterseiten; wirkt sich auf den + Paging-Fallback und die Form des Weblinks aus (Standard: ``false``) + +**HTTP** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Parameter + - Erforderlich + - Beschreibung + * - ``http.connection_timeout`` + - Nein + - HTTP-Verbindungs-Timeout in ms; wird auch als Wartezeit-Timeout für den Connection-Pool + verwendet (Standard: ``30000``) + * - ``http.socket_timeout`` + - Nein + - HTTP-Socket-Timeout (Lesevorgang) in ms (Standard: ``30000``) + * - ``proxy_host`` + - Nein + - HTTP-Proxy-Host + * - ``proxy_port`` + - Bedingt + - HTTP-Proxy-Port; erforderlich, wenn ``proxy_host`` gesetzt ist (Standard: ``-1`` = kein + Proxy) + +**Filterung und Inhalt** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Parameter + - Erforderlich + - Beschreibung + * - ``include_pattern`` + - Nein + - Regex, dem der Wert eines Elements entsprechen muss, damit es gecrawlt wird. Welcher + Wert das genau ist, siehe Hinweis unter dieser Tabelle + * - ``exclude_pattern`` + - Nein + - Regex, das ein übereinstimmendes Element vom Crawling ausschließt + * - ``supported_mimetypes`` + - Nein + - Kommagetrennte Regex-Ausdrücke, von denen der MIME-Typ einer Datei mindestens einem + entsprechen muss (Standard: ``.*``) + * - ``max_content_length`` + - Nein + - Maximale Dateigröße in Bytes; eine Datei über diesem Limit wird übersprungen, nicht als + Fehler behandelt (Standard: ``-1`` = kein Limit) + * - ``extractor_name`` + - Nein + - Fallback-Extraktor, der nur für einen MIME-Typ verwendet wird, den die Extractor-Factory + nicht zuordnen kann (Standard: ``tikaExtractor``) + +**Verhalten** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Parameter + - Erforderlich + - Beschreibung + * - ``sp.version`` + - Nein + - Auf ``2013`` setzen, um für SharePoint 2013 auf die XML/Atom-API-Familie + ``GetXxxByServerRelativeUrl`` umzuschalten (nicht gesetzt ⇒ REST-Dialekt von SharePoint + Online / 2016+) + * - ``retry_limit`` + - Nein + - Maximale Anzahl an Wiederholungsversuchen pro Crawl-Einheit bei einer + SharePoint-Server-/Client-Exception (Standard: ``2``) + * - ``role.skip`` + - Nein + - Überspringt das Abrufen der Berechtigungen pro Element vollständig (Standard: ``false``). + Siehe `Berechtigungen`_ + * - ``ignore_error`` + - Nein + - Protokolliert einen Fehler bei der Inhaltsextraktion einer Datei und überspringt ihn, + anstatt das Crawl-Ziel fehlschlagen zu lassen (Standard: ``false``) + * - ``default_permissions`` + - Nein + - Kommagetrennte Berechtigungszeichenfolgen, die zusätzlich zu den von SharePoint + zurückgegebenen Werten in die Rollenliste jedes Dokuments eingemischt werden + * - ``delete_old_docs`` + - Nein + - Ob Dokumente gelöscht werden, die in diesem Durchlauf nicht aktualisiert wurden (Standard + im Kern: ``true``). Dieses Plugin erzwingt für den aktuellen Durchlauf den Wert + ``false``, sobald irgendein Crawl-Ziel fehlgeschlagen ist + * - ``number_of_threads`` + - Nein + - Wie viele Crawl-Ziele gleichzeitig bearbeitet werden (Standard: ``1`` = kein Thread-Pool), + gedeckelt auf das Doppelte der Prozessoranzahl. Siehe `Paralleles Crawling und Last`_ + * - ``script_type`` + - Nein + - Skript-Engine für das Skript der Datenspeicher-Konfiguration (Standard: ``groovy``) + * - ``readInterval`` + - Nein + - Wartezeit zwischen aufeinanderfolgenden Crawl-Ergebnissen, in ms (Standard: ``0``). + Beachten Sie die camelCase-Schreibweise, die von allen anderen Parametern hier abweicht + +Skript-Einstellungen +-------------------- + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + role=role + +Verfügbare Felder +~~~~~~~~~~~~~~~~~ + +.. list-table:: + :header-rows: 1 + :widths: 16 20 32 32 + + * - Schlüssel + - Listenelement (ItemCrawl) + - Dokumentbibliothek-Datei (FolderCrawl->FileCrawl) + - Anhang (ItemAttachmentsCrawl->FileCrawl) + * - ``url`` + - Weblink + - Datei-URL + - Datei-URL + * - ``host`` + - Hostname + - Hostname + - Hostname + * - ``site`` + - Serverrelativer Pfad (``FileRef``) + - Serverrelativer Pfad + - Serverrelativer Pfad + * - ``title`` + - ``Title``-Feld, sonst ``FileLeafRef``/Dateiname + - Der eigene ``Title``-Listenwert der Dokumentbibliothek-Datei, falls vorhanden, sonst + Dateiname + - Dateiname + * - ``titleWithListName`` + - ``"[listName] title"`` + - ``"[listName] filename"`` (der Listenname ist bei einem Dokumentbibliothek-Crawl immer + leer, es bleibt also praktisch nur der Dateiname) + - ``"[listName] filename"`` + * - ``listName`` + - Anzeigename der Liste, oder ``""`` + - Immer ``""`` + - Tatsächlicher Listenname + * - ``content`` + - Verkettung der Feldwerte + - Extrahierter Text + - Extrahierter Text + * - ``digest`` + - Gekürzter ``content`` + - Gekürzter ``content`` + - Gekürzter ``content`` + * - ``content_length`` + - ``content.length()`` + - ``content.length()`` + - ``content.length()`` + * - ``last_modified`` + - Aus der Listenabfrage + - Aus der Listenabfrage + - Aus der Listenabfrage + * - ``created`` + - Aus der Listenabfrage + - Aus der Listenabfrage + - Aus der Listenabfrage + * - ``mimetype`` + - Immer ``text/html`` + - Erkannt + - Erkannt + * - ``filetype`` + - Abgeleitet von ``mimetype`` + - Abgeleitet von ``mimetype`` + - Abgeleitet von ``mimetype`` + * - ``role`` + - Berechtigungsliste, nur wenn nicht leer + - Berechtigungsliste, nur wenn nicht leer + - Berechtigungsliste, nur wenn nicht leer + * - ``list_name`` + - Vorhanden + - **Nicht vorhanden** + - Vorhanden + * - ``list_id`` + - Vorhanden + - **Nicht vorhanden** + - Vorhanden + * - ``item_id`` + - Vorhanden + - **Nicht vorhanden** + - Vorhanden + +.. note:: + + ``content_length`` ist ``content.length()`` – also die Zeichenanzahl (UTF-16-Codeeinheiten) + des extrahierten bzw. verketteten Textes, nicht die Bytegröße der Datei. Das unterscheidet + sich von ``file.size`` in den Konnektoren für Box, Google Drive und Dropbox, das die + tatsächliche Bytegröße aus den jeweiligen Datei-Metadaten der Dienste ist. Vergleichen Sie + ``content_length`` dieses Konnektors nicht mit jenen Werten. + +**Dynamische Schlüssel: ``val_*``** + +Jeder Schlüssel der ``FieldValuesAsText`` eines Listenelements (die rohe Feldwert-Map, die +SharePoint für dieses Element zurückgibt, einschließlich OData-Metadatenschlüsseln wie +``odata.metadata``) wird unter zwei Namen bereitgestellt: einmal ohne Präfix (nur wenn dieser +Name nicht bereits einem der oben genannten festen Schlüssel entspricht) und immer mit dem +Präfix ``val_`` – ein Feld ``Status`` wird also sowohl zu ``Status`` als auch zu ``val_Status``. + +``val_*``-Schlüssel gibt es nur auf dem **Listenelement-Crawl-Pfad (ItemCrawl)**. Eine +Dokumentbibliothek-Datei (FolderCrawl->FileCrawl) oder ein Anhang eines Listenelements +(ItemAttachmentsCrawl->FileCrawl) erzeugt niemals einen ``val_*``-Schlüssel. + +Authentifizierung +================= + +Es stehen drei Authentifizierungsmethoden zur Verfügung, von denen **genau eine konfiguriert +werden darf**. Werden mehr als eines von ``auth.kerberos.principal``, ``auth.ntlm.user`` und +``auth.oauth.client_id`` gesetzt, schlägt der Datenspeicher-Konfigurationsjob mit einem +Validierungsfehler fehl, bevor überhaupt eine Anfrage gestellt wird. Das ist beabsichtigt: Beim +HTTP-Client wird nur ein einziges Credential registriert, und der Scope, unter dem es registriert +wird, passt ebenso gut auf eine ``Negotiate``-Challenge wie auf eine ``NTLM``-Challenge. Würde +man mehr als eines konfigurieren, kämen andernfalls 401-Fehler zustande, die im Log durch nichts +erklärt werden. + +NTLM +---- + +:: + + auth.ntlm.user={SharePoint-Benutzername} + auth.ntlm.password={Passwort} + auth.ntlm.domain={Windows-Domäne. Optional; standardmäßig nicht gesetzt.} + auth.ntlm.workstation={Workstation-Name, der bei der NTLM-Aushandlung gesendet wird. Optional; standardmäßig nicht gesetzt.} + +``auth.ntlm.domain`` und ``auth.ntlm.workstation`` sind standardmäßig beide nicht gesetzt, +wodurch genau das Credential entsteht, das dieser Konnektor schon immer gebaut hat. Die Domäne +weiterhin als ``DOMAIN\user`` in den Benutzernamen zu schreiben, funktioniert nach wie vor. Wird +``auth.ntlm.domain`` gesetzt, wird die Domäne stattdessen als eigenes NTLM-Feld gesendet – das +ist es, was ein Server erwartet, der die kombinierte Form ablehnt. + +Kerberos (SPNEGO) +----------------- + +**Unterstützter Rahmen:** eine einzelne Crawler-JVM, eine ``krb5.conf`` pro Fess-Instanz, eine +Keytab-Datei oder ein Passwort, keine Delegation, kein Channel Binding, und gegenseitig +ausschließend zu NTLM und OAuth. Alles außerhalb dieses Rahmens wird nicht unterstützt. + +:: + + auth.kerberos.principal={Client-Principal, geschrieben als user@REALM. Durch das Setzen wird Kerberos aktiviert.} + auth.kerberos.keytab={Pfad zu einer Keytab-Datei mit einem Schlüssel für den Principal. Schließt sich mit auth.kerberos.password gegenseitig aus.} + auth.kerberos.password={Das Passwort des Principals. Wird nur verwendet, wenn keine Keytab-Datei gesetzt ist.} + auth.kerberos.strip_port={true oder false. Entfernt den Port aus dem Service Principal Name. Standard ist true.} + auth.kerberos.use_canonical_hostname={true oder false. Löst den Zielhost für den Service Principal Name in seinen kanonischen Namen auf. Standard ist false.} + auth.kerberos.krb5_conf={Pfad zu einer krb5.conf. Wird nur angewendet, wenn java.security.krb5.conf noch nicht gesetzt ist.} + auth.kerberos.debug={true oder false. Debug-Ausgabe von Krb5LoginModule. Standard ist false.} + +- **``krb5.conf`` gehört in ``jvm.crawler.options``**, etwa als + ``-Djava.security.krb5.conf=/path/to/krb5.conf``. Das Crawling von Datenspeichern läuft im + **Kindprozess** des Crawlers, daher hat es keine Wirkung, diese Einstellung irgendwo zu setzen, + das nur die Webapp betrifft, und ein Neustart der Webapp übernimmt eine Änderung nicht – der + Crawl-Job muss erneut ausgeführt werden. ``auth.kerberos.krb5_conf`` ist eine Erleichterung für + den Fall, dass diese Eigenschaft noch von nichts gesetzt wurde: Sie **überschreibt niemals + einen bereits gesetzten Wert**, da die Eigenschaft JVM-global ist und eine einzelne + Crawler-JVM innerhalb eines Crawl-Jobs jede Datenspeicher-Konfiguration ausführt. Wenn sie das + Überschreiben verweigert, protokolliert sie eine Warnung, die beide Pfade nennt. +- **Setzen Sie ``udp_preference_limit = 1`` im Abschnitt ``[libdefaults]`` von ``krb5.conf``.** + Ohne diese Einstellung versucht das JDK zuerst UDP, und wenn der KDC nicht antwortet (nicht + erreichbar, eine Firewall verwirft UDP 88, oder die Antwort ist größer als die + Datagrammgröße), wiederholt es dreimal je dreißig Sekunden, bevor es auf TCP zurückfällt. Ein + Crawl, der pro Authentifizierung etwa anderthalb Minuten lang hängen zu bleiben scheint, ohne + dass etwas im Log steht, hat meist diese Ursache. +- **Schreiben Sie den Principal immer als ``user@REALM``.** ``default_realm`` ist JVM-global, + und mehrere SharePoint-Farmen in unterschiedlichen Realms müssen sich unter Umständen eine + ``krb5.conf`` teilen, sodass ein bloßes ``user`` gegen den Realm aufgelöst wird, den diese + Datei zufällig nennt. +- **``auth.kerberos.use_canonical_hostname`` ist standardmäßig ``false``** – bewusst abweichend + vom eigenen Standardwert von Apache HttpClient. Ist es aktiviert, wird der Zielhost vor dem + Erstellen des Service Principal Name einer Reverse-DNS-Auflösung unterzogen, was bei + alternativen Access Mappings oder hinter einem Load Balancer einen Namen erzeugen kann, für + den kein SPN registriert ist – und der daraus resultierende Fehler lässt in keiner Weise auf + DNS als Ursache schließen. Aktivieren Sie es nur, wenn der SPN wirklich gegen den kanonischen + Namen registriert ist. +- **IIS Extended Protection mit ``tokenChecking=Require`` kann nicht funktionieren.** Weder + Apache HttpClient 4.5 noch 5.x unterstützt Channel Binding. IIS setzt diesen Wert + standardmäßig auf ``None``, sodass dies meist nicht zutrifft; trifft es doch zu, gibt es keine + Umgehungsmöglichkeit. +- **Das Ticket wird nur einmal beim Erstellen des HTTP-Clients für den Crawl abgerufen und + danach nie erneuert.** Ein Crawl, der länger läuft als die Gültigkeitsdauer des Tickets, + beginnt mittendrin, bei der Authentifizierung zu scheitern. +- **``auth.kerberos.password`` wird, genau wie ``auth.ntlm.password``, im Klartext gespeichert + und angezeigt.** Fess besitzt keinen Maskierungsmechanismus für Parameter von + Datenspeicher-Handlern; der Bearbeitungsbildschirm der Datenspeicher-Konfiguration stellt sie + als einfaches Textfeld im Klartext dar. Bevorzugen Sie ``auth.kerberos.keytab`` und vergeben + Sie für die Keytab-Datei restriktive Zugriffsrechte. +- ``auth.kerberos.debug=true`` bewirkt, dass ``Krb5LoginModule`` in die Standardausgabe des + Crawler-Prozesses schreibt, nicht in das Fess-Log. + +OAuth (ACS) +----------- + +:: + + auth.oauth.client_id={OAuth-Client-ID} + auth.oauth.client_secret={OAuth-Client-Secret} + auth.oauth.tenant={Mandantenname ohne .sharepoint.com} + auth.oauth.realm={Azure-AD-Realm/Verzeichnis-ID} + +Durch das Setzen von ``auth.oauth.client_id`` wird ein Client-Credentials-Flow +(anwendungsspezifisch) gegen den Windows Azure Access Control Service aktiviert, +``https://accounts.accesscontrol.windows.net/{realm}/tokens/OAuth/2``. Das Access Token wird +einmal beim Erstellen des HTTP-Clients für den Crawl abgerufen, bei jeder Anfrage als +``Bearer``-``Authorization``-Header angewendet und bei einem 401 einmal erneuert und erneut +versucht. **Microsoft hat ACS als veraltet eingestuft und dessen Abschaltung angekündigt**; +dieser Konnektor protokolliert bei jedem OAuth-konfigurierten Crawl eine entsprechende Warnung. +Ein Entra-ID-App-Registrierungsflow (per Zertifikat oder Client-Secret) ist hier nicht +implementiert – nur die veraltete anwendungsspezifische ACS-Authentifizierung. + +Bevor OAuth verdrahtet wird, wird nur geprüft, ob ``auth.oauth.client_id`` vorhanden ist; +``client_secret``, ``tenant`` und ``realm`` werden bedingungslos gelesen und können, wenn sie +weggelassen werden, stillschweigend leer bleiben – das lässt den Tokenerwerb scheitern, ohne +dass eine eigene Validierungsmeldung erscheint. + +**``sp.version=2013`` und OAuth haben noch nie zusammen funktioniert.** Jeder API-Aufruf, den +dieser Konnektor für SharePoint 2013 ausführt, läuft über den XML/Atom-Client, und kein +Codepfad in diesem Client hängt einer Anfrage ein OAuth-Token an – sind also beide gesetzt, wird +jede Anfrage unauthentifiziert gesendet. Der Crawl protokolliert eine Warnung mit genau diesem +Sachverhalt und nennt ``auth.ntlm.*`` als Alternative; der Job schlägt dadurch nicht fehl. +Verwenden Sie für SharePoint 2013 ``auth.ntlm.*``. + +Berechtigungen +============== + +``role.skip=true`` (Standard ``false``) überspringt das Abrufen der Berechtigungen pro Element +vollständig: Es wird kein ``GetListItemRole``-Aufruf gemacht, es wird nie ein ``role``-Schlüssel +für das Element gesetzt, und das Dokument trägt am Ende nur die statische Permission-Einstellung +der Datenspeicher-Konfiguration sowie, falls konfiguriert, ``default_permissions`` – von +SharePoint abgeleitete Berechtigungen erreichen es überhaupt nicht. + +Wenn Rollen abgerufen werden, werden SharePoints eigene Benutzer, Sicherheitsgruppen und +SharePoint-Gruppen expandiert und auf Fess-Suchrollen abgebildet: + +- Ein **On-Premises-AD**-Konto oder eine solche Gruppe (Anmeldename enthält einen Backslash und + beginnt nicht mit einem Azure-Claim-Präfix) wird über die Standard-Rollenhelfer für + AD-Benutzer/-Gruppen abgebildet. +- Ein **Azure-AD-(Entra-ID-)**-Konto (Anmeldename beginnt mit ``i:0#.f|membership|``) wird + **zweimal** abgebildet – einmal über den vollständigen Azure-Claim-Wert, einmal über den + AD-Kontoanteil vor dem ``@`` in diesem Claim –, sodass für denselben Benutzer sowohl eine + Rolle im Entra-ID-Stil als auch eine im AD-Stil hinzugefügt wird. Eine Sicherheitsgruppe, die + als Azure-Gruppe erkannt wird (anhand eines von mehreren Claim-Stil-Präfixen, darunter die + spezielle "Alle"-Gruppe ``spo-grid-all-users``), wird auf dieselbe Weise in beiden Formen + abgebildet. +- Bei einer **SharePoint-Gruppe** wird deren eigene Mitgliedschaft (Benutzer, + Sicherheitsgruppen, verschachtelte Gruppen) rekursiv expandiert, wobei eine Schutzmaßnahme + gegen bereits besuchte Gruppen unendliche Rekursion zwischen Gruppen verhindert, die sich + gegenseitig enthalten. + +``default_permissions`` (kommagetrennt) wird **nach** all dem Vorstehenden eingemischt und gilt +selbst dann, wenn SharePoint für das Element überhaupt keine Rolle zurückgegeben hat – der Fall, +den sowohl ``role.skip=true`` als auch "SharePoint hat nichts zurückgegeben" erzeugen. Die +endgültige Rollenliste ist die – dedupliziert – Vereinigung aus der statischen +Permission-Einstellung der Datenspeicher-Konfiguration, den von SharePoint abgeleiteten Rollen +(sofern nicht übersprungen) und ``default_permissions``. + +Subsites und verwaltete Pfade +============================= + +Wird ``site.path`` gesetzt, wird der angegebene serverrelative verwaltete Pfad unverändert +anstelle des fest codierten ``/sites/``-Präfixes verwendet, und ``site.name`` ist dann nicht +mehr erforderlich. + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Szenario + - Einstellung + * - Root-Site-Sammlung + - ``site.path=/`` + * - Die Site ``/teams/eng`` + - ``site.path=/teams/eng`` + * - Die klassische Form ``/sites/mysite/`` + - ``site.name=mysite`` (``site.path`` nicht setzen) + +Wird ``site.crawl_subsites`` (Standard ``false``) gesetzt, steigt ein vollständiger Site-Crawl – +also einer, bei dem weder ``site.list_name`` noch ``site.doclib_path`` gesetzt ist – rekursiv in +die Subsites der Site ab, die über ``_api/web/webinfos`` ermittelt werden. Bleibt der Parameter +ungesetzt, stellt der Crawl weiterhin exakt dieselben Anfragen wie bisher, einschließlich der +Tatsache, dass ``webinfos`` überhaupt nie angefragt wird. + +Die Dokumente einer Subsite landen in derselben Datenspeicher-Konfiguration wie die der +Root-Site, unter ihren eigenen serverrelativen Pfaden – im Index gibt es nichts, das ein +Dokument als von einer Subsite statt von der Root-Site stammend kennzeichnet. + +``site.max_depth`` (Standard ``10``) begrenzt, wie viele Subsite-Ebenen unterhalb der Root-Site +gecrawlt werden, sobald ``site.crawl_subsites=true`` gilt. Die Root-Site selbst hat Tiefe 0, +sodass ``site.max_depth=1`` nur die direkten Kinder der Root-Site crawlt und nicht weiter. Wird +bei ``site.crawl_subsites=true`` ein Wert unter ``1`` gesetzt, schaltet das die Funktion wieder +ab – es wird dann überhaupt keine Subsite gecrawlt – und dies wird beim Start des Crawls als +Warnung protokolliert. + +Das Aktivieren des Subsite-Crawlings **vervielfacht die Gesamtdauer des Crawls** ungefähr um die +Anzahl der gefundenen Subsites (begrenzt durch ``site.max_depth``): Jede Subsite erhält ihre +eigene vollständige Ordner- und Listenauflistung sowie, falls die Tiefenbegrenzung noch nicht +erreicht ist, ihren eigenen ``webinfos``-Aufruf – zusätzlich zu allem, was der Crawl der +Root-Site ohnehin schon leistet. + +``number_of_threads`` und ``readInterval``, beschrieben unter `Paralleles Crawling und Last`_, +gelten für einen rekursiven Subsite-Crawl genauso wie für jeden anderen Crawl. + +Paralleles Crawling und Last +============================ + +``number_of_threads`` (Standard ``1``) gibt an, wie viele Crawl-Ziele gleichzeitig bearbeitet +werden. Beim Standardwert läuft der Crawl exakt wie bisher: Jedes Ziel wird auf dem +Crawling-Thread gecrawlt, und **es wird überhaupt kein Thread-Pool erstellt**. + +Der Wert wird auf **das Doppelte der Prozessoranzahl** der Maschine gedeckelt, auf der Fess +läuft, damit eine Datenspeicher-Konfiguration nicht mehr Parallelität anfordern kann, als der +Host leisten kann. Ein Wert unter ``1`` – oder ein leerer bzw. nicht parsbarer Wert – fällt auf +``1`` zurück, anstatt berücksichtigt zu werden oder den Job scheitern zu lassen. Wurde ein Wert +gedeckelt oder lag er unter ``1``, werden sowohl der angeforderte als auch der tatsächliche Wert +protokolliert; ein nicht parsbarer Wert protokolliert eine Warnung. Ein leerer Wert protokolliert +nichts, da ein leeres Feld lediglich bedeutet, dass der Parameter schlicht nicht gesetzt wurde. + +Der HTTP-Connection-Pool wird entsprechend dimensioniert. Apache HttpClient erlaubt +standardmäßig nur 2 Verbindungen pro Route, und ein gesamter Crawl gilt als eine einzige Route: +Ohne diese Anhebung würde jeder Thread ab dem dritten seine Zeit damit verbringen, auf eine +Verbindung zu warten, statt Anfragen zu stellen. + +**``readInterval`` taktet die Übergabe der Dokumente weiterhin mit einem Dokument pro Intervall, +unabhängig davon, worauf es gesetzt ist.** Threads machen das Auffinden und Abrufen beim Crawl +schneller; sie machen nicht, dass Dokumente schneller beim Indexer ankommen. Das ist +beabsichtigt: Das vom Betreiber konfigurierte Intervall durch die Thread-Anzahl zu teilen, würde +genau die Last vervielfachen, die dieses Intervall eigentlich begrenzen soll. Ein Worker, der +ein Dokument fertigbearbeitet hat, während vorherige Dokumente noch übergeben werden, wartet +einfach. + +Was das Erhöhen von ``number_of_threads`` tatsächlich vervielfacht, ist die Anfragerate +gegenüber SharePoint. Der weiter unten beschriebene 503-Backoff und die Wartezeit durch +``X-SharePointHealthScore`` werden pro Crawl-Ziel auf dem Thread angewendet, der es crawlt, +sodass ``n`` Threads bis zu ``n``-mal so viele Anfragen stellen wie ein einzelner Thread – auch +während eines Zeitraums, in dem die Farm signalisiert, dass sie ausgelastet ist. Erhöhen Sie +diesen Wert bei einer On-Premises-Farm nur schrittweise. + +Zwei Dinge setzen dem tatsächlichen Nutzen zusätzlicher Threads eine Obergrenze: + +- **Die Mitgliedschaft jeder SharePoint-Gruppe wird beim ersten Lesen jeweils nur von einem + Thread nach dem anderen gelesen.** Berechtigungen werden über einen Cache aufgelöst, der sich + über den gesamten Crawl erstreckt und durch eine einzelne Sperre geschützt wird, die während + der Mitgliederabfrage einer Gruppe gehalten wird. Diese Sperre verhindert, dass ein Thread + einem anderen eine Gruppe übergibt, deren Mitglieder noch gelesen werden – was dazu führen + würde, dass die von dieser Gruppe geschützten Elemente ohne jede ihrer Berechtigungen + indexiert werden. Sobald eine Gruppe im Cache liegt, ist jeder spätere Zugriff darauf eine + billige Abfrage; es handelt sich also um **Cold-Cache-Kosten**: Der Crawl einer Site mit vielen + unterschiedlichen Gruppen verbringt seine ersten Minuten näher an einem einzelnen Thread als + an ``n`` Threads, während eine Site, deren Elemente sich eine Handvoll Gruppen teilen, davon + kaum etwas merkt. ``role.skip=true``, das überhaupt keine Berechtigungen liest, vermeidet + diese Kosten vollständig. +- Die Discovery erfolgt pro Site sequenziell: Die Ordner- und Listenauflistung einer Site bilden + ein einziges Crawl-Ziel, sodass es für Threads nichts zu verteilen gibt, bis dieses Ziel + abgeschlossen ist und seine Funde in die Warteschlange eingereiht hat. + +**Eine 503-Antwort** wird wie jeder andere Fehler bis zu ``retry_limit``-mal erneut versucht, +jedoch mit einer vor jedem erneuten Versuch wachsenden Wartezeit: 2 Sekunden, dann 4, dann 8, +verdoppelnd bis zu einer Obergrenze von 30 Sekunden, jeweils zufällig auf 70–129 % dieses Werts +variiert. Ein Crawl-Ziel, das weiterhin 503 zurückgibt, zahlt diese Wartezeit vor jedem +tatsächlich stattfindenden erneuten Versuch, aber nicht nach dem letzten. + +**Jede Antwort** – ob erfolgreich oder nicht, einschließlich einer Seite einer Auflistung, die +der Crawl gleich verwerfen wird – wird auf den Response-Header ``X-SharePointHealthScore`` +(0 = im Leerlauf bis 10 = sehr ausgelastet) untersucht. Ein Wert von 9 oder darüber lässt den +Crawl warten, bevor irgendetwas anderes geschieht: Bei Score 9 wird etwa 2 Sekunden gewartet, bei +Score 10 etwa 4 Sekunden, und so weiter, wobei sich der Wert für jeden Punkt über 9 verdoppelt. +**Das summiert sich über den gesamten Crawl hinweg auf, ohne eine Obergrenze für die Summe**: +Eine Farm, die unter anhaltender Last bei Health Score 9 verharrt, fügt zu *jeder einzelnen +Anfrage* dieses Konnektors rund 2 Sekunden hinzu – einschließlich jeder Seite jeder Ordner- und +Listenauflistung –, wodurch aus einem Crawl, der sonst Stunden dauern würde, einer werden kann, +der erheblich länger dauert. Wenn sich ein Crawl unerwartet um eine Größenordnung verlangsamt, +prüfen Sie zuerst den Health Score der Farm in diesem Zeitraum, bevor Sie eine andere Ursache +vermuten. + +Anwendungsbeispiele +=================== + +Alle folgenden Beispiele setzen NTLM voraus. Um stattdessen Kerberos oder OAuth zu verwenden, +siehe `Authentifizierung`_ und ersetzen Sie die ``auth.ntlm.*``-Zeilen. + +Listen-Crawl +------------ + +Parameter: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.list_name=Tasks + +Skript: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +Dokumentbibliothek-Crawl +------------------------ + +Parameter: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.doclib_path=/Shared Documents + +Skript: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +Crawlen einer ``/teams/``-Site +------------------------------ + +Mit ``site.path`` können Sie direkt auf eine Dokumentbibliothek einer Site unter einem anderen +verwalteten Pfad als ``/sites/`` verweisen. + +Parameter: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.path=/teams/eng + site.doclib_path=/Shared Documents + +Skript: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +Rekursiver Subsite-Crawl +------------------------ + +Startet bei der Root-Site-Sammlung und folgt Subsites bis zu einer Tiefe von 3 Ebenen. + +Parameter: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.path=/ + site.crawl_subsites=true + site.max_depth=3 + +Skript: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + role=role + +Einschränkungen +=============== + +- **Keinerlei inkrementelles oder Delta-Crawling.** Es gibt in diesem Konnektor nirgends ein + Change-Token, eine Delta-Query oder eine Filterung nach "zuletzt geändert seit" – jeder + Durchlauf listet vollständig jede Liste, jeden Ordner und jede Datei auf, die er erreichen + soll. ``delete_old_docs`` steuert lediglich, ob Dokumente, die der aktuelle vollständige Crawl + nicht erneut gesehen hat, im Nachhinein gelöscht werden; das ist nachträgliches Aufräumen, + kein inkrementelles Abrufen. +- **``%`` und ``#`` in Datei-/Ordnernamen** werden auf dem Standard-Codepfad (nicht ``2013``) + unterstützt. Nur SharePoint Server 2019 und die Subscription Edition lassen diese beiden + Zeichen in einem Namen überhaupt zu; 2016 lehnt sie ausdrücklich weiterhin ab, und 2013 + ebenso. Der Standard-Codepfad erreicht eine solche Datei über die + ``...ByServerRelativePath(decodedUrl=...)``-Endpunkte, die den dekodierten Pfad + entgegennehmen, und der Crawl maskiert beide Zeichen zusätzlich in dem Link, unter dem er die + Datei indexiert. **Mit ``sp.version=2013`` lässt sich eine solche Datei nicht erreichen**, da + dieser Pfad die älteren ``...ByServerRelativeUrl(...)``-Endpunkte verwendet, die ihr Argument + als bereits kodierte URL lesen. Das ist eine bewusste Einschränkung und keine Lücke: Eine + SharePoint-2013-Farm kann einen solchen Namen gar nicht enthalten. Relevant wird es nur, wenn + ``sp.version=2013`` gegen einen 2019- oder Subscription-Edition-Server verwendet wird, was + keine zu verwendende Konfiguration ist. Siehe + `Use of # and % characters in file and folder names + `__ + und `File names - expanded support for special characters + `__. +- **IIS Extended Protection mit ``tokenChecking=Require`` kann nicht unterstützt werden.** Weder + Apache HttpClient 4.5 noch 5.x implementiert Channel Binding, worauf Extended Protection bei + ``Require`` angewiesen ist. IIS setzt diese Einstellung standardmäßig auf ``None``, sodass die + meisten Farmen nicht betroffen sind; für eine Farm, bei der ``Require`` gesetzt ist, gibt es + keine Umgehungsmöglichkeit. +- **Passwörter in Parametern der Datenspeicher-Konfiguration werden im Klartext gespeichert und + angezeigt.** Das gilt gleichermaßen für ``auth.ntlm.password`` und ``auth.kerberos.password``: + Fess besitzt keinen Maskierungsmechanismus für Parameter von Datenspeicher-Handlern, und der + Bearbeitungsbildschirm der Datenspeicher-Konfiguration stellt sie als einfaches Textfeld im + Klartext dar. Bevorzugen Sie, wo Kerberos verfügbar ist, ``auth.kerberos.keytab`` gegenüber + ``auth.kerberos.password``, und vergeben Sie für die Keytab-Datei restriktive Zugriffsrechte. +- **``sp.version=2013`` und OAuth haben noch nie zusammen funktioniert.** Jeder API-Aufruf für + SharePoint 2013 läuft über den XML/Atom-Client, und kein Codepfad in diesem Client hängt einer + Anfrage ein OAuth-Token an, sodass bei beiden gesetzten Werten jede Anfrage unauthentifiziert + gesendet wird. Verwenden Sie für SharePoint 2013 ``auth.ntlm.*``. +- **Verwaltete Pfade außer ``/sites/`` und dem über ``site.path`` gesetzten werden weiterhin + nicht von selbst entdeckt.** ``site.crawl_subsites`` steigt nur ausgehend von der + konfigurierten Root-Site rekursiv ab, und ``site.path`` erreicht genau den einen von Ihnen + gesetzten verwalteten Pfad, nicht jeden verwalteten Pfad auf der Farm. + +Fehlerbehebung +============== + +Authentifizierung schlägt lautlos fehl +-------------------------------------- + +**Symptom**: Anfragen liefern 401 (oder Ähnliches) zurück, ohne dass das Log einen klaren Grund +dafür nennt + +**Zu überprüfen**: + +1. Prüfen Sie, ob mehr als eines von ``auth.kerberos.principal``, ``auth.ntlm.user`` und + ``auth.oauth.client_id`` gesetzt ist – zwei oder mehr lassen den Job vor Beginn des Crawls + mit einem Validierungsfehler scheitern +2. Stellen Sie bei Kerberos sicher, dass ``-Djava.security.krb5.conf=...`` in + ``jvm.crawler.options`` gesetzt ist. Wird es irgendwo gesetzt, das nur die Webapp betrifft, + hat das keine Wirkung. Führen Sie nach einer Änderung den Crawl-Job erneut aus – ein Neustart + der Webapp übernimmt die Änderung nicht +3. Stellen Sie bei Kerberos sicher, dass ``udp_preference_limit = 1`` im Abschnitt + ``[libdefaults]`` von ``krb5.conf`` gesetzt ist. Ohne diese Einstellung kann ein nicht + antwortender KDC dazu führen, dass jede Authentifizierung etwa 90 Sekunden lang hängen + bleibt (drei UDP-Wiederholungen zu je 30 Sekunden), ohne dass etwas im Log steht +4. Stellen Sie sicher, dass der Principal als ``user@REALM`` geschrieben ist – ein bloßes + ``user`` wird gegen den ``default_realm`` aufgelöst, den die gemeinsam genutzte + ``krb5.conf`` zufällig nennt +5. Stellen Sie bei OAuth sicher, dass ``client_secret``, ``tenant`` und ``realm`` nicht leer + sind – validiert wird nur, ob ``client_id`` vorhanden ist, sodass die übrigen stillschweigend + leer bleiben können +6. Stellen Sie sicher, dass IIS Extended Protection nicht auf ``tokenChecking=Require`` gesetzt + ist – für diese Einstellung gibt es keine Umgehungsmöglichkeit +7. Prüfen Sie bei einem lange laufenden Crawl, ob er erst mittendrin zu scheitern begonnen hat – + das Kerberos-Ticket wird nur einmal beim Erstellen des HTTP-Clients abgerufen und nie + erneuert, sodass ein Crawl, der die Ticketlaufzeit überdauert, mittendrin zu scheitern + beginnt + +Der Crawl ist langsam (503-Fehler und der Health Score) +------------------------------------------------------- + +**Symptom**: Der Crawl dauert erheblich länger als erwartet oder läuft in ein Timeout + +**Zu überprüfen**: + +1. Prüfen Sie den ``X-SharePointHealthScore`` der SharePoint-Farm während des langsamen + Zeitraums. Ein Wert von 9 oder darüber fügt vor jeder Anfrage eine Wartezeit hinzu (bei 9 + etwa 2 Sekunden, bei 10 etwa 4 Sekunden, sich danach verdoppelnd, ohne Obergrenze für die + Summe), wodurch aus einem Crawl, der eigentlich Stunden dauern sollte, einer werden kann, + der erheblich länger dauert +2. Prüfen Sie auf wiederholte 503-Antworten. Ein 503 wird bis zu ``retry_limit``-mal erneut + versucht, wobei vor jedem Versuch 2, dann 4, dann 8 Sekunden (gedeckelt bei 30) gewartet wird +3. Prüfen Sie, ob ``number_of_threads`` zu stark erhöht wurde. Mehr Threads bedeuten ungefähr + proportional mehr Anfragen gegenüber SharePoint, was den Health Score weiter nach oben + treiben kann. Erhöhen Sie ihn bei einer On-Premises-Farm schrittweise +4. Denken Sie bei ``site.crawl_subsites=true`` daran, dass die Gesamtdauer des Crawls ungefähr + mit der Anzahl der gefundenen Subsites wächst – erwägen Sie, den Umfang mit + ``site.max_depth`` einzugrenzen + +Es wird nichts indexiert +------------------------ + +**Symptom**: Der Crawl endet normal, aber die Suche liefert null Ergebnisse + +**Zu überprüfen**: + +1. Prüfen Sie das Crawler-Log auf Fehler oder Warnungen (setzen Sie ``org.codelibs.fess.ds`` in + ``app/WEB-INF/env/crawler/resources/log4j2.xml`` auf ``DEBUG``) +2. Prüfen Sie ``url``, ``site.name`` (bzw. ``site.path``) und ``site.list_name`` auf Tippfehler – + denken Sie daran, dass ``site.name`` nicht mehr benötigt wird, sobald ``site.path`` gesetzt + ist +3. Stellen Sie sicher, dass die Authentifizierung tatsächlich erfolgreich ist (keine + 401-Fehler) – eine Anfrage, die nie authentifiziert wird, ist eine weitaus häufigere Ursache + als ein falsch konfiguriertes ``role.skip`` oder ``default_permissions`` +4. Denken Sie, falls ``include_pattern`` oder ``exclude_pattern`` gesetzt ist, daran, dass diese + gegen einen serverrelativen Pfad (bei einer Dokumentbibliothek-Datei oder einem Anhang eines + Listenelements) oder gegen ``FileRef`` (bei einem Listenelement) abgeglichen werden – nicht + gegen die in den Suchergebnissen angezeigte URL. Prüfen Sie, ob ein Muster versehentlich für + eine vollständige URL geschrieben wurde +5. Prüfen Sie, ob ``supported_mimetypes`` oder ``max_content_length`` die erwarteten Dateien + ausschließt +6. Prüfen Sie, ob ``site.exclude_list`` oder ``site.exclude_folder`` das Ziel unbeabsichtigt + ausschließt + +Weiterführende Informationen +============================ + +- :doc:`ds-overview` - Übersicht der Datenspeicher-Konnektoren +- :doc:`ds-microsoft365` - Microsoft 365-Konnektor (für SharePoint Online) +- :doc:`../../admin/dataconfig-guide` - Leitfaden zur Datenspeicher-Konfiguration +- :doc:`../../admin/plugin-guide` - Leitfaden zur Plugin-Verwaltung diff --git a/de/15.9/config/datastore/index.rst b/de/15.9/config/datastore/index.rst index d9904132..25aef80d 100644 --- a/de/15.9/config/datastore/index.rst +++ b/de/15.9/config/datastore/index.rst @@ -25,6 +25,7 @@ Die Datenspeicher-Konnektoren von |Fess| ermöglichen es, Inhalte aus verschiede ds-atlassian ds-slack + ds-sharepoint .. toctree:: :maxdepth: 2 diff --git a/en/15.9/config/datastore/ds-microsoft365.rst b/en/15.9/config/datastore/ds-microsoft365.rst index 69e0db92..aea0145f 100644 --- a/en/15.9/config/datastore/ds-microsoft365.rst +++ b/en/15.9/config/datastore/ds-microsoft365.rst @@ -10,6 +10,12 @@ The Microsoft 365 Connector provides functionality to retrieve data from Microso This feature requires the ``fess-ds-microsoft365`` plugin. +.. note:: + + The SharePoint this connector targets is SharePoint Online (Microsoft 365). To crawl an + on-premises SharePoint Server deployment (2013, 2016, 2019, or Subscription Edition), use + :doc:`ds-sharepoint` instead. + Supported Services ================== @@ -654,6 +660,7 @@ Reference Information ===================== - :doc:`ds-overview` - Data Store Connector Overview +- :doc:`ds-sharepoint` - SharePoint Server Connector (on-premises) - :doc:`ds-gsuite` - Google Workspace Connector - :doc:`../../admin/dataconfig-guide` - Data Store Configuration Guide - `Microsoft Graph API `_ diff --git a/en/15.9/config/datastore/ds-overview.rst b/en/15.9/config/datastore/ds-overview.rst index a6c801e2..cc916aa6 100644 --- a/en/15.9/config/datastore/ds-overview.rst +++ b/en/15.9/config/datastore/ds-overview.rst @@ -60,6 +60,9 @@ Collaboration Tools * - :doc:`ds-slack` - fess-ds-slack - Crawl Slack messages and files + * - :doc:`ds-sharepoint` + - fess-ds-sharepoint + - Crawl on-premises SharePoint Server sites Development & Operations Tools ------------------------------ diff --git a/en/15.9/config/datastore/ds-sharepoint.rst b/en/15.9/config/datastore/ds-sharepoint.rst new file mode 100644 index 00000000..7314117a --- /dev/null +++ b/en/15.9/config/datastore/ds-sharepoint.rst @@ -0,0 +1,873 @@ +=========================== +SharePoint Server Connector +=========================== + +Overview +======== + +The SharePoint Server Connector retrieves document library files and list items from an +on-premises **SharePoint Server** deployment (2013, 2016, 2019, or Subscription Edition) over its +REST/OData API (and, for 2013, its XML/Atom API), and registers them in the |Fess| index. + +This feature requires the ``fess-ds-sharepoint`` plugin. + +.. note:: + + If you need to crawl SharePoint Online (Microsoft 365) instead, use + :doc:`ds-microsoft365`, not this connector. This connector's OAuth support targets Azure ACS + application-only authentication only, and it has no Microsoft Graph API integration. + +Supported versions: SharePoint Server 2013 / 2016 / 2019 / Subscription Edition (SE) + +Supported Content +================= + +- Document library files +- List items +- List item attachments + +Prerequisites +============= + +1. Plugin installation is required +2. The crawl account needs read access to the sites, lists, and document libraries being crawled +3. Choose exactly one authentication method - NTLM, Kerberos (SPNEGO), or OAuth (ACS) - and have + its credentials ready + +Installing the Plugin +--------------------- + +Install it from the admin console under "System" -> "Plugin": + +1. Download ``fess-ds-sharepoint-X.X.X.jar`` +2. Place it under ``$FESS_HOME/app/WEB-INF/lib`` (or ``/usr/share/fess/app/WEB-INF/lib``) +3. Restart |Fess| + +See :doc:`../../admin/plugin-guide` for details. + +Configuration +============= + +Configure this connector in the admin console under "Crawler" -> "Data Store" -> "Create New". + +Basic Settings +-------------- + +.. list-table:: + :header-rows: 1 + :widths: 25 75 + + * - Item + - Example + * - Name + - SharePoint + * - Handler Name + - SharePointDataStore + * - Enabled + - On + +Parameter Configuration +----------------------- + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.doclib_path=/Shared Documents + +Parameter List +~~~~~~~~~~~~~~ + +**URL / Site** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Parameter + - Required + - Description + * - ``url`` + - Yes + - SharePoint server base URL, e.g. ``http://sharepoint.example.com/`` + * - ``site.name`` + - Conditional + - Site collection name crawled under ``/sites//``. Not needed if ``site.path`` is set + * - ``site.path`` + - No + - Server-relative managed path of the site (e.g. ``/teams/eng``; use ``/`` for the root site + collection). When set, it is used verbatim instead of the hardcoded ``/sites/`` prefix, and + ``site.name`` is no longer required + * - ``site.list_id`` + - No + - Crawl a single list by GUID (List Crawl mode) + * - ``site.list_name`` + - No + - Crawl a single list by display name (List Crawl mode) + * - ``site.doclib_path`` + - No + - Document-library path under the site (Document Library Crawl mode), e.g. ``/Shared Documents`` + * - ``site.exclude_list`` + - No + - Comma-separated regex patterns of list entity-type names to exclude. Only applies to a + whole-site crawl + * - ``site.exclude_folder`` + - No + - Comma-separated regex patterns of top-level folder titles to exclude. Only applies to a + whole-site crawl + * - ``site.crawl_subsites`` + - No + - Recurse into the site's subsites (default: ``false``). See `Subsites and Managed Paths`_ + * - ``site.max_depth`` + - No + - How many subsite hops ``site.crawl_subsites`` may recurse (default: ``10``); the root is depth 0 + +**Authentication** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Parameter + - Required + - Description + * - ``auth.ntlm.user`` + - No + - NTLM username. Setting it enables NTLM (``DOMAIN\user`` works) + * - ``auth.ntlm.password`` + - No + - NTLM password + * - ``auth.ntlm.domain`` + - No + - Windows domain, sent as its own NTLM field + * - ``auth.ntlm.workstation`` + - No + - Workstation name sent in the NTLM negotiation + * - ``auth.kerberos.principal`` + - No + - Client principal, written as ``user@REALM``. Setting it enables Kerberos/SPNEGO + * - ``auth.kerberos.keytab`` + - No + - Path to a keytab holding a key for the principal. Mutually exclusive with + ``auth.kerberos.password`` + * - ``auth.kerberos.password`` + - No + - The principal's password, used only when no keytab is set + * - ``auth.kerberos.strip_port`` + - No + - Strip the port from the service principal name (default: ``true``) + * - ``auth.kerberos.use_canonical_hostname`` + - No + - Resolve the target host to its canonical name before building the service principal name + (default: ``false``) + * - ``auth.kerberos.krb5_conf`` + - No + - Path to a ``krb5.conf``. Applied only when ``java.security.krb5.conf`` is not already set + * - ``auth.kerberos.debug`` + - No + - Enable ``Krb5LoginModule`` debug output (default: ``false``) + * - ``auth.oauth.client_id`` + - No + - Azure ACS application-only OAuth client ID. Setting it enables OAuth + * - ``auth.oauth.client_secret`` + - No + - OAuth client secret + * - ``auth.oauth.tenant`` + - No + - Tenant name, without ``.sharepoint.com`` + * - ``auth.oauth.realm`` + - No + - Azure AD realm/directory ID + +**Exactly one** of ``auth.kerberos.principal``, ``auth.ntlm.user``, and ``auth.oauth.client_id`` +may be set. See `Authentication`_ below. + +**List** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Parameter + - Required + - Description + * - ``list.items.number_per_page`` + - No + - Page size for ``GetListItems`` (default: ``100``) + * - ``list.item.content.include_fields`` + - No + - Comma-separated field names; if set, only these list-item fields are concatenated into + ``content`` + * - ``list.item.content.exclude_fields`` + - No + - Comma-separated field-name patterns (each treated as a regex), excluded from ``content`` in + addition to a large built-in set of standard fields + * - ``list.is_sub_page`` + - No + - Treat list items as SitePages/wiki subpages, affecting paging fallback and the web-link shape + (default: ``false``) + +**HTTP** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Parameter + - Required + - Description + * - ``http.connection_timeout`` + - No + - HTTP connect timeout in ms; also used as the connection-pool wait timeout (default: ``30000``) + * - ``http.socket_timeout`` + - No + - HTTP socket (read) timeout in ms (default: ``30000``) + * - ``proxy_host`` + - No + - HTTP proxy host + * - ``proxy_port`` + - Conditional + - HTTP proxy port; required if ``proxy_host`` is set (default: ``-1`` = no proxy) + +**Filtering & Content** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Parameter + - Required + - Description + * - ``include_pattern`` + - No + - Regex an item's value must match to be crawled. See the note under this table for what + value that is + * - ``exclude_pattern`` + - No + - Regex that excludes a matching item from being crawled + * - ``supported_mimetypes`` + - No + - Comma-separated regexes a file's MIME type must match at least one of (default: ``.*``) + * - ``max_content_length`` + - No + - Maximum file size in bytes; an over-limit file is skipped, not failed (default: ``-1`` = no + limit) + * - ``extractor_name`` + - No + - Fallback extractor used only for a MIME type the extractor factory does not map + (default: ``tikaExtractor``) + +**Behaviour** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Parameter + - Required + - Description + * - ``sp.version`` + - No + - Set to ``2013`` to switch to the XML/Atom, ``GetXxxByServerRelativeUrl`` API family for + SharePoint 2013 (unset ⇒ SharePoint Online / 2016+ REST dialect) + * - ``retry_limit`` + - No + - Max retries per crawl unit on a SharePoint server/client exception (default: ``2``) + * - ``role.skip`` + - No + - Skip fetching per-item permissions entirely (default: ``false``). See `Permissions`_ + * - ``ignore_error`` + - No + - Log and skip a file's content-extraction failure instead of failing the crawl target + (default: ``false``) + * - ``default_permissions`` + - No + - Comma-separated permission strings merged into every document's role list in addition to + whatever SharePoint returned + * - ``delete_old_docs`` + - No + - Whether documents not refreshed this run are deleted (core default: ``true``). This plugin + forces it to ``false`` for the current run whenever any crawl target failed + * - ``number_of_threads`` + - No + - How many crawl targets are worked on at once (default: ``1`` = no thread pool), capped at + twice the processor count. See `Parallel Crawling and Load`_ + * - ``script_type`` + - No + - Script engine for the data-config Script (default: ``groovy``) + * - ``readInterval`` + - No + - Sleep between successive crawl results, in ms (default: ``0``). Note the camelCase spelling, + unlike every other parameter here + +Script Configuration +-------------------- + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + role=role + +Available Fields +~~~~~~~~~~~~~~~~ + +.. list-table:: + :header-rows: 1 + :widths: 16 20 32 32 + + * - Key + - List item (ItemCrawl) + - Doclib file (FolderCrawl->FileCrawl) + - Attachment (ItemAttachmentsCrawl->FileCrawl) + * - ``url`` + - Web link + - File URL + - File URL + * - ``host`` + - Hostname + - Hostname + - Hostname + * - ``site`` + - Server-relative path (``FileRef``) + - Server-relative path + - Server-relative path + * - ``title`` + - ``Title`` field, else ``FileLeafRef``/filename + - The doclib file's own ``Title`` list value if present, else filename + - Filename + * - ``titleWithListName`` + - ``"[listName] title"`` + - ``"[listName] filename"`` (list name is always empty for a doclib crawl, so effectively just + the filename) + - ``"[listName] filename"`` + * - ``listName`` + - List display name, or ``""`` + - Always ``""`` + - Actual list name + * - ``content`` + - Concatenation of field values + - Extracted text + - Extracted text + * - ``digest`` + - Abbreviated ``content`` + - Abbreviated ``content`` + - Abbreviated ``content`` + * - ``content_length`` + - ``content.length()`` + - ``content.length()`` + - ``content.length()`` + * - ``last_modified`` + - From the listing + - From the listing + - From the listing + * - ``created`` + - From the listing + - From the listing + - From the listing + * - ``mimetype`` + - Always ``text/html`` + - Detected + - Detected + * - ``filetype`` + - Derived from ``mimetype`` + - Derived from ``mimetype`` + - Derived from ``mimetype`` + * - ``role`` + - Permission list, only if non-empty + - Permission list, only if non-empty + - Permission list, only if non-empty + * - ``list_name`` + - Present + - **Absent** + - Present + * - ``list_id`` + - Present + - **Absent** + - Present + * - ``item_id`` + - Present + - **Absent** + - Present + +.. note:: + + ``content_length`` is ``content.length()`` - the character count (UTF-16 code units) of the + extracted or concatenated text, not the file's byte size. This differs from ``file.size`` in + the Box, Google Drive, and Dropbox connectors, which is the actual byte size from each + service's own file metadata. Do not compare this connector's ``content_length`` against those. + +**Dynamic keys: ``val_*``** + +Every key of a list item's ``FieldValuesAsText`` (the raw field-value map SharePoint returns for +that item, including OData metadata keys such as ``odata.metadata``) is exposed under two names: +once unprefixed (only if that name is not already one of the fixed keys above), and once with a +``val_`` prefix, unconditionally - e.g. a ``Status`` field becomes both ``Status`` and +``val_Status``. + +``val_*`` keys exist only on the **list-item crawl path (ItemCrawl)**. A document-library file +(FolderCrawl->FileCrawl) or a list-item attachment (ItemAttachmentsCrawl->FileCrawl) never +produces any ``val_*`` key. + +Authentication +============== + +Three authentication methods are available, and **exactly one may be configured**. Setting more +than one of ``auth.kerberos.principal``, ``auth.ntlm.user``, and ``auth.oauth.client_id`` fails +the data config job with a validation error before any request is made. This is deliberate: only +one credential is registered with the HTTP client, and the scope it is registered under matches a +``Negotiate`` challenge as readily as an ``NTLM`` one, so configuring more than one would otherwise +produce 401s that nothing in the log explains. + +NTLM +---- + +:: + + auth.ntlm.user={SharePoint username} + auth.ntlm.password={Password} + auth.ntlm.domain={Windows domain. Optional; unset by default.} + auth.ntlm.workstation={Workstation name sent in the NTLM negotiation. Optional; unset by default.} + +``auth.ntlm.domain`` and ``auth.ntlm.workstation`` both default to unset, which builds exactly the +credential this connector has always built. Writing the domain into the username as +``DOMAIN\user`` keeps working. Setting ``auth.ntlm.domain`` sends the domain as its own NTLM field +instead, which is what a server that rejects the combined form wants. + +Kerberos (SPNEGO) +----------------- + +**Supported envelope:** a single crawler JVM, one ``krb5.conf`` per Fess instance, a keytab or a +password, no delegation, no channel binding, and mutually exclusive with NTLM and OAuth. Anything +outside that is not supported. + +:: + + auth.kerberos.principal={Client principal, written as user@REALM. Setting it enables Kerberos.} + auth.kerberos.keytab={Path to a keytab holding a key for the principal. Mutually exclusive with auth.kerberos.password.} + auth.kerberos.password={The principal's password. Used only when no keytab is set.} + auth.kerberos.strip_port={true or false. Strip the port from the service principal name. Default is true.} + auth.kerberos.use_canonical_hostname={true or false. Resolve the target host to its canonical name for the service principal name. Default is false.} + auth.kerberos.krb5_conf={Path to a krb5.conf. Applied only when java.security.krb5.conf is not already set.} + auth.kerberos.debug={true or false. Krb5LoginModule debug output. Default is false.} + +- **``krb5.conf`` belongs in ``jvm.crawler.options``**, as + ``-Djava.security.krb5.conf=/path/to/krb5.conf``. Data-store crawling runs in the crawler + **child process**, so setting this anywhere that only affects the webapp has no effect, and a + webapp restart does not pick up a change - the crawl job has to run again. ``auth.kerberos.krb5_conf`` + is a convenience for when nothing has set the property yet: it **never overwrites an + already-set value**, since the property is JVM-global and one crawler JVM runs every data + config in a crawl job. When it declines to overwrite, it logs a warning naming both paths. +- **Put ``udp_preference_limit = 1`` in ``krb5.conf``'s ``[libdefaults]``.** Without it, the JDK + tries UDP first, and when the KDC does not answer (unreachable, a firewall dropping UDP 88, or a + reply larger than the datagram size), it retries three times at thirty seconds each before + falling back to TCP. A crawl that looks hung for about a minute and a half per authentication, + with nothing in the log, is usually this. +- **Always write the principal as ``user@REALM``.** ``default_realm`` is JVM-global, and several + SharePoint farms in different realms may have to share one ``krb5.conf``, so a bare ``user`` + resolves against whichever realm that file happens to name. +- **``auth.kerberos.use_canonical_hostname`` defaults to ``false``**, deliberately unlike Apache + HttpClient's own default. With it on, the target host is put through reverse DNS before the + service principal name is built, which under alternate access mappings or behind a load + balancer can produce a name no SPN is registered for - and the resulting failure says nothing + about DNS. Turn it on only if the SPN really is registered against the canonical name. +- **IIS Extended Protection set to ``tokenChecking=Require`` cannot work.** Neither Apache + HttpClient 4.5 nor 5.x supports channel binding. IIS defaults this to ``None``, so it is usually + not hit, and there is no workaround when it is. +- **The ticket is obtained once, when the crawl's HTTP client is built, and is never renewed.** A + crawl that runs longer than the ticket lifetime starts failing to authenticate partway through. +- **``auth.kerberos.password`` is stored and displayed in clear text**, exactly like + ``auth.ntlm.password``. Fess has no masking mechanism for data-store handler parameters; the + data config edit screen renders them as a plain text area. Prefer ``auth.kerberos.keytab``, and + give the keytab file restrictive permissions. +- ``auth.kerberos.debug=true`` makes ``Krb5LoginModule`` write to the crawler process's standard + output, not to the Fess log. + +OAuth (ACS) +----------- + +:: + + auth.oauth.client_id={OAuth client ID} + auth.oauth.client_secret={OAuth client secret} + auth.oauth.tenant={Tenant name, without .sharepoint.com} + auth.oauth.realm={Azure AD realm/directory ID} + +Setting ``auth.oauth.client_id`` enables a client-credentials (app-only) flow against the Windows +Azure Access Control Service, ``https://accounts.accesscontrol.windows.net/{realm}/tokens/OAuth/2``. +The access token is fetched once, when the crawl's HTTP client is built, applied as a ``Bearer`` +``Authorization`` header on every request, and refreshed and retried once on a 401. +**Microsoft has deprecated ACS and scheduled it for retirement**; this connector logs a warning to +that effect on every OAuth-configured crawl. There is no Entra ID app-registration (certificate or +client-secret) flow implemented here - only legacy ACS app-only auth. + +Only ``auth.oauth.client_id``'s presence is checked before OAuth is wired up; ``client_secret``, +``tenant``, and ``realm`` are read unconditionally and can silently be blank if omitted, which +breaks token acquisition with no dedicated validation message. + +**``sp.version=2013`` and OAuth have never worked together.** Every SharePoint 2013 API call this +connector makes goes through the XML/Atom client, and no code path in that client attaches an +OAuth token to a request - so with both set, every request is sent unauthenticated. The crawl logs +a warning saying exactly this and naming ``auth.ntlm.*`` as the alternative; it does not fail the +job. Use ``auth.ntlm.*`` for SharePoint 2013. + +Permissions +=========== + +``role.skip=true`` (default ``false``) skips fetching per-item permissions entirely: no +``GetListItemRole`` call is made, no ``role`` key is ever set for the item, and the document ends +up carrying only the data config's static Permission setting and, if configured, +``default_permissions`` - no SharePoint-derived permission reaches it at all. + +When roles are fetched, SharePoint's own users, security groups, and SharePoint groups are +expanded and mapped to Fess search roles: + +- An **on-premises AD** account or group (login name containing a backslash, not starting with an + Azure claim prefix) is mapped via the standard AD user/group role helpers. +- An **Azure AD (Entra ID)** account (login name starting with ``i:0#.f|membership|``) is mapped + **twice** - once by its full Azure claim value, once by the AD-account portion before ``@`` in + that claim - so both an Entra-ID-style and an AD-style role are added for the same user. A + security group flagged as Azure (by one of several claim-style prefixes, including the special + ``spo-grid-all-users`` "everyone" group) is mapped the same way, under both forms. +- A **SharePoint group** has its own membership (users, security groups, nested groups) expanded + recursively, with a visited-group guard to stop infinite recursion between groups that contain + each other. + +``default_permissions`` (comma-separated) is merged in **after** all of the above, and applies +even when SharePoint returned no role for the item at all - the case both ``role.skip=true`` and +"SharePoint returned nothing" produce. The final role list is the union of the data config's +static Permission setting, the SharePoint-derived roles (unless skipped), and +``default_permissions``, de-duplicated. + +Subsites and Managed Paths +========================== + +Setting ``site.path`` uses the given server-relative managed path verbatim instead of the +hardcoded ``/sites/`` prefix, and ``site.name`` is no longer required. + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Scenario + - Setting + * - Root site collection + - ``site.path=/`` + * - The ``/teams/eng`` site + - ``site.path=/teams/eng`` + * - The classic ``/sites/mysite/`` form + - ``site.name=mysite`` (leave ``site.path`` unset) + +Setting ``site.crawl_subsites`` (default ``false``) makes a full site crawl - one where neither +``site.list_name`` nor ``site.doclib_path`` is set - recurse into the site's subsites, discovered +via ``_api/web/webinfos``. Leaving it unset keeps the crawl issuing exactly the same requests it +always has, including never requesting ``webinfos`` at all. + +A subsite's documents land in the same data config as the root site's, under their own +server-relative paths - there is nothing in the index that marks a document as having come from a +subsite rather than the root. + +``site.max_depth`` (default ``10``) bounds how many subsite hops below the root site are crawled +once ``site.crawl_subsites=true``. The root site itself is depth 0, so ``site.max_depth=1`` crawls +the root's direct children and no further. Setting it below ``1`` while +``site.crawl_subsites=true`` turns the feature back off - no subsite is crawled at all - and is +logged as a warning when the crawl starts. + +Turning subsite crawling on **multiplies the crawl's total time** by roughly the number of +subsites discovered (bounded by ``site.max_depth``): each one gets its own full folder listing, +list listing, and (if not at the depth bound) its own ``webinfos`` call, on top of everything the +root site's crawl already does. + +``number_of_threads`` and ``readInterval``, described in `Parallel Crawling and Load`_, apply to a +subsite-recursive crawl the same way they apply to any other crawl. + +Parallel Crawling and Load +========================== + +``number_of_threads`` (default ``1``) is how many crawl targets are worked on at once. At the +default, the crawl runs exactly as it always has: every target is crawled on the crawling thread +and **no thread pool is created at all**. + +The value is **capped at twice the processor count** of the machine running Fess, so a data config +cannot ask for more concurrency than the host can serve. A value below ``1`` - or a blank or +unparseable one - falls back to ``1`` rather than being honoured or failing the job. A value that +was capped, or one below ``1``, is logged with both the requested and the actual value; an +unparseable one logs a warning. A blank value logs nothing, because a blank field means the +parameter was simply not set. + +The HTTP connection pool is sized to match. Apache HttpClient allows only 2 connections per route +by default, and a whole crawl is a single route: without raising it, every thread past the second +would spend the crawl waiting for a connection rather than making requests. + +**``readInterval`` still paces document hand-off, one document per interval, whatever it is set +to.** Threads make the crawl discover and fetch faster; they do not make documents reach the +indexer faster. That is deliberate: dividing an operator's configured interval by the thread count +would multiply exactly the load they configured that interval to limit. A worker that finishes a +document while the previous ones are still being handed over simply waits. + +What raising ``number_of_threads`` **does** multiply is the request rate against SharePoint. The +503 backoff and the ``X-SharePointHealthScore`` wait described below are applied per crawl target, +on the thread crawling it, so ``n`` threads make up to ``n`` times the requests a single-threaded +crawl makes - including during a period the farm is signalling that it is busy. On an on-premises +farm, raise this gradually. + +Two things put a ceiling on what more threads actually buy: + +- **The first time each SharePoint group's membership is read, it is read by one thread at a + time.** Permissions are resolved through a cache shared by the whole crawl, guarded by a single + lock held across a group's member lookups. That lock stops one thread from handing another a + group whose members are still being read, which would index the items that group protects with + none of its permissions. Once a group is cached, every later reference to it is a cheap lookup, + so this is a **cold-cache cost**: a crawl of a site with many distinct groups spends its early + minutes closer to single-threaded than to ``n`` threads, while one whose items share a handful + of groups barely notices. ``role.skip=true``, which does not read permissions at all, avoids it + entirely. +- Discovery is sequential per site: a site's folder and list listings are one crawl target, so + threads have nothing to share out until that target has finished and queued what it found. + +**A 503 response** is retried the same as any other error, up to ``retry_limit``, but with an +increasing wait before each retry: 2 seconds, then 4, then 8, doubling up to a 30-second cap, each +randomized to 70-129% of that value. A crawl target that keeps returning 503 pays this wait before +every retry it actually gets, but not after its last one. + +**Every response** - successful or not, including a page of a listing the crawl is about to +discard - is inspected for the ``X-SharePointHealthScore`` response header (0 idle to 10 very +busy). A score of 9 or above makes the crawl wait before doing anything else: score 9 waits about +2 seconds, score 10 about 4 seconds, and so on, doubling for each point past 9. **This adds up +across the whole crawl, with no aggregate cap**: a farm sitting at health score 9 under sustained +load adds roughly 2 seconds to *every single request* this connector makes - including every page +of every folder and list listing - which can turn a crawl that would otherwise take hours into one +that takes substantially longer. If a crawl unexpectedly slows down by an order of magnitude, +check the farm's health score during that window before assuming something else is wrong. + +Configuration Examples +====================== + +All of these assume NTLM. To use Kerberos or OAuth instead, see `Authentication`_ and replace the +``auth.ntlm.*`` lines. + +List Crawl +---------- + +Parameter: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.list_name=Tasks + +Script: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +Document Library Crawl +---------------------- + +Parameter: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.doclib_path=/Shared Documents + +Script: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +Crawling a ``/teams/`` Site +--------------------------- + +``site.path`` lets you point directly at a document library on a site under a managed path other +than ``/sites/``. + +Parameter: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.path=/teams/eng + site.doclib_path=/Shared Documents + +Script: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +Recursive Subsite Crawl +----------------------- + +Starts at the root site collection and follows subsites up to 3 levels deep. + +Parameter: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.path=/ + site.crawl_subsites=true + site.max_depth=3 + +Script: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + role=role + +Limitations +=========== + +- **No incremental or delta crawl of any kind.** There is no change-token, delta-query, or + "last modified since" filtering anywhere in this connector - every run does a full listing of + every list, folder, and file it is configured to reach. ``delete_old_docs`` only controls + whether documents the current full crawl did not see again are deleted afterwards; that is + post-hoc cleanup, not incremental fetching. +- **``%`` and ``#`` in file/folder names** are supported on the default (non-``2013``) code path. + Only SharePoint Server 2019 and Subscription Edition accept those two characters in a name at + all; 2016 explicitly still rejects them, and so does 2013. The default code path reaches such a + file through the ``...ByServerRelativePath(decodedUrl=...)`` endpoints, which take the decoded + path, and the crawl escapes both characters in the link it indexes the file under. + **``sp.version=2013`` cannot reach such a file**, because it uses the older + ``...ByServerRelativeUrl(...)`` endpoints, which read their argument as an already-encoded URL. + That is a deliberate limit rather than a gap - a SharePoint 2013 farm cannot hold such a name in + the first place - so it only matters if ``sp.version=2013`` is pointed at a 2019 or Subscription + Edition server, which is not a configuration to use. See + `Use of # and % characters in file and folder names + `__ + and `File names - expanded support for special characters + `__. +- **IIS Extended Protection ``tokenChecking=Require`` cannot be supported.** Neither Apache + HttpClient 4.5 nor 5.x implements channel binding, which Extended Protection at ``Require`` + depends on. IIS defaults this setting to ``None``, so most farms are unaffected, and there is no + workaround for a farm where it is set to ``Require``. +- **Passwords in data-config parameters are stored and displayed in clear text.** This applies to + ``auth.ntlm.password`` and ``auth.kerberos.password`` alike: Fess has no masking mechanism for + data-store handler parameters, and the data config edit screen renders them in a plain text + area. Prefer ``auth.kerberos.keytab`` over ``auth.kerberos.password`` where Kerberos is + available, and give the keytab file restrictive permissions. +- **``sp.version=2013`` and OAuth have never worked together.** Every SharePoint 2013 API call + goes through the XML/Atom client, and no code path in that client attaches an OAuth token to a + request, so with both set every request is sent unauthenticated. Use ``auth.ntlm.*`` for + SharePoint 2013. +- **Managed paths other than ``/sites/`` and the one set via ``site.path`` are still not + discovered on their own.** ``site.crawl_subsites`` recurses only from the root site you + configure, and ``site.path`` reaches exactly the one managed path you set, not every managed + path on the farm. + +Troubleshooting +=============== + +Authentication Fails Silently +----------------------------- + +**Symptom**: requests come back 401 (or similar) with nothing clear in the log to explain why + +**Checklist**: + +1. Check whether more than one of ``auth.kerberos.principal``, ``auth.ntlm.user``, and + ``auth.oauth.client_id`` is set - two or more fails the job with a validation error before the + crawl starts +2. For Kerberos, confirm ``-Djava.security.krb5.conf=...`` is set in ``jvm.crawler.options``. + Setting it anywhere that only affects the webapp has no effect. After changing it, re-run the + crawl job - restarting the webapp does not pick it up +3. For Kerberos, confirm ``udp_preference_limit = 1`` is set in ``krb5.conf``'s + ``[libdefaults]``. Without it, an unresponsive KDC can make each authentication hang for about + 90 seconds (three 30-second UDP retries) with nothing in the log +4. Confirm the principal is written as ``user@REALM`` - a bare ``user`` resolves against + whatever ``default_realm`` the shared ``krb5.conf`` happens to name +5. For OAuth, confirm ``client_secret``, ``tenant``, and ``realm`` are not blank - only + ``client_id``'s presence is validated, so the others can be silently empty +6. Confirm IIS Extended Protection is not set to ``tokenChecking=Require`` - there is no + workaround for that setting +7. For a long-running crawl, check whether it started failing only partway through - the + Kerberos ticket is obtained once at HTTP client build time and is never renewed, so a crawl + that outlives the ticket starts failing partway through + +The Crawl Is Slow (503s and the Health Score) +--------------------------------------------- + +**Symptom**: the crawl takes far longer than expected, or times out + +**Checklist**: + +1. Check the SharePoint farm's ``X-SharePointHealthScore`` during the slow window. A score of 9 + or above adds a wait before every request (about 2 seconds at 9, about 4 at 10, doubling from + there, with no aggregate cap), which can turn a crawl that should take hours into one that + takes far longer +2. Check for repeated 503 responses. A 503 is retried up to ``retry_limit`` times, waiting + 2, then 4, then 8 seconds (capped at 30) before each retry +3. Check whether ``number_of_threads`` has been raised too far. More threads mean roughly + proportionally more requests against SharePoint, which can push the health score higher. Raise + it gradually on an on-premises farm +4. If ``site.crawl_subsites=true``, remember that total crawl time grows roughly with the number + of subsites discovered - consider narrowing the scope with ``site.max_depth`` + +Nothing Gets Indexed +-------------------- + +**Symptom**: the crawl finishes normally, but search returns zero results + +**Checklist**: + +1. Check the crawler log for errors or warnings (set ``org.codelibs.fess.ds`` to ``DEBUG`` in + ``app/WEB-INF/env/crawler/resources/log4j2.xml``) +2. Check ``url``, ``site.name`` (or ``site.path``), and ``site.list_name`` for typos - remember + that ``site.name`` is not needed once ``site.path`` is set +3. Confirm authentication is actually succeeding (no 401s) - a request that never authenticates + is a far more common cause than a misconfigured ``role.skip`` or ``default_permissions`` +4. If ``include_pattern`` or ``exclude_pattern`` is set, remember these match a server-relative + path (for a document-library file or a list-item attachment) or the ``FileRef`` (for a list + item) - not the URL shown in search results. Check for a pattern written for a full URL +5. Check whether ``supported_mimetypes`` or ``max_content_length`` is excluding the files you + expect to see +6. Check whether ``site.exclude_list`` or ``site.exclude_folder`` is unintentionally excluding + the target + +Reference Information +===================== + +- :doc:`ds-overview` - Data Store Connector Overview +- :doc:`ds-microsoft365` - Microsoft 365 Connector (for SharePoint Online) +- :doc:`../../admin/dataconfig-guide` - Data Store Configuration Guide +- :doc:`../../admin/plugin-guide` - Plugin Management Guide diff --git a/en/15.9/config/datastore/index.rst b/en/15.9/config/datastore/index.rst index b30d0530..9afb5a2e 100644 --- a/en/15.9/config/datastore/index.rst +++ b/en/15.9/config/datastore/index.rst @@ -25,6 +25,7 @@ Data Store Connector Guide ds-atlassian ds-slack + ds-sharepoint .. toctree:: :maxdepth: 2 diff --git a/es/15.9/config/datastore/ds-overview.rst b/es/15.9/config/datastore/ds-overview.rst index 965b91a7..9db41987 100644 --- a/es/15.9/config/datastore/ds-overview.rst +++ b/es/15.9/config/datastore/ds-overview.rst @@ -60,6 +60,9 @@ Herramientas de Colaboración * - :doc:`ds-slack` - fess-ds-slack - Rastrea mensajes y archivos de Slack + * - :doc:`ds-sharepoint` + - fess-ds-sharepoint + - Rastrea sitios de SharePoint Server on-premises Herramientas de Desarrollo y Operaciones ---------------------------------------- diff --git a/es/15.9/config/datastore/ds-sharepoint.rst b/es/15.9/config/datastore/ds-sharepoint.rst new file mode 100644 index 00000000..9824c983 --- /dev/null +++ b/es/15.9/config/datastore/ds-sharepoint.rst @@ -0,0 +1,892 @@ +============================= +Conector de SharePoint Server +============================= + +Descripción general +=================== + +El conector de SharePoint Server obtiene los archivos de bibliotecas de documentos y los elementos +de lista de una implementación local (on-premises) de **SharePoint Server** (2013, 2016, 2019 o +Subscription Edition) a través de su API REST/OData (y, para 2013, de su API XML/Atom), y los +registra en el índice de |Fess|. + +Esta funcionalidad requiere el plugin ``fess-ds-sharepoint``. + +.. note:: + + Si necesita rastrear SharePoint Online (Microsoft 365), use :doc:`ds-microsoft365` en lugar de + este conector. La compatibilidad OAuth de este conector se limita a la autenticación exclusiva de + aplicación (application-only) de Azure ACS, y no tiene integración con la API de Microsoft Graph. + +Versiones compatibles: SharePoint Server 2013 / 2016 / 2019 / Subscription Edition (SE) + +Contenido compatible +==================== + +- Archivos de bibliotecas de documentos +- Elementos de lista +- Archivos adjuntos de elementos de lista + +Requisitos previos +================== + +1. Se requiere la instalación del plugin +2. La cuenta de rastreo necesita acceso de lectura a los sitios, las listas y las bibliotecas de + documentos que se van a rastrear +3. Elija exactamente un método de autenticación (NTLM, Kerberos [SPNEGO] u OAuth [ACS]) y tenga + preparadas sus credenciales correspondientes + +Instalación del plugin +---------------------- + +Instálelo desde la pantalla de administración en "Sistema" -> "Plugin": + +1. Descargue ``fess-ds-sharepoint-X.X.X.jar`` +2. Colóquelo en ``$FESS_HOME/app/WEB-INF/lib`` (o en ``/usr/share/fess/app/WEB-INF/lib``) +3. Reinicie |Fess| + +Consulte :doc:`../../admin/plugin-guide` para más información. + +Configuración +============= + +Configure este conector desde la pantalla de administración en "Rastreador" -> "Almacén de datos" -> +"Crear nuevo". + +Configuración básica +-------------------- + +.. list-table:: + :header-rows: 1 + :widths: 25 75 + + * - Elemento + - Ejemplo + * - Nombre + - SharePoint + * - Nombre del manejador + - SharePointDataStore + * - Habilitado + - Activado + +Configuración de parámetros +--------------------------- + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.doclib_path=/Shared Documents + +Lista de parámetros +~~~~~~~~~~~~~~~~~~~ + +**URL / Sitio** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Parámetro + - Requerido + - Descripción + * - ``url`` + - Sí + - URL base del servidor SharePoint, p. ej., ``http://sharepoint.example.com/`` + * - ``site.name`` + - Condicional + - Nombre de la colección de sitios que se rastrea bajo ``/sites//``. No es necesario si se configura ``site.path`` + * - ``site.path`` + - No + - Ruta administrada relativa al servidor del sitio (p. ej., ``/teams/eng``; use ``/`` para la colección de sitios raíz). Cuando se configura, se usa tal cual en lugar del prefijo fijo ``/sites/``, y ``site.name`` deja de ser necesario + * - ``site.list_id`` + - No + - Rastrea una única lista mediante su GUID (modo Crawl de lista) + * - ``site.list_name`` + - No + - Rastrea una única lista mediante su nombre visible (modo Crawl de lista) + * - ``site.doclib_path`` + - No + - Ruta de la biblioteca de documentos dentro del sitio (modo Crawl de biblioteca de documentos), p. ej., ``/Shared Documents`` + * - ``site.exclude_list`` + - No + - Patrones regex (separados por comas) de nombres de tipo de entidad de lista que se excluirán. Solo se aplica a un rastreo de todo el sitio + * - ``site.exclude_folder`` + - No + - Patrones regex (separados por comas) de títulos de carpetas de nivel superior que se excluirán. Solo se aplica a un rastreo de todo el sitio + * - ``site.crawl_subsites`` + - No + - Recorre recursivamente los subsitios del sitio (predeterminado: ``false``). Consulte `Subsitios y rutas administradas`_ + * - ``site.max_depth`` + - No + - Cuántos saltos de subsitio puede recorrer ``site.crawl_subsites`` (predeterminado: ``10``); la raíz tiene profundidad 0 + +**Autenticación** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Parámetro + - Requerido + - Descripción + * - ``auth.ntlm.user`` + - No + - Nombre de usuario NTLM. Al configurarlo se habilita NTLM (funciona el formato ``DOMAIN\user``) + * - ``auth.ntlm.password`` + - No + - Contraseña NTLM + * - ``auth.ntlm.domain`` + - No + - Dominio de Windows, enviado como campo NTLM independiente + * - ``auth.ntlm.workstation`` + - No + - Nombre de estación de trabajo enviado en la negociación NTLM + * - ``auth.kerberos.principal`` + - No + - Principal del cliente, escrito como ``user@REALM``. Al configurarlo se habilita Kerberos/SPNEGO + * - ``auth.kerberos.keytab`` + - No + - Ruta a un archivo keytab que contiene una clave para el principal. Es mutuamente excluyente con ``auth.kerberos.password`` + * - ``auth.kerberos.password`` + - No + - La contraseña del principal, usada solo cuando no se configura un keytab + * - ``auth.kerberos.strip_port`` + - No + - Elimina el puerto del nombre principal de servicio (predeterminado: ``true``) + * - ``auth.kerberos.use_canonical_hostname`` + - No + - Resuelve el host de destino a su nombre canónico antes de construir el nombre principal de servicio (predeterminado: ``false``) + * - ``auth.kerberos.krb5_conf`` + - No + - Ruta a un ``krb5.conf``. Solo se aplica cuando ``java.security.krb5.conf`` aún no está configurado + * - ``auth.kerberos.debug`` + - No + - Habilita la salida de depuración de ``Krb5LoginModule`` (predeterminado: ``false``) + * - ``auth.oauth.client_id`` + - No + - ID de cliente OAuth de aplicación exclusiva de Azure ACS. Al configurarlo se habilita OAuth + * - ``auth.oauth.client_secret`` + - No + - Secreto de cliente OAuth + * - ``auth.oauth.tenant`` + - No + - Nombre del tenant, sin ``.sharepoint.com`` + * - ``auth.oauth.realm`` + - No + - Realm/ID de directorio de Azure AD + +**Solo se puede configurar uno** de ``auth.kerberos.principal``, ``auth.ntlm.user`` y +``auth.oauth.client_id``. Consulte `Autenticación`_ más abajo. + +**Lista** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Parámetro + - Requerido + - Descripción + * - ``list.items.number_per_page`` + - No + - Tamaño de página para ``GetListItems`` (predeterminado: ``100``) + * - ``list.item.content.include_fields`` + - No + - Nombres de campo separados por comas; si se configura, solo estos campos del elemento de lista se concatenan en ``content`` + * - ``list.item.content.exclude_fields`` + - No + - Patrones de nombre de campo separados por comas (cada uno tratado como una expresión regular), excluidos de ``content`` además de un amplio conjunto integrado de campos estándar + * - ``list.is_sub_page`` + - No + - Trata los elementos de lista como subpáginas de SitePages/wiki, lo que afecta al mecanismo de reserva de paginación y al formato del enlace web (predeterminado: ``false``) + +**HTTP** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Parámetro + - Requerido + - Descripción + * - ``http.connection_timeout`` + - No + - Tiempo de espera de conexión HTTP en ms; también se usa como tiempo de espera del grupo de conexiones (predeterminado: ``30000``) + * - ``http.socket_timeout`` + - No + - Tiempo de espera de socket HTTP (lectura) en ms (predeterminado: ``30000``) + * - ``proxy_host`` + - No + - Host del proxy HTTP + * - ``proxy_port`` + - Condicional + - Puerto del proxy HTTP; obligatorio si se configura ``proxy_host`` (predeterminado: ``-1`` = sin proxy) + +**Filtrado y contenido** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Parámetro + - Requerido + - Descripción + * - ``include_pattern`` + - No + - Expresión regular que debe coincidir con el valor de un elemento para que se rastree. Consulte la nota bajo esta tabla para saber cuál es ese valor + * - ``exclude_pattern`` + - No + - Expresión regular que excluye del rastreo a un elemento que coincida + * - ``supported_mimetypes`` + - No + - Expresiones regulares separadas por comas; el tipo MIME de un archivo debe coincidir con al menos una de ellas (predeterminado: ``.*``) + * - ``max_content_length`` + - No + - Tamaño máximo de archivo en bytes; un archivo que supere el límite se omite, no falla (predeterminado: ``-1`` = sin límite) + * - ``extractor_name`` + - No + - Extractor de reserva usado solo para un tipo MIME que la fábrica de extractores no tiene asignado (predeterminado: ``tikaExtractor``) + +**Comportamiento** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Parámetro + - Requerido + - Descripción + * - ``sp.version`` + - No + - Establézcalo en ``2013`` para cambiar a la familia de API XML/Atom, ``GetXxxByServerRelativeUrl``, de SharePoint 2013 (sin configurar ⇒ dialecto REST de SharePoint Online / 2016 en adelante) + * - ``retry_limit`` + - No + - Número máximo de reintentos por unidad de rastreo ante una excepción de servidor/cliente de SharePoint (predeterminado: ``2``) + * - ``role.skip`` + - No + - Omite por completo la obtención de permisos por elemento (predeterminado: ``false``). Consulte `Permisos`_ + * - ``ignore_error`` + - No + - Registra en el log y omite un fallo de extracción de contenido de un archivo en lugar de hacer fallar el objetivo de rastreo (predeterminado: ``false``) + * - ``default_permissions`` + - No + - Cadenas de permisos separadas por comas que se combinan en la lista de roles de cada documento, además de lo que haya devuelto SharePoint + * - ``delete_old_docs`` + - No + - Indica si se eliminan los documentos que no se han actualizado en esta ejecución (predeterminado del núcleo: ``true``). Este plugin lo fuerza a ``false`` para la ejecución actual siempre que algún objetivo de rastreo haya fallado + * - ``number_of_threads`` + - No + - Cuántos objetivos de rastreo se procesan a la vez (predeterminado: ``1`` = sin grupo de hilos), con un límite de hasta el doble del número de procesadores. Consulte `Rastreo paralelo y carga`_ + * - ``script_type`` + - No + - Motor de scripts para el Script de la configuración de datos (predeterminado: ``groovy``) + * - ``readInterval`` + - No + - Espera entre resultados de rastreo sucesivos, en ms (predeterminado: ``0``). Tenga en cuenta que se escribe en camelCase, a diferencia de todos los demás parámetros de esta lista + +Configuración de scripts +------------------------ + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + role=role + +Campos disponibles +~~~~~~~~~~~~~~~~~~ + +.. list-table:: + :header-rows: 1 + :widths: 16 20 32 32 + + * - Clave + - Elemento de lista (ItemCrawl) + - Archivo de biblioteca de documentos (FolderCrawl->FileCrawl) + - Archivo adjunto (ItemAttachmentsCrawl->FileCrawl) + * - ``url`` + - Enlace web + - URL del archivo + - URL del archivo + * - ``host`` + - Nombre de host + - Nombre de host + - Nombre de host + * - ``site`` + - Ruta relativa al servidor (``FileRef``) + - Ruta relativa al servidor + - Ruta relativa al servidor + * - ``title`` + - Campo ``Title``; si no, ``FileLeafRef``/nombre de archivo + - El propio valor de lista ``Title`` del archivo de la biblioteca de documentos si existe; si no, el nombre de archivo + - Nombre de archivo + * - ``titleWithListName`` + - ``"[listName] title"`` + - ``"[listName] filename"`` (el nombre de lista siempre está vacío en un crawl de biblioteca de documentos, por lo que en la práctica es solo el nombre de archivo) + - ``"[listName] filename"`` + * - ``listName`` + - Nombre visible de la lista, o ``""`` + - Siempre ``""`` + - Nombre real de la lista + * - ``content`` + - Concatenación de valores de campo + - Texto extraído + - Texto extraído + * - ``digest`` + - ``content`` abreviado + - ``content`` abreviado + - ``content`` abreviado + * - ``content_length`` + - ``content.length()`` + - ``content.length()`` + - ``content.length()`` + * - ``last_modified`` + - Del listado + - Del listado + - Del listado + * - ``created`` + - Del listado + - Del listado + - Del listado + * - ``mimetype`` + - Siempre ``text/html`` + - Detectado + - Detectado + * - ``filetype`` + - Derivado de ``mimetype`` + - Derivado de ``mimetype`` + - Derivado de ``mimetype`` + * - ``role`` + - Lista de permisos, solo si no está vacía + - Lista de permisos, solo si no está vacía + - Lista de permisos, solo si no está vacía + * - ``list_name`` + - Presente + - **Ausente** + - Presente + * - ``list_id`` + - Presente + - **Ausente** + - Presente + * - ``item_id`` + - Presente + - **Ausente** + - Presente + +.. note:: + + ``content_length`` es ``content.length()``, es decir, el número de caracteres (unidades de código + UTF-16) del texto extraído o concatenado, no el tamaño en bytes del archivo. Esto difiere de + ``file.size`` en los conectores de Box, Google Drive y Dropbox, que es el tamaño en bytes real + obtenido de los propios metadatos de archivo de cada servicio. No compare el ``content_length`` + de este conector con esos valores. + +**Claves dinámicas: ``val_*``** + +Cada clave del ``FieldValuesAsText`` de un elemento de lista (el mapa de valores de campo en bruto +que SharePoint devuelve para ese elemento, incluidas las claves de metadatos OData como +``odata.metadata``) se expone con dos nombres: uno sin prefijo (solo si ese nombre no coincide ya +con alguna de las claves fijas anteriores) y otro, incondicionalmente, con el prefijo ``val_`` - por +ejemplo, un campo ``Status`` se convierte tanto en ``Status`` como en ``val_Status``. + +Las claves ``val_*`` solo existen en la **ruta de crawl de elementos de lista (ItemCrawl)**. Un +archivo de biblioteca de documentos (FolderCrawl->FileCrawl) o un archivo adjunto de elemento de +lista (ItemAttachmentsCrawl->FileCrawl) nunca produce ninguna clave ``val_*``. + +Autenticación +============= + +Hay disponibles tres métodos de autenticación, y **solo se puede configurar uno**. Si se configura +más de uno de ``auth.kerberos.principal``, ``auth.ntlm.user`` y ``auth.oauth.client_id``, el trabajo +de configuración de datos falla con un error de validación antes de que se realice ninguna +solicitud. Esto es intencional: solo se registra una credencial en el cliente HTTP, y el ámbito bajo +el que se registra coincide tanto con un desafío ``Negotiate`` como con uno ``NTLM``, por lo que +configurar más de una produciría errores 401 que el log no explicaría en absoluto. + +NTLM +---- + +:: + + auth.ntlm.user={nombre de usuario de SharePoint} + auth.ntlm.password={contraseña} + auth.ntlm.domain={dominio de Windows. Opcional; sin configurar de forma predeterminada.} + auth.ntlm.workstation={nombre de estación de trabajo enviado en la negociación NTLM. Opcional; sin configurar de forma predeterminada.} + +``auth.ntlm.domain`` y ``auth.ntlm.workstation`` no están configurados de forma predeterminada, lo +que construye exactamente la misma credencial que este conector siempre ha construido. Escribir el +dominio dentro del nombre de usuario como ``DOMAIN\user`` sigue funcionando. Al configurar +``auth.ntlm.domain``, el dominio se envía en su lugar como un campo NTLM independiente, que es lo +que necesita un servidor que rechace la forma combinada. + +Kerberos (SPNEGO) +----------------- + +**Entorno admitido:** una única JVM de rastreo, un ``krb5.conf`` por instancia de Fess, un keytab o +una contraseña, sin delegación, sin channel binding, y mutuamente excluyente con NTLM y OAuth. +Cualquier configuración fuera de esto no es compatible. + +:: + + auth.kerberos.principal={principal del cliente, escrito como user@REALM. Al configurarlo se habilita Kerberos.} + auth.kerberos.keytab={ruta a un keytab que contiene una clave para el principal. Mutuamente excluyente con auth.kerberos.password.} + auth.kerberos.password={la contraseña del principal. Se usa solo cuando no hay un keytab configurado.} + auth.kerberos.strip_port={true o false. Elimina el puerto del nombre principal de servicio. El valor predeterminado es true.} + auth.kerberos.use_canonical_hostname={true o false. Resuelve el host de destino a su nombre canónico para el nombre principal de servicio. El valor predeterminado es false.} + auth.kerberos.krb5_conf={ruta a un krb5.conf. Solo se aplica cuando java.security.krb5.conf aún no está configurado.} + auth.kerberos.debug={true o false. Salida de depuración de Krb5LoginModule. El valor predeterminado es false.} + +- **``krb5.conf`` se configura en ``jvm.crawler.options``**, como + ``-Djava.security.krb5.conf=/ruta/a/krb5.conf``. El rastreo de almacenes de datos se ejecuta en el + **proceso hijo** del rastreador, por lo que configurar esto en cualquier lugar que solo afecte a + la aplicación web no tiene ningún efecto, y reiniciar la aplicación web no recoge el cambio: hay + que volver a ejecutar el trabajo de rastreo. ``auth.kerberos.krb5_conf`` es una comodidad para + cuando aún nada ha configurado la propiedad: **nunca sobrescribe un valor ya configurado**, ya que + la propiedad es global a la JVM y una única JVM de rastreo ejecuta todas las configuraciones de + datos de un trabajo de rastreo. Cuando se abstiene de sobrescribir, registra una advertencia en el + log indicando ambas rutas. +- **Ponga ``udp_preference_limit = 1`` en la sección ``[libdefaults]`` de ``krb5.conf``.** Sin esto, + el JDK intenta primero UDP, y cuando el KDC no responde (inalcanzable, un firewall que descarta + UDP 88, o una respuesta mayor que el tamaño del datagrama), reintenta tres veces a treinta + segundos cada una antes de recurrir a TCP. Un rastreo que parece bloqueado durante aproximadamente + un minuto y medio por cada autenticación, sin nada en el log, suele deberse a esto. +- **Escriba siempre el principal como ``user@REALM``.** ``default_realm`` es global a la JVM, y + varias granjas de SharePoint en distintos realms pueden tener que compartir un mismo + ``krb5.conf``, de modo que un ``user`` sin realm se resuelve contra el realm que ese archivo + indique en ese momento. +- **``auth.kerberos.use_canonical_hostname`` es ``false`` de forma predeterminada**, deliberadamente + distinto del valor predeterminado propio de Apache HttpClient. Con esta opción activada, el host + de destino pasa por una resolución DNS inversa antes de construir el nombre principal de servicio, + lo que en mapeos de acceso alternativos o detrás de un balanceador de carga puede producir un + nombre para el que no hay ningún SPN registrado, y el fallo resultante no dice nada sobre el DNS. + Actívela solo si el SPN está realmente registrado con el nombre canónico. +- **IIS Extended Protection configurado como ``tokenChecking=Require`` no puede funcionar.** Ni + Apache HttpClient 4.5 ni 5.x admiten channel binding. IIS establece esto en ``None`` de forma + predeterminada, por lo que normalmente no se da el caso, y no hay ninguna solución alternativa + cuando sí se da. +- **El ticket se obtiene una sola vez, al construirse el cliente HTTP del rastreo, y nunca se + renueva.** Un rastreo que se ejecuta más tiempo que la vida útil del ticket empieza a fallar en la + autenticación a mitad de camino. +- **``auth.kerberos.password`` se almacena y se muestra en texto sin cifrar**, exactamente igual que + ``auth.ntlm.password``. Fess no cuenta con ningún mecanismo de enmascaramiento para los parámetros + de los manejadores de almacén de datos; la pantalla de edición de la configuración de datos los + muestra como un área de texto plano. Dé preferencia a ``auth.kerberos.keytab`` y asigne al archivo + keytab permisos restrictivos. +- ``auth.kerberos.debug=true`` hace que ``Krb5LoginModule`` escriba en la salida estándar del + proceso del rastreador, no en el log de Fess. + +OAuth (ACS) +----------- + +:: + + auth.oauth.client_id={ID de cliente OAuth} + auth.oauth.client_secret={secreto de cliente OAuth} + auth.oauth.tenant={nombre del tenant, sin .sharepoint.com} + auth.oauth.realm={realm/ID de directorio de Azure AD} + +Configurar ``auth.oauth.client_id`` habilita un flujo de credenciales de cliente (exclusivo de +aplicación) frente al Windows Azure Access Control Service, +``https://accounts.accesscontrol.windows.net/{realm}/tokens/OAuth/2``. El token de acceso se obtiene +una sola vez, al construirse el cliente HTTP del rastreo, se aplica como encabezado +``Authorization`` de tipo ``Bearer`` en cada solicitud, y se renueva y reintenta una vez ante un +401. **Microsoft ha declarado obsoleto ACS y ha programado su retirada**; este conector registra una +advertencia al respecto en cada rastreo configurado con OAuth. Aquí no está implementado ningún +flujo de registro de aplicación de Entra ID (por certificado o por secreto de cliente); solo la +autenticación heredada de aplicación exclusiva de ACS. + +Antes de activar OAuth solo se comprueba la presencia de ``auth.oauth.client_id``; +``client_secret``, ``tenant`` y ``realm`` se leen incondicionalmente y pueden quedar vacíos en +silencio si se omiten, lo que rompe la obtención del token sin ningún mensaje de validación +dedicado. + +**``sp.version=2013`` y OAuth nunca han funcionado juntos.** Todas las llamadas a la API de +SharePoint 2013 que hace este conector pasan por el cliente XML/Atom, y ninguna ruta de código de +ese cliente adjunta un token OAuth a una solicitud, de modo que, con ambos configurados, cada +solicitud se envía sin autenticar. El rastreo registra una advertencia que indica exactamente esto y +menciona ``auth.ntlm.*`` como alternativa; no hace fallar el trabajo. Use ``auth.ntlm.*`` para +SharePoint 2013. + +Permisos +======== + +``role.skip=true`` (predeterminado ``false``) omite por completo la obtención de permisos por +elemento: no se realiza ninguna llamada a ``GetListItemRole``, nunca se establece la clave ``role`` +para el elemento, y el documento termina llevando únicamente el ajuste estático de Permission de la +configuración de datos y, si está configurado, ``default_permissions``; ningún permiso derivado de +SharePoint llega a él en absoluto. + +Cuando se obtienen los roles, los propios usuarios, grupos de seguridad y grupos de SharePoint se +expanden y se asignan a roles de búsqueda de Fess: + +- Una cuenta o grupo de **AD local (on-premises)** (nombre de inicio de sesión que contiene una + barra invertida y no comienza con un prefijo de reclamación de Azure) se asigna mediante los + ayudantes de rol estándar de usuario/grupo de AD. +- Una cuenta de **Azure AD (Entra ID)** (nombre de inicio de sesión que comienza con + ``i:0#.f|membership|``) se asigna **dos veces**: una por el valor completo de la reclamación de + Azure y otra por la parte de cuenta de AD anterior al ``@`` de esa reclamación, de modo que se + añaden tanto un rol de estilo Entra ID como uno de estilo AD para el mismo usuario. Un grupo de + seguridad marcado como de Azure (mediante uno de varios prefijos de estilo reclamación, incluido + el grupo especial «todos» ``spo-grid-all-users``) se asigna de la misma manera, en ambas formas. +- Un **grupo de SharePoint** tiene su propia membresía (usuarios, grupos de seguridad, grupos + anidados) expandida de forma recursiva, con una protección de grupos visitados para detener la + recursión infinita entre grupos que se contienen mutuamente. + +``default_permissions`` (separado por comas) se combina **después** de todo lo anterior, y se aplica +incluso cuando SharePoint no devolvió ningún rol para el elemento, el caso que producen tanto +``role.skip=true`` como «SharePoint no devolvió nada». La lista final de roles es la unión, sin +duplicados, del ajuste estático de Permission de la configuración de datos, los roles derivados de +SharePoint (salvo que se omitan) y ``default_permissions``. + +Subsitios y rutas administradas +=============================== + +Configurar ``site.path`` hace que se use tal cual la ruta administrada relativa al servidor +indicada, en lugar del prefijo fijo ``/sites/``, y ``site.name`` deja de ser necesario. + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Escenario + - Configuración + * - Colección de sitios raíz + - ``site.path=/`` + * - El sitio ``/teams/eng`` + - ``site.path=/teams/eng`` + * - La forma clásica ``/sites/mysite/`` + - ``site.name=mysite`` (deje ``site.path`` sin configurar) + +Configurar ``site.crawl_subsites`` (predeterminado ``false``) hace que un rastreo de sitio completo +(aquel en el que no se configura ni ``site.list_name`` ni ``site.doclib_path``) recorra +recursivamente los subsitios del sitio, descubiertos mediante ``_api/web/webinfos``. Dejarlo sin +configurar mantiene el rastreo emitiendo exactamente las mismas solicitudes de siempre, incluyendo +no solicitar nunca ``webinfos``. + +Los documentos de un subsitio terminan en la misma configuración de datos que los del sitio raíz, +bajo sus propias rutas relativas al servidor; no hay nada en el índice que marque un documento como +procedente de un subsitio en lugar de la raíz. + +``site.max_depth`` (predeterminado ``10``) limita cuántos saltos de subsitio por debajo del sitio +raíz se rastrean una vez que ``site.crawl_subsites=true``. El propio sitio raíz tiene profundidad 0, +de modo que ``site.max_depth=1`` rastrea los hijos directos de la raíz y nada más. Configurarlo por +debajo de ``1`` mientras ``site.crawl_subsites=true`` desactiva de nuevo la función (no se rastrea +ningún subsitio en absoluto) y se registra como advertencia al iniciarse el rastreo. + +Activar el rastreo de subsitios **multiplica el tiempo total del rastreo** aproximadamente por el +número de subsitios descubiertos (limitado por ``site.max_depth``): cada uno recibe su propio +listado completo de carpetas, su propio listado de listas y, si no ha alcanzado el límite de +profundidad, su propia llamada a ``webinfos``, todo ello además de todo lo que ya hace el rastreo +del sitio raíz. + +``number_of_threads`` y ``readInterval``, descritos en `Rastreo paralelo y carga`_, se aplican a un +rastreo recursivo de subsitios de la misma manera que se aplican a cualquier otro rastreo. + +Rastreo paralelo y carga +======================== + +``number_of_threads`` (predeterminado ``1``) indica cuántos objetivos de rastreo se procesan a la +vez. Con el valor predeterminado, el rastreo se ejecuta exactamente como siempre: cada objetivo se +rastrea en el hilo de rastreo y **no se crea ningún grupo de hilos**. + +El valor está **limitado al doble del número de procesadores** de la máquina que ejecuta Fess, de +modo que una configuración de datos no puede pedir más concurrencia de la que el host puede ofrecer. +Un valor inferior a ``1``, o uno vacío o no interpretable, recurre a ``1`` en lugar de respetarse o +hacer fallar el trabajo. Un valor que se haya limitado, o uno inferior a ``1``, se registra con el +valor solicitado y el valor real; uno no interpretable registra una advertencia. Un valor vacío no +registra nada, porque un campo vacío significa simplemente que el parámetro no se configuró. + +El grupo de conexiones HTTP se dimensiona en consecuencia. Apache HttpClient permite de forma +predeterminada solo 2 conexiones por ruta, y todo el rastreo constituye una única ruta: sin aumentar +este límite, cada hilo a partir del segundo pasaría el rastreo esperando una conexión en lugar de +hacer solicitudes. + +**``readInterval`` sigue marcando el ritmo de entrega de documentos, uno por intervalo, sea cual sea +su valor.** Los hilos hacen que el rastreo descubra y obtenga contenido más rápido; no hacen que los +documentos lleguen al indexador más rápido. Esto es intencional: dividir el intervalo configurado +por el operador entre el número de hilos multiplicaría exactamente la carga que ese intervalo +pretendía limitar. Un trabajador que termina un documento mientras los anteriores aún se están +entregando simplemente espera. + +Lo que aumentar ``number_of_threads`` **sí** multiplica es la tasa de solicitudes contra SharePoint. +La espera exponencial ante 503 y la espera por ``X-SharePointHealthScore`` que se describen más +abajo se aplican por objetivo de rastreo, en el hilo que lo rastrea, de modo que ``n`` hilos hacen +hasta ``n`` veces las solicitudes que haría un rastreo de un solo hilo, incluso durante un período +en el que la granja esté señalando que está ocupada. En una granja local (on-premises), aumente este +valor gradualmente. + +Hay dos factores que ponen un techo a lo que más hilos realmente aportan: + +- **La primera vez que se lee la membresía de cada grupo de SharePoint, se lee con un solo hilo a la + vez.** Los permisos se resuelven a través de una caché compartida por todo el rastreo, protegida + por un único bloqueo que se mantiene durante toda la consulta de los miembros de un grupo. Ese + bloqueo impide que un hilo entregue a otro un grupo cuyos miembros aún se están leyendo, lo que + indexaría los elementos que ese grupo protege sin ninguno de sus permisos. Una vez que un grupo + está en la caché, cada referencia posterior a él es una consulta económica, por lo que se trata de + un **coste de caché fría**: el rastreo de un sitio con muchos grupos distintos pasa sus primeros + minutos más cerca de un único hilo que de ``n`` hilos, mientras que uno cuyos elementos comparten + un puñado de grupos apenas lo nota. ``role.skip=true``, que no lee ningún permiso, evita este + coste por completo. +- El descubrimiento es secuencial por sitio: el listado de carpetas y de listas de un sitio + constituye un único objetivo de rastreo, por lo que los hilos no tienen nada que repartirse hasta + que ese objetivo termina y encola lo que ha encontrado. + +**Una respuesta 503** se reintenta igual que cualquier otro error, hasta ``retry_limit`` veces, pero +con una espera creciente antes de cada reintento: 2 segundos, luego 4, luego 8, duplicándose hasta +un límite de 30 segundos, cada uno aleatorizado entre el 70 % y el 129 % de ese valor. Un objetivo +de rastreo que sigue devolviendo 503 paga esta espera antes de cada reintento que realmente llega a +hacer, pero no después del último. + +**Cada respuesta** (exitosa o no, incluida una página de un listado que el rastreo está a punto de +descartar) se inspecciona en busca del encabezado de respuesta ``X-SharePointHealthScore`` (0 +inactivo a 10 muy ocupado). Una puntuación de 9 o superior hace que el rastreo espere antes de hacer +cualquier otra cosa: la puntuación 9 espera unos 2 segundos, la 10 unos 4 segundos, y así +sucesivamente, duplicándose por cada punto por encima de 9. **Esto se va acumulando a lo largo de +todo el rastreo, sin ningún límite agregado**: una granja que se mantiene en una puntuación de +estado de 9 bajo carga sostenida añade aproximadamente 2 segundos a *cada solicitud* que hace este +conector, incluida cada página de cada listado de carpetas y de listas, lo que puede convertir un +rastreo que de otro modo tardaría horas en uno que tarda sustancialmente más. Si un rastreo se +ralentiza inesperadamente en un orden de magnitud, compruebe la puntuación de estado de la granja +durante esa ventana antes de suponer que el problema es otro. + +Ejemplos de configuración +========================= + +Todos estos ejemplos asumen NTLM. Para usar Kerberos u OAuth en su lugar, consulte `Autenticación`_ +y sustituya las líneas ``auth.ntlm.*``. + +Crawl de lista +-------------- + +Parámetros: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.list_name=Tasks + +Script: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +Crawl de biblioteca de documentos +--------------------------------- + +Parámetros: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.doclib_path=/Shared Documents + +Script: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +Crawl de un sitio ``/teams/`` +----------------------------- + +``site.path`` permite apuntar directamente a una biblioteca de documentos de un sitio bajo una ruta +administrada distinta de ``/sites/``. + +Parámetros: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.path=/teams/eng + site.doclib_path=/Shared Documents + +Script: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +Crawl recursivo de subsitios +---------------------------- + +Comienza en la colección de sitios raíz y sigue los subsitios hasta 3 niveles de profundidad. + +Parámetros: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.path=/ + site.crawl_subsites=true + site.max_depth=3 + +Script: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + role=role + +Limitaciones +============ + +- **No hay ningún tipo de crawl incremental ni de delta.** En este conector no existe en ningún + sitio un token de cambio, una consulta de delta ni un filtrado de tipo «modificado desde la última + vez»: cada ejecución hace un listado completo de todas las listas, carpetas y archivos que está + configurada para alcanzar. ``delete_old_docs`` solo controla si los documentos que el rastreo + completo actual no volvió a ver se eliminan después; eso es una limpieza posterior, no una + obtención incremental. +- **Los caracteres ``%`` y ``#`` en nombres de archivo/carpeta** se admiten en la ruta de código + predeterminada (distinta de ``2013``). Solo SharePoint Server 2019 y Subscription Edition + aceptan esos dos caracteres en un nombre; 2016 los sigue rechazando explícitamente, y 2013 + también. La ruta predeterminada llega a esos archivos mediante los puntos finales + ``...ByServerRelativePath(decodedUrl=...)``, que reciben la ruta ya decodificada, y el rastreo + escapa ambos caracteres en el enlace con el que indexa el archivo. **Con ``sp.version=2013`` no + es posible llegar a esos archivos**, porque usa los puntos finales más antiguos + ``...ByServerRelativeUrl(...)``, que interpretan su argumento como una URL ya codificada. Es una + limitación deliberada y no una carencia: una granja de SharePoint 2013 no puede contener un + nombre así. Solo importa si se apunta ``sp.version=2013`` a un servidor 2019 o Subscription + Edition, que no es una configuración que deba usarse. Consulte + `Use of # and % characters in file and folder names + `__ + y `File names - expanded support for special characters + `__. +- **IIS Extended Protection con ``tokenChecking=Require`` no se puede admitir.** Ni Apache + HttpClient 4.5 ni 5.x implementan channel binding, del que depende Extended Protection en + ``Require``. IIS establece este ajuste en ``None`` de forma predeterminada, por lo que la mayoría + de las granjas no se ven afectadas, y no hay ninguna solución alternativa para una granja en la + que esté configurado como ``Require``. +- **Las contraseñas en los parámetros de configuración de datos se almacenan y se muestran en texto + sin cifrar.** Esto se aplica tanto a ``auth.ntlm.password`` como a ``auth.kerberos.password``: + Fess no cuenta con ningún mecanismo de enmascaramiento para los parámetros de los manejadores de + almacén de datos, y la pantalla de edición de la configuración de datos los muestra en un área de + texto plano. Dé preferencia a ``auth.kerberos.keytab`` frente a ``auth.kerberos.password`` cuando + Kerberos esté disponible, y asigne al archivo keytab permisos restrictivos. +- **``sp.version=2013`` y OAuth nunca han funcionado juntos.** Todas las llamadas a la API de + SharePoint 2013 pasan por el cliente XML/Atom, y ninguna ruta de código de ese cliente adjunta un + token OAuth a una solicitud, de modo que, con ambos configurados, cada solicitud se envía sin + autenticar. Use ``auth.ntlm.*`` para SharePoint 2013. +- **Las rutas administradas distintas de ``/sites/`` y de la establecida mediante ``site.path`` + siguen sin descubrirse por sí solas.** ``site.crawl_subsites`` recorre recursivamente solo desde + el sitio raíz que configure, y ``site.path`` alcanza exactamente la única ruta administrada que + establezca, no todas las rutas administradas de la granja. + +Solución de problemas +===================== + +La autenticación falla en silencio +---------------------------------- + +**Síntoma**: las solicitudes devuelven 401 (o algo similar) sin nada claro en el log que lo explique + +**Puntos a verificar**: + +1. Compruebe si hay configurado más de uno de ``auth.kerberos.principal``, ``auth.ntlm.user`` y + ``auth.oauth.client_id``; dos o más hacen fallar el trabajo con un error de validación antes de + que empiece el rastreo +2. Para Kerberos, confirme que ``-Djava.security.krb5.conf=...`` está configurado en + ``jvm.crawler.options``. Configurarlo en cualquier lugar que solo afecte a la aplicación web no + tiene ningún efecto. Después de cambiarlo, vuelva a ejecutar el trabajo de rastreo; reiniciar la + aplicación web no lo recoge +3. Para Kerberos, confirme que ``udp_preference_limit = 1`` está configurado en la sección + ``[libdefaults]`` de ``krb5.conf``. Sin esto, un KDC que no responda puede hacer que cada + autenticación se bloquee durante unos 90 segundos (tres reintentos UDP de 30 segundos) sin nada + en el log +4. Confirme que el principal está escrito como ``user@REALM``; un ``user`` sin realm se resuelve + contra el ``default_realm`` que en ese momento indique el ``krb5.conf`` compartido +5. Para OAuth, confirme que ``client_secret``, ``tenant`` y ``realm`` no están vacíos; solo se + valida la presencia de ``client_id``, por lo que los demás pueden quedar vacíos en silencio +6. Confirme que IIS Extended Protection no está configurado como ``tokenChecking=Require``; no hay + ninguna solución alternativa para ese ajuste +7. Para un rastreo de larga duración, compruebe si empezó a fallar solo a mitad de camino; el ticket + de Kerberos se obtiene una sola vez al construirse el cliente HTTP y nunca se renueva, por lo que + un rastreo que dura más que el ticket empieza a fallar a mitad de camino + +El rastreo es lento (503 y la puntuación de estado) +--------------------------------------------------- + +**Síntoma**: el rastreo tarda mucho más de lo esperado, o se agota el tiempo de espera + +**Puntos a verificar**: + +1. Compruebe el ``X-SharePointHealthScore`` de la granja de SharePoint durante la ventana de + lentitud. Una puntuación de 9 o superior añade una espera antes de cada solicitud (unos 2 + segundos en 9, unos 4 en 10, duplicándose a partir de ahí, sin límite agregado), lo que puede + convertir un rastreo que debería tardar horas en uno que tarda mucho más +2. Compruebe si hay respuestas 503 repetidas. Un 503 se reintenta hasta ``retry_limit`` veces, + esperando 2, luego 4 y luego 8 segundos (con un límite de 30) antes de cada reintento +3. Compruebe si ``number_of_threads`` se ha aumentado demasiado. Más hilos suponen, de forma + aproximadamente proporcional, más solicitudes contra SharePoint, lo que puede elevar la + puntuación de estado. Auméntelo gradualmente en una granja local (on-premises) +4. Si ``site.crawl_subsites=true``, recuerde que el tiempo total de rastreo crece aproximadamente + con el número de subsitios descubiertos; considere reducir el alcance con ``site.max_depth`` + +No se indexa nada +----------------- + +**Síntoma**: el rastreo finaliza con normalidad, pero la búsqueda devuelve cero resultados + +**Puntos a verificar**: + +1. Compruebe el log del rastreador en busca de errores o advertencias (establezca + ``org.codelibs.fess.ds`` en ``DEBUG`` en ``app/WEB-INF/env/crawler/resources/log4j2.xml``) +2. Compruebe si hay errores tipográficos en ``url``, ``site.name`` (o ``site.path``) y + ``site.list_name``; recuerde que ``site.name`` no es necesario una vez que se configura + ``site.path`` +3. Confirme que la autenticación realmente se está realizando con éxito (sin 401); una solicitud que + nunca llega a autenticarse es una causa mucho más habitual que un ``role.skip`` o + ``default_permissions`` mal configurados +4. Si se configura ``include_pattern`` o ``exclude_pattern``, recuerde que estos comparan con una + ruta relativa al servidor (para un archivo de biblioteca de documentos o un archivo adjunto de + elemento de lista) o con ``FileRef`` (para un elemento de lista), no con la URL que se muestra en + los resultados de búsqueda. Compruebe si el patrón está escrito para una URL completa +5. Compruebe si ``supported_mimetypes`` o ``max_content_length`` está excluyendo los archivos que + espera ver +6. Compruebe si ``site.exclude_list`` o ``site.exclude_folder`` está excluyendo el objetivo de forma + no intencionada + +Información de referencia +========================= + +- :doc:`ds-overview` - Descripción general de conectores de almacén de datos +- :doc:`ds-microsoft365` - Conector de Microsoft 365 (para SharePoint Online) +- :doc:`../../admin/dataconfig-guide` - Guía de configuración del almacén de datos +- :doc:`../../admin/plugin-guide` - Guía de gestión de plugins diff --git a/es/15.9/config/datastore/index.rst b/es/15.9/config/datastore/index.rst index 97cce29b..ee7273c1 100644 --- a/es/15.9/config/datastore/index.rst +++ b/es/15.9/config/datastore/index.rst @@ -25,6 +25,7 @@ Los conectores de almacén de datos de |Fess| permiten obtener contenido de dive ds-atlassian ds-slack + ds-sharepoint .. toctree:: :maxdepth: 2 diff --git a/fr/15.9/config/datastore/ds-overview.rst b/fr/15.9/config/datastore/ds-overview.rst index e91d426e..4f7e22ea 100644 --- a/fr/15.9/config/datastore/ds-overview.rst +++ b/fr/15.9/config/datastore/ds-overview.rst @@ -60,6 +60,9 @@ Outils de collaboration * - :doc:`ds-slack` - fess-ds-slack - Exploration des messages et fichiers Slack + * - :doc:`ds-sharepoint` + - fess-ds-sharepoint + - Exploration de sites SharePoint Server sur site (on-premises) Outils de développement et opérations ------------------------------------- diff --git a/fr/15.9/config/datastore/ds-sharepoint.rst b/fr/15.9/config/datastore/ds-sharepoint.rst new file mode 100644 index 00000000..cd66125c --- /dev/null +++ b/fr/15.9/config/datastore/ds-sharepoint.rst @@ -0,0 +1,1033 @@ +============================ +Connecteur SharePoint Server +============================ + +Aperçu +====== + +Le connecteur SharePoint Server récupère les fichiers des bibliothèques de documents et +les éléments de liste d'un déploiement **SharePoint Server** sur site (2013, 2016, 2019 +ou Subscription Edition) via son API REST/OData (et, pour 2013, son API XML/Atom), puis +les enregistre dans l'index |Fess|. + +Cette fonctionnalité nécessite le plugin ``fess-ds-sharepoint``. + +.. note:: + + Si vous devez crawler SharePoint Online (Microsoft 365), utilisez + :doc:`ds-microsoft365` et non ce connecteur. La prise en charge OAuth de ce + connecteur ne cible que l'authentification application uniquement (application-only) + d'Azure ACS, et il n'intègre aucune intégration avec l'API Microsoft Graph. + +Versions prises en charge : SharePoint Server 2013 / 2016 / 2019 / Subscription Edition +(SE) + +Contenu pris en charge +====================== + +- Fichiers des bibliothèques de documents +- Éléments de liste +- Pièces jointes des éléments de liste + +Prérequis +========= + +1. L'installation du plugin est requise +2. Le compte de crawl doit disposer d'un accès en lecture aux sites, listes et + bibliothèques de documents crawlés +3. Choisissez exactement une méthode d'authentification - NTLM, Kerberos (SPNEGO) ou + OAuth (ACS) - et tenez ses identifiants prêts + +Installation du plugin +---------------------- + +Installez-le depuis l'interface d'administration via « Système » → « Plugin » : + +1. Téléchargez ``fess-ds-sharepoint-X.X.X.jar`` +2. Placez-le sous ``$FESS_HOME/app/WEB-INF/lib`` (ou + ``/usr/share/fess/app/WEB-INF/lib``) +3. Redémarrez |Fess| + +Consultez :doc:`../../admin/plugin-guide` pour plus de détails. + +Configuration +============= + +Configurez ce connecteur depuis l'interface d'administration via « Crawler » → « Data +Store » → « Nouveau ». + +Configuration de base +--------------------- + +.. list-table:: + :header-rows: 1 + :widths: 25 75 + + * - Élément + - Exemple + * - Nom + - SharePoint + * - Nom du gestionnaire + - SharePointDataStore + * - Activé + - Oui + +Configuration des paramètres +---------------------------- + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.doclib_path=/Shared Documents + +Liste des paramètres +~~~~~~~~~~~~~~~~~~~~ + +**URL / Site** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Paramètre + - Requis + - Description + * - ``url`` + - Oui + - URL de base du serveur SharePoint, par exemple + ``http://sharepoint.example.com/`` + * - ``site.name`` + - Conditionnel + - Nom de la collection de sites crawlée sous + ``/sites//``. Non nécessaire si + ``site.path`` est défini + * - ``site.path`` + - Non + - Chemin d'accès géré relatif au serveur du site (par + exemple ``/teams/eng`` ; utilisez ``/`` pour la + collection de sites racine). Lorsqu'il est défini, il + est utilisé tel quel à la place du préfixe codé en dur + ``/sites/``, et ``site.name`` n'est plus requis + * - ``site.list_id`` + - Non + - Crawle une seule liste par son GUID (mode Crawl de + liste) + * - ``site.list_name`` + - Non + - Crawle une seule liste par son nom d'affichage (mode + Crawl de liste) + * - ``site.doclib_path`` + - Non + - Chemin de la bibliothèque de documents sous le site + (mode Crawl de bibliothèque de documents), par exemple + ``/Shared Documents`` + * - ``site.exclude_list`` + - Non + - Motifs regex séparés par des virgules des noms de types + d'entité de liste à exclure. S'applique uniquement à un + crawl de site complet + * - ``site.exclude_folder`` + - Non + - Motifs regex séparés par des virgules des titres de + dossiers de premier niveau à exclure. S'applique + uniquement à un crawl de site complet + * - ``site.crawl_subsites`` + - Non + - Parcourt récursivement les sous-sites du site (par + défaut : ``false``). Voir `Sous-sites et chemins + d'accès gérés`_ + * - ``site.max_depth`` + - Non + - Nombre de niveaux de sous-sites que + ``site.crawl_subsites`` peut parcourir (par défaut : + ``10``) ; la racine est à la profondeur 0 + +**Authentification** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Paramètre + - Requis + - Description + * - ``auth.ntlm.user`` + - Non + - Nom d'utilisateur NTLM. Le définir active NTLM + (``DOMAIN\user`` fonctionne) + * - ``auth.ntlm.password`` + - Non + - Mot de passe NTLM + * - ``auth.ntlm.domain`` + - Non + - Domaine Windows, envoyé comme champ NTLM distinct + * - ``auth.ntlm.workstation`` + - Non + - Nom de poste de travail envoyé lors de la négociation + NTLM + * - ``auth.kerberos.principal`` + - Non + - Principal client, écrit sous la forme ``user@REALM``. + Le définir active Kerberos/SPNEGO + * - ``auth.kerberos.keytab`` + - Non + - Chemin vers un keytab contenant une clé pour le + principal. Mutuellement exclusif avec + ``auth.kerberos.password`` + * - ``auth.kerberos.password`` + - Non + - Mot de passe du principal, utilisé uniquement si aucun + keytab n'est défini + * - ``auth.kerberos.strip_port`` + - Non + - Supprime le port du nom de principal de service (par + défaut : ``true``) + * - ``auth.kerberos.use_canonical_hostname`` + - Non + - Résout l'hôte cible vers son nom canonique avant de + construire le nom de principal de service (par défaut : + ``false``) + * - ``auth.kerberos.krb5_conf`` + - Non + - Chemin vers un ``krb5.conf``. Appliqué uniquement si + ``java.security.krb5.conf`` n'est pas déjà défini + * - ``auth.kerberos.debug`` + - Non + - Active la sortie de débogage de ``Krb5LoginModule`` + (par défaut : ``false``) + * - ``auth.oauth.client_id`` + - Non + - ID client OAuth application uniquement + (application-only) d'Azure ACS. Le définir active OAuth + * - ``auth.oauth.client_secret`` + - Non + - Secret client OAuth + * - ``auth.oauth.tenant`` + - Non + - Nom du tenant, sans ``.sharepoint.com`` + * - ``auth.oauth.realm`` + - Non + - ID de royaume/répertoire (realm/directory) Azure AD + +**Une seule** des options ``auth.kerberos.principal``, ``auth.ntlm.user`` et +``auth.oauth.client_id`` peut être définie. Voir `Authentification`_ ci-dessous. + +**Liste** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Paramètre + - Requis + - Description + * - ``list.items.number_per_page`` + - Non + - Taille de page pour ``GetListItems`` (par défaut : + ``100``) + * - ``list.item.content.include_fields`` + - Non + - Noms de champs séparés par des virgules ; si défini, + seuls ces champs de l'élément de liste sont concaténés + dans ``content`` + * - ``list.item.content.exclude_fields`` + - Non + - Motifs de noms de champs séparés par des virgules + (chacun traité comme une regex), exclus de ``content`` + en plus d'un vaste ensemble intégré de champs standard + * - ``list.is_sub_page`` + - Non + - Traite les éléments de liste comme des sous-pages + SitePages/wiki, ce qui affecte le repli de pagination + et la forme du lien web (par défaut : ``false``) + +**HTTP** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Paramètre + - Requis + - Description + * - ``http.connection_timeout`` + - Non + - Délai de connexion HTTP en ms ; également utilisé comme + délai d'attente du pool de connexions (par défaut : + ``30000``) + * - ``http.socket_timeout`` + - Non + - Délai de socket HTTP (lecture) en ms (par défaut : + ``30000``) + * - ``proxy_host`` + - Non + - Hôte du proxy HTTP + * - ``proxy_port`` + - Conditionnel + - Port du proxy HTTP ; requis si ``proxy_host`` est + défini (par défaut : ``-1`` = pas de proxy) + +**Filtrage et contenu** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Paramètre + - Requis + - Description + * - ``include_pattern`` + - Non + - Regex que la valeur d'un élément doit satisfaire pour + être crawlé. Voir la note sous ce tableau pour savoir + de quelle valeur il s'agit + * - ``exclude_pattern`` + - Non + - Regex qui exclut du crawl un élément correspondant + * - ``supported_mimetypes`` + - Non + - Regex séparées par des virgules dont le type MIME d'un + fichier doit satisfaire au moins une (par défaut : + ``.*``) + * - ``max_content_length`` + - Non + - Taille maximale de fichier en octets ; un fichier + dépassant la limite est ignoré, pas mis en échec (par + défaut : ``-1`` = pas de limite) + * - ``extractor_name`` + - Non + - Extracteur de repli utilisé uniquement pour un type + MIME que la fabrique d'extracteurs ne mappe pas (par + défaut : ``tikaExtractor``) + +**Comportement** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - Paramètre + - Requis + - Description + * - ``sp.version`` + - Non + - Définir à ``2013`` pour basculer vers la famille d'API + XML/Atom ``GetXxxByServerRelativeUrl`` de SharePoint + 2013 (non défini ⇒ dialecte REST SharePoint Online / + 2016+) + * - ``retry_limit`` + - Non + - Nombre maximal de tentatives par unité de crawl en cas + d'exception serveur/client SharePoint (par défaut : + ``2``) + * - ``role.skip`` + - Non + - Ignore complètement la récupération des permissions par + élément (par défaut : ``false``). Voir `Permissions`_ + * - ``ignore_error`` + - Non + - Journalise et ignore un échec d'extraction de contenu + d'un fichier au lieu de mettre en échec la cible de + crawl (par défaut : ``false``) + * - ``default_permissions`` + - Non + - Chaînes de permission séparées par des virgules, + fusionnées dans la liste de rôles de chaque document en + plus de ce que SharePoint a renvoyé + * - ``delete_old_docs`` + - Non + - Indique si les documents non actualisés lors de cette + exécution sont supprimés (par défaut du cœur : + ``true``). Ce plugin le force à ``false`` pour + l'exécution en cours dès qu'une cible de crawl a échoué + * - ``number_of_threads`` + - Non + - Nombre de cibles de crawl traitées simultanément (par + défaut : ``1`` = pas de pool de threads), plafonné au + double du nombre de processeurs. Voir `Crawl parallèle + et charge`_ + * - ``script_type`` + - Non + - Moteur de script pour le Script de la configuration de + données (par défaut : ``groovy``) + * - ``readInterval`` + - Non + - Pause entre deux résultats de crawl successifs, en ms + (par défaut : ``0``). Notez l'orthographe en camelCase, + contrairement à tous les autres paramètres ci-dessus + +Configuration du script +----------------------- + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + role=role + +Champs disponibles +~~~~~~~~~~~~~~~~~~ + +.. list-table:: + :header-rows: 1 + :widths: 16 20 32 32 + + * - Clé + - Élément de liste + (ItemCrawl) + - Fichier de bibliothèque + (FolderCrawl->FileCrawl) + - Pièce jointe + (ItemAttachmentsCrawl->FileCrawl) + * - ``url`` + - Lien web + - URL du fichier + - URL du fichier + * - ``host`` + - Nom d'hôte + - Nom d'hôte + - Nom d'hôte + * - ``site`` + - Chemin relatif au + serveur + (``FileRef``) + - Chemin relatif au serveur + - Chemin relatif au serveur + * - ``title`` + - Champ ``Title``, + sinon + ``FileLeafRef``/nom + de fichier + - La valeur de liste ``Title`` + propre au fichier de la + bibliothèque si présente, + sinon le nom de fichier + - Nom de fichier + * - ``titleWithListName`` + - ``"[listName] + title"`` + - ``"[listName] filename"`` (le + nom de liste est toujours + vide pour un crawl de + bibliothèque de documents, + donc il s'agit en pratique du + seul nom de fichier) + - ``"[listName] filename"`` + * - ``listName`` + - Nom d'affichage de + la liste, ou + ``""`` + - Toujours ``""`` + - Nom réel de la liste + * - ``content`` + - Concaténation des + valeurs de champs + - Texte extrait + - Texte extrait + * - ``digest`` + - ``content`` abrégé + - ``content`` abrégé + - ``content`` abrégé + * - ``content_length`` + - ``content.length()`` + - ``content.length()`` + - ``content.length()`` + * - ``last_modified`` + - Depuis le listing + - Depuis le listing + - Depuis le listing + * - ``created`` + - Depuis le listing + - Depuis le listing + - Depuis le listing + * - ``mimetype`` + - Toujours + ``text/html`` + - Détecté + - Détecté + * - ``filetype`` + - Dérivé de + ``mimetype`` + - Dérivé de ``mimetype`` + - Dérivé de ``mimetype`` + * - ``role`` + - Liste de + permissions, + uniquement si non + vide + - Liste de permissions, + uniquement si non vide + - Liste de permissions, + uniquement si non vide + * - ``list_name`` + - Présent + - **Absent** + - Présent + * - ``list_id`` + - Présent + - **Absent** + - Présent + * - ``item_id`` + - Présent + - **Absent** + - Présent + +.. note:: + + ``content_length`` correspond à ``content.length()`` - le nombre de caractères + (unités de code UTF-16) du texte extrait ou concaténé, et non la taille du fichier en + octets. Ceci diffère de ``file.size`` dans les connecteurs Box, Google Drive et + Dropbox, qui est la taille réelle en octets issue des métadonnées de fichier propres + à chaque service. Ne comparez pas le ``content_length`` de ce connecteur à ces + valeurs. + +**Clés dynamiques : ``val_*``** + +Chaque clé du ``FieldValuesAsText`` d'un élément de liste (la map brute des valeurs de +champs que SharePoint renvoie pour cet élément, y compris les clés de métadonnées OData +telles que ``odata.metadata``) est exposée sous deux noms : une fois sans préfixe +(uniquement si ce nom n'est pas déjà l'une des clés fixes ci-dessus), et une fois avec +le préfixe ``val_``, systématiquement - par exemple, un champ ``Status`` devient à la +fois ``Status`` et ``val_Status``. + +Les clés ``val_*`` n'existent que sur le **chemin de crawl des éléments de liste +(ItemCrawl)**. Un fichier de bibliothèque de documents (FolderCrawl->FileCrawl) ou une +pièce jointe d'élément de liste (ItemAttachmentsCrawl->FileCrawl) ne produit jamais de +clé ``val_*``. + +Authentification +================ + +Trois méthodes d'authentification sont disponibles, et **une seule peut être +configurée**. Définir plus d'une des options ``auth.kerberos.principal``, +``auth.ntlm.user`` et ``auth.oauth.client_id`` fait échouer le job de configuration de +données avec une erreur de validation avant qu'aucune requête ne soit émise. C'est voulu +: un seul jeu d'identifiants est enregistré auprès du client HTTP, et la portée sous +laquelle il est enregistré correspond aussi bien à un défi ``Negotiate`` qu'à un défi +``NTLM``, donc en configurer plus d'une produirait sinon des 401 que rien dans le +journal n'expliquerait. + +NTLM +---- + +:: + + auth.ntlm.user={Nom d'utilisateur SharePoint} + auth.ntlm.password={Mot de passe} + auth.ntlm.domain={Domaine Windows. Optionnel ; non défini par défaut.} + auth.ntlm.workstation={Nom de poste de travail envoyé lors de la négociation NTLM. Optionnel ; non défini par défaut.} + +``auth.ntlm.domain`` et ``auth.ntlm.workstation`` sont tous deux non définis par défaut, +ce qui construit exactement les identifiants que ce connecteur a toujours construits. +Écrire le domaine dans le nom d'utilisateur sous la forme ``DOMAIN\user`` continue de +fonctionner. Définir ``auth.ntlm.domain`` envoie le domaine comme champ NTLM distinct, +ce qui est ce que veut un serveur qui rejette la forme combinée. + +Kerberos (SPNEGO) +----------------- + +**Périmètre pris en charge :** une seule JVM crawler, un ``krb5.conf`` par instance +Fess, un keytab ou un mot de passe, aucune délégation, aucune liaison de canal (channel +binding), et mutuellement exclusif avec NTLM et OAuth. Tout ce qui sort de ce périmètre +n'est pas pris en charge. + +:: + + auth.kerberos.principal={Principal client, écrit sous la forme user@REALM. Le définir active Kerberos.} + auth.kerberos.keytab={Chemin vers un keytab contenant une clé pour le principal. Mutuellement exclusif avec auth.kerberos.password.} + auth.kerberos.password={Mot de passe du principal. Utilisé uniquement si aucun keytab n'est défini.} + auth.kerberos.strip_port={true ou false. Supprime le port du nom de principal de service. Par défaut true.} + auth.kerberos.use_canonical_hostname={true ou false. Résout l'hôte cible vers son nom canonique pour le nom de principal de service. Par défaut false.} + auth.kerberos.krb5_conf={Chemin vers un krb5.conf. Appliqué uniquement si java.security.krb5.conf n'est pas déjà défini.} + auth.kerberos.debug={true ou false. Sortie de débogage de Krb5LoginModule. Par défaut false.} + +- **``krb5.conf`` doit être placé dans ``jvm.crawler.options``**, sous la forme + ``-Djava.security.krb5.conf=/path/to/krb5.conf``. Le crawl des data stores s'exécute + dans le **processus enfant** du crawler, donc définir ce paramètre à un endroit qui + n'affecte que le webapp n'a aucun effet, et un redémarrage du webapp ne prend pas en + compte un changement - le job de crawl doit être réexécuté. + ``auth.kerberos.krb5_conf`` est une commodité pour le cas où rien n'a encore défini + cette propriété : il **n'écrase jamais une valeur déjà définie**, car cette propriété + est globale à la JVM et une seule JVM crawler exécute toutes les configurations de + données d'un job de crawl. Lorsqu'il renonce à écraser, il journalise un avertissement + nommant les deux chemins. +- **Placez ``udp_preference_limit = 1`` dans la section ``[libdefaults]`` de + ``krb5.conf``.** Sans cela, le JDK tente d'abord l'UDP, et lorsque le KDC ne répond + pas (injoignable, un pare-feu qui bloque l'UDP 88, ou une réponse plus grande que la + taille du datagramme), il retente trois fois à trente secondes d'intervalle avant de + basculer sur TCP. Un crawl qui semble bloqué pendant environ une minute et demie par + authentification, sans rien dans le journal, en est généralement la cause. +- **Écrivez toujours le principal sous la forme ``user@REALM``.** ``default_realm`` est + global à la JVM, et plusieurs fermes SharePoint dans des royaumes (realms) différents + peuvent avoir à partager un seul ``krb5.conf``, donc un simple ``user`` se résout par + rapport au royaume que ce fichier indique, quel qu'il soit. +- **``auth.kerberos.use_canonical_hostname`` vaut ``false`` par défaut**, délibérément à + l'inverse du défaut propre d'Apache HttpClient. Une fois activé, l'hôte cible passe + par une résolution DNS inverse avant que le nom de principal de service ne soit + construit, ce qui, sous des mappages d'accès alternatifs ou derrière un équilibreur de + charge, peut produire un nom pour lequel aucun SPN n'est enregistré - et l'échec + résultant ne dit rien sur le DNS. Ne l'activez que si le SPN est réellement enregistré + sous le nom canonique. +- **IIS Extended Protection réglé sur ``tokenChecking=Require`` ne peut pas + fonctionner.** Ni Apache HttpClient 4.5 ni 5.x ne prennent en charge la liaison de + canal (channel binding). IIS règle ce paramètre par défaut sur ``None``, donc ce n'est + généralement pas rencontré, et il n'existe aucun contournement lorsque c'est le cas. +- **Le ticket est obtenu une seule fois, lors de la construction du client HTTP du + crawl, et n'est jamais renouvelé.** Un crawl qui dure plus longtemps que la durée de + vie du ticket commence à échouer à s'authentifier en cours de route. +- **``auth.kerberos.password`` est stocké et affiché en texte clair**, exactement comme + ``auth.ntlm.password``. Fess n'a aucun mécanisme de masquage pour les paramètres des + gestionnaires de data store ; l'écran d'édition de la configuration de données les + affiche dans une zone de texte brut. Préférez ``auth.kerberos.keytab``, et donnez au + fichier keytab des permissions restrictives. +- ``auth.kerberos.debug=true`` fait écrire ``Krb5LoginModule`` sur la sortie standard du + processus crawler, et non dans le journal de Fess. + +OAuth (ACS) +----------- + +:: + + auth.oauth.client_id={ID client OAuth} + auth.oauth.client_secret={Secret client OAuth} + auth.oauth.tenant={Nom du tenant, sans .sharepoint.com} + auth.oauth.realm={ID de royaume/répertoire Azure AD} + +Définir ``auth.oauth.client_id`` active un flux client-credentials (application +uniquement) vers le Windows Azure Access Control Service, +``https://accounts.accesscontrol.windows.net/{realm}/tokens/OAuth/2``. Le jeton d'accès +est récupéré une seule fois, lors de la construction du client HTTP du crawl, appliqué +comme en-tête ``Authorization`` ``Bearer`` sur chaque requête, et rafraîchi puis retenté +une seule fois en cas de 401. **Microsoft a déprécié ACS et prévu son retrait** ; ce +connecteur journalise un avertissement à ce sujet à chaque crawl configuré avec OAuth. +Aucun flux d'enregistrement d'application Entra ID (par certificat ou secret client) +n'est implémenté ici - seule l'authentification ACS application uniquement, historique +(legacy), est prise en charge. + +Seule la présence de ``auth.oauth.client_id`` est vérifiée avant l'activation d'OAuth ; +``client_secret``, ``tenant`` et ``realm`` sont lus inconditionnellement et peuvent +rester vides en silence s'ils sont omis, ce qui casse l'acquisition du jeton sans +message de validation dédié. + +**``sp.version=2013`` et OAuth n'ont jamais fonctionné ensemble.** Tous les appels d'API +SharePoint 2013 effectués par ce connecteur passent par le client XML/Atom, et aucun +chemin de code de ce client n'attache de jeton OAuth à une requête - donc si les deux +sont définis, chaque requête est envoyée sans authentification. Le crawl journalise un +avertissement le disant explicitement et mentionnant ``auth.ntlm.*`` comme alternative ; +cela ne fait pas échouer le job. Utilisez ``auth.ntlm.*`` pour SharePoint 2013. + +Permissions +=========== + +``role.skip=true`` (par défaut ``false``) ignore complètement la récupération des +permissions par élément : aucun appel ``GetListItemRole`` n'est effectué, la clé +``role`` n'est jamais définie pour l'élément, et le document finit par ne porter que le +paramètre de permission statique de la configuration de données et, si configuré, +``default_permissions`` - aucune permission dérivée de SharePoint ne l'atteint. + +Lorsque les rôles sont récupérés, les utilisateurs, groupes de sécurité et groupes +SharePoint propres à SharePoint sont développés et mappés vers les rôles de recherche +Fess : + +- Un compte ou groupe **AD sur site** (nom de connexion contenant une barre oblique + inverse, ne commençant pas par un préfixe de revendication (claim) Azure) est mappé + via les assistants de rôle utilisateur/groupe AD standard. +- Un compte **Azure AD (Entra ID)** (nom de connexion commençant par + ``i:0#.f|membership|``) est mappé **deux fois** - une fois par sa valeur de + revendication Azure complète, une fois par la partie compte AD précédant le ``@`` dans + cette revendication - de sorte qu'un rôle de style Entra ID et un rôle de style AD + sont tous deux ajoutés pour le même utilisateur. Un groupe de sécurité marqué comme + Azure (par l'un de plusieurs préfixes de style revendication, y compris le groupe + spécial « tout le monde » ``spo-grid-all-users``) est mappé de la même façon, sous les + deux formes. +- Un **groupe SharePoint** voit sa propre appartenance (utilisateurs, groupes de + sécurité, groupes imbriqués) développée récursivement, avec une protection contre les + groupes déjà visités pour arrêter la récursion infinie entre des groupes qui se + contiennent mutuellement. + +``default_permissions`` (séparés par des virgules) est fusionné **après** tout ce qui +précède, et s'applique même lorsque SharePoint n'a renvoyé aucun rôle pour l'élément - +le cas produit aussi bien par ``role.skip=true`` que par « SharePoint n'a rien +renvoyé ». La liste de rôles finale est l'union du paramètre de permission statique de +la configuration de données, des rôles dérivés de SharePoint (sauf s'ils sont ignorés) +et de ``default_permissions``, après suppression des doublons. + +Sous-sites et chemins d'accès gérés +=================================== + +Définir ``site.path`` utilise tel quel le chemin d'accès géré relatif au serveur +indiqué, à la place du préfixe codé en dur ``/sites/``, et ``site.name`` n'est plus +requis. + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Scénario + - Configuration + * - Collection de sites racine + - ``site.path=/`` + * - Le site ``/teams/eng`` + - ``site.path=/teams/eng`` + * - La forme classique + ``/sites/mysite/`` + - ``site.name=mysite`` (laisser ``site.path`` non défini) + +Définir ``site.crawl_subsites`` (par défaut ``false``) fait qu'un crawl de site complet +- un crawl où ni ``site.list_name`` ni ``site.doclib_path`` n'est défini - parcourt +récursivement les sous-sites du site, découverts via ``_api/web/webinfos``. Le laisser +non défini fait que le crawl continue d'émettre exactement les mêmes requêtes +qu'auparavant, y compris de ne jamais demander ``webinfos``. + +Les documents d'un sous-site atterrissent dans la même configuration de données que ceux +du site racine, sous leurs propres chemins relatifs au serveur - rien dans l'index ne +marque un document comme provenant d'un sous-site plutôt que de la racine. + +``site.max_depth`` (par défaut ``10``) limite le nombre de niveaux de sous-sites en +dessous du site racine qui sont crawlés une fois ``site.crawl_subsites=true``. Le site +racine lui-même est à la profondeur 0, donc ``site.max_depth=1`` crawle les enfants +directs de la racine et rien de plus. Le définir en dessous de ``1`` alors que +``site.crawl_subsites=true`` désactive de fait la fonctionnalité - aucun sous-site n'est +crawlé - et un avertissement est journalisé au démarrage du crawl. + +Activer le crawl des sous-sites **multiplie le temps total du crawl** par environ le +nombre de sous-sites découverts (limité par ``site.max_depth``) : chacun reçoit son +propre listing complet de dossiers, son propre listing de listes et, s'il n'est pas à la +limite de profondeur, son propre appel ``webinfos``, en plus de tout ce que le crawl du +site racine effectue déjà. + +``number_of_threads`` et ``readInterval``, décrits dans `Crawl parallèle et charge`_, +s'appliquent à un crawl récursif de sous-sites de la même façon qu'à tout autre crawl. + +Crawl parallèle et charge +========================= + +``number_of_threads`` (par défaut ``1``) est le nombre de cibles de crawl traitées +simultanément. Avec la valeur par défaut, le crawl s'exécute exactement comme avant : +chaque cible est crawlée sur le thread de crawl et **aucun pool de threads n'est créé**. + +La valeur est **plafonnée au double du nombre de processeurs** de la machine exécutant +Fess, de sorte qu'une configuration de données ne peut pas demander plus de parallélisme +que l'hôte ne peut en servir. Une valeur inférieure à ``1`` - ou une valeur vide ou +impossible à analyser - retombe à ``1`` plutôt que d'être honorée ou de faire échouer le +job. Une valeur qui a été plafonnée, ou une valeur inférieure à ``1``, est journalisée +avec à la fois la valeur demandée et la valeur réelle ; une valeur impossible à analyser +journalise un avertissement. Une valeur vide ne journalise rien, car un champ vide +signifie simplement que le paramètre n'a pas été défini. + +Le pool de connexions HTTP est dimensionné en conséquence. Apache HttpClient n'autorise +par défaut que 2 connexions par route, et un crawl entier constitue une seule route : +sans l'augmenter, chaque thread au-delà du deuxième passerait le crawl à attendre une +connexion plutôt qu'à émettre des requêtes. + +**``readInterval`` continue de cadencer la remise des documents, un document par +intervalle, quelle que soit sa valeur.** Les threads accélèrent la découverte et la +récupération par le crawl ; ils n'accélèrent pas l'arrivée des documents à l'indexeur. +C'est voulu : diviser l'intervalle configuré par l'opérateur par le nombre de threads +multiplierait exactement la charge que cet intervalle est censé limiter. Un worker qui +termine un document pendant que les précédents sont encore en cours de remise attend +simplement. + +Ce que l'augmentation de ``number_of_threads`` **multiplie** réellement, c'est le débit +de requêtes vers SharePoint. L'attente de repli (backoff) sur 503 et l'attente liée à +``X-SharePointHealthScore`` décrites ci-dessous sont appliquées par cible de crawl, sur +le thread qui la crawle, donc ``n`` threads génèrent jusqu'à ``n`` fois les requêtes +d'un crawl mono-thread - y compris pendant une période où la ferme signale qu'elle est +occupée. Sur une ferme sur site, augmentez cette valeur progressivement. + +Deux facteurs plafonnent ce que des threads supplémentaires apportent réellement : + +- **La première fois que l'appartenance de chaque groupe SharePoint est lue, elle l'est + par un seul thread à la fois.** Les permissions sont résolues via un cache partagé par + tout le crawl, protégé par un verrou unique maintenu pendant les recherches des + membres d'un groupe. Ce verrou empêche qu'un thread ne transmette à un autre un groupe + dont les membres sont encore en cours de lecture, ce qui indexerait les éléments que + ce groupe protège sans aucune de ses permissions. Une fois un groupe mis en cache, + toute référence ultérieure à celui-ci est une recherche peu coûteuse ; il s'agit donc + d'un **coût de cache froid** : le crawl d'un site comportant de nombreux groupes + distincts passe ses premières minutes plus proche d'un fonctionnement mono-thread que + de ``n`` threads, tandis qu'un site dont les éléments partagent une poignée de groupes + le remarque à peine. ``role.skip=true``, qui ne lit aucune permission, évite + entièrement ce coût. +- La découverte est séquentielle par site : les listings de dossiers et de listes d'un + site constituent une seule cible de crawl, donc les threads n'ont rien à se répartir + tant que cette cible n'est pas terminée et que ce qu'elle a trouvé n'est pas mis en + file d'attente. + +**Une réponse 503** est retentée comme n'importe quelle autre erreur, jusqu'à +``retry_limit`` fois, mais avec une attente croissante avant chaque nouvelle tentative : +2 secondes, puis 4, puis 8, en doublant jusqu'à un plafond de 30 secondes, chacune +randomisée entre 70 et 129 % de cette valeur. Une cible de crawl qui continue de +renvoyer 503 paie cette attente avant chaque nouvelle tentative qu'elle obtient +réellement, mais pas après la dernière. + +**Chaque réponse** - qu'elle réussisse ou non, y compris une page d'un listing que le +crawl s'apprête à écarter - est inspectée pour l'en-tête de réponse +``X-SharePointHealthScore`` (0 = inactif à 10 = très occupé). Un score de 9 ou plus fait +attendre le crawl avant toute autre action : un score de 9 attend environ 2 secondes, un +score de 10 environ 4 secondes, et ainsi de suite, en doublant pour chaque point au-delà +de 9. **Cela s'accumule sur l'ensemble du crawl, sans plafond global** : une ferme se +maintenant à un score de santé de 9 sous charge soutenue ajoute environ 2 secondes à +*chaque requête* effectuée par ce connecteur - y compris chaque page de chaque listing +de dossiers et de listes - ce qui peut transformer un crawl qui prendrait autrement des +heures en un crawl nettement plus long. Si un crawl ralentit de façon inattendue d'un +ordre de grandeur, vérifiez le score de santé de la ferme durant cette période avant de +supposer autre chose. + +Exemples d'utilisation +====================== + +Tous ces exemples supposent NTLM. Pour utiliser Kerberos ou OAuth à la place, voir +`Authentification`_ et remplacer les lignes ``auth.ntlm.*``. + +Crawl de liste +-------------- + +Paramètres : + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.list_name=Tasks + +Script : + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +Crawl de bibliothèque de documents +---------------------------------- + +Paramètres : + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.doclib_path=/Shared Documents + +Script : + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +Crawl d'un site ``/teams/`` +--------------------------- + +``site.path`` permet de pointer directement vers une bibliothèque de documents sur un +site situé sous un chemin d'accès géré autre que ``/sites/``. + +Paramètres : + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.path=/teams/eng + site.doclib_path=/Shared Documents + +Script : + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +Crawl récursif des sous-sites +----------------------------- + +Démarre à la collection de sites racine et suit les sous-sites jusqu'à 3 niveaux de +profondeur. + +Paramètres : + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.path=/ + site.crawl_subsites=true + site.max_depth=3 + +Script : + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + role=role + +Limitations +=========== + +- **Aucun crawl incrémental ou différentiel d'aucune sorte.** Il n'existe dans ce + connecteur aucun jeton de changement, aucune delta-query, ni aucun filtrage « modifié + depuis » - chaque exécution effectue un listing complet de chaque liste, dossier et + fichier qu'elle est configurée pour atteindre. ``delete_old_docs`` contrôle uniquement + si les documents que le crawl complet en cours n'a pas revus sont supprimés après coup + ; il s'agit d'un nettoyage a posteriori, pas d'une récupération incrémentale. +- **``%`` et ``#`` dans les noms de fichiers/dossiers** sont pris en charge sur le + chemin de code par défaut (non ``2013``). Seuls SharePoint Server 2019 et la + Subscription Edition acceptent ces deux caractères dans un nom ; 2016 les refuse + toujours explicitement, et 2013 également. Le chemin par défaut atteint un tel + fichier via les points de terminaison ``...ByServerRelativePath(decodedUrl=...)``, + qui reçoivent le chemin décodé, et l'exploration échappe les deux caractères dans + le lien sous lequel elle indexe le fichier. **``sp.version=2013`` ne permet pas + d'atteindre un tel fichier**, car ce chemin utilise les points de terminaison plus + anciens ``...ByServerRelativeUrl(...)``, qui lisent leur argument comme une URL + déjà encodée. Il s'agit d'une limite délibérée et non d'une lacune : une ferme + SharePoint 2013 ne peut pas contenir un tel nom. Cela ne compte donc que si + ``sp.version=2013`` est pointé vers un serveur 2019 ou Subscription Edition, ce + qui n'est pas une configuration à utiliser. Voir + `Use of # and % characters in file and folder names + `__ + et `File names - expanded support for special characters + `__. +- **IIS Extended Protection avec ``tokenChecking=Require`` ne peut pas être pris en + charge.** Ni Apache HttpClient 4.5 ni 5.x n'implémentent la liaison de canal (channel + binding), dont dépend Extended Protection en mode ``Require``. IIS règle ce paramètre + par défaut sur ``None``, donc la plupart des fermes ne sont pas concernées, et il + n'existe aucun contournement pour une ferme où il est réglé sur ``Require``. +- **Les mots de passe dans les paramètres de la configuration de données sont stockés et + affichés en texte clair.** Cela s'applique aussi bien à ``auth.ntlm.password`` qu'à + ``auth.kerberos.password`` : Fess n'a aucun mécanisme de masquage pour les paramètres + des gestionnaires de data store, et l'écran d'édition de la configuration de données + les affiche dans une zone de texte brut. Préférez ``auth.kerberos.keytab`` à + ``auth.kerberos.password`` là où Kerberos est disponible, et donnez au fichier keytab + des permissions restrictives. +- **``sp.version=2013`` et OAuth n'ont jamais fonctionné ensemble.** Tout appel d'API + SharePoint 2013 passe par le client XML/Atom, et aucun chemin de code de ce client + n'attache de jeton OAuth à une requête, donc si les deux sont définis, chaque requête + est envoyée sans authentification. Utilisez ``auth.ntlm.*`` pour SharePoint 2013. +- **Les chemins d'accès gérés autres que ``/sites/`` et celui défini via ``site.path`` + ne sont toujours pas découverts automatiquement.** ``site.crawl_subsites`` ne parcourt + récursivement qu'à partir du site racine que vous configurez, et ``site.path`` + n'atteint que le seul chemin d'accès géré que vous définissez, pas tous les chemins + d'accès gérés de la ferme. + +Dépannage +========= + +L'authentification échoue silencieusement +----------------------------------------- + +**Symptôme** : les requêtes reviennent en 401 (ou similaire) sans rien de clair dans le +journal pour expliquer pourquoi + +**Points à vérifier** : + +1. Vérifiez si plus d'une des options ``auth.kerberos.principal``, ``auth.ntlm.user`` et + ``auth.oauth.client_id`` est définie - en définir deux ou plus fait échouer le job + avec une erreur de validation avant que le crawl ne démarre +2. Pour Kerberos, confirmez que ``-Djava.security.krb5.conf=...`` est défini dans + ``jvm.crawler.options``. Le définir à un endroit qui n'affecte que le webapp n'a + aucun effet. Après l'avoir modifié, réexécutez le job de crawl - redémarrer le webapp + ne le prend pas en compte +3. Pour Kerberos, confirmez que ``udp_preference_limit = 1`` est défini dans la section + ``[libdefaults]`` de ``krb5.conf``. Sans cela, un KDC qui ne répond pas peut faire + durer chaque authentification environ 90 secondes (trois tentatives UDP de 30 + secondes) sans rien dans le journal +4. Confirmez que le principal est écrit sous la forme ``user@REALM`` - un simple + ``user`` se résout par rapport au ``default_realm`` que le ``krb5.conf`` partagé + indique +5. Pour OAuth, confirmez que ``client_secret``, ``tenant`` et ``realm`` ne sont pas + vides - seule la présence de ``client_id`` est validée, donc les autres peuvent être + vides en silence +6. Confirmez qu'IIS Extended Protection n'est pas réglé sur ``tokenChecking=Require`` - + il n'existe aucun contournement pour ce paramètre +7. Pour un crawl de longue durée, vérifiez s'il n'a commencé à échouer qu'à mi-parcours + - le ticket Kerberos est obtenu une seule fois à la construction du client HTTP et + n'est jamais renouvelé, donc un crawl qui dépasse la durée de vie du ticket commence + à échouer en cours de route + +Le crawl est lent (503 et le Health Score) +------------------------------------------ + +**Symptôme** : le crawl prend beaucoup plus de temps que prévu, ou expire + +**Points à vérifier** : + +1. Vérifiez le ``X-SharePointHealthScore`` de la ferme SharePoint pendant la période de + ralentissement. Un score de 9 ou plus ajoute une attente avant chaque requête + (environ 2 secondes à 9, environ 4 à 10, en doublant ensuite, sans plafond global), + ce qui peut transformer un crawl qui devrait prendre des heures en un crawl bien plus + long +2. Vérifiez la présence de réponses 503 répétées. Une réponse 503 est retentée jusqu'à + ``retry_limit`` fois, en attendant 2, puis 4, puis 8 secondes (plafonné à 30) avant + chaque nouvelle tentative +3. Vérifiez si ``number_of_threads`` a été augmenté de façon excessive. Plus de threads + signifie à peu près proportionnellement plus de requêtes vers SharePoint, ce qui peut + pousser le score de santé plus haut. Augmentez-le progressivement sur une ferme sur + site +4. Si ``site.crawl_subsites=true``, gardez à l'esprit que le temps total du crawl croît + à peu près avec le nombre de sous-sites découverts - envisagez de réduire la portée + avec ``site.max_depth`` + +Rien n'est indexé +----------------- + +**Symptôme** : le crawl se termine normalement, mais la recherche ne renvoie aucun +résultat + +**Points à vérifier** : + +1. Vérifiez le journal du crawler pour des erreurs ou avertissements (réglez + ``org.codelibs.fess.ds`` sur ``DEBUG`` dans + ``app/WEB-INF/env/crawler/resources/log4j2.xml``) +2. Vérifiez ``url``, ``site.name`` (ou ``site.path``) et ``site.list_name`` pour des + fautes de frappe - rappelez-vous que ``site.name`` n'est pas nécessaire une fois + ``site.path`` défini +3. Confirmez que l'authentification réussit effectivement (pas de 401) - une requête qui + ne s'authentifie jamais est une cause bien plus fréquente qu'un ``role.skip`` ou + ``default_permissions`` mal configuré +4. Si ``include_pattern`` ou ``exclude_pattern`` est défini, rappelez-vous qu'ils + correspondent à un chemin relatif au serveur (pour un fichier de bibliothèque de + documents ou une pièce jointe d'élément de liste) ou au ``FileRef`` (pour un élément + de liste) - pas à l'URL affichée dans les résultats de recherche. Vérifiez qu'un + motif n'a pas été écrit pour une URL complète +5. Vérifiez si ``supported_mimetypes`` ou ``max_content_length`` exclut les fichiers que + vous attendez de voir +6. Vérifiez si ``site.exclude_list`` ou ``site.exclude_folder`` exclut involontairement + la cible + +Informations de référence +========================= + +- :doc:`ds-overview` - Aperçu des connecteurs Data Store +- :doc:`ds-microsoft365` - Connecteur Microsoft 365 (pour SharePoint Online) +- :doc:`../../admin/dataconfig-guide` - Guide de configuration Data Store +- :doc:`../../admin/plugin-guide` - Guide de gestion des plugins diff --git a/fr/15.9/config/datastore/index.rst b/fr/15.9/config/datastore/index.rst index 5f0b2ee9..9911ec6c 100644 --- a/fr/15.9/config/datastore/index.rst +++ b/fr/15.9/config/datastore/index.rst @@ -25,6 +25,7 @@ Les connecteurs DataStore de |Fess| permettent de récupérer du contenu depuis ds-atlassian ds-slack + ds-sharepoint .. toctree:: :maxdepth: 2 diff --git a/ja/15.9/config/datastore/ds-microsoft365.rst b/ja/15.9/config/datastore/ds-microsoft365.rst index 344ac40c..5cecac77 100644 --- a/ja/15.9/config/datastore/ds-microsoft365.rst +++ b/ja/15.9/config/datastore/ds-microsoft365.rst @@ -10,6 +10,12 @@ Microsoft 365コネクタは、Microsoft 365サービス(OneDrive、OneNote、 この機能には ``fess-ds-microsoft365`` プラグインが必要です。 +.. note:: + + このコネクタが対象とするSharePointはSharePoint Online(Microsoft 365)です。オンプレミス版の + SharePoint Server(2013、2016、2019、Subscription Edition)をクロールする場合は、 + :doc:`ds-sharepoint` を使用してください。 + 対応サービス ============ @@ -653,6 +659,7 @@ PowerShellで確認: ======== - :doc:`ds-overview` - データストアコネクタ概要 +- :doc:`ds-sharepoint` - SharePoint Serverコネクタ(オンプレミス版) - :doc:`ds-gsuite` - Google Workspaceコネクタ - :doc:`../../admin/dataconfig-guide` - データストア設定ガイド - `Microsoft Graph API `_ diff --git a/ja/15.9/config/datastore/ds-overview.rst b/ja/15.9/config/datastore/ds-overview.rst index a7cdd024..b73d8222 100644 --- a/ja/15.9/config/datastore/ds-overview.rst +++ b/ja/15.9/config/datastore/ds-overview.rst @@ -60,6 +60,9 @@ * - :doc:`ds-slack` - fess-ds-slack - Slackのメッセージとファイルをクロール + * - :doc:`ds-sharepoint` + - fess-ds-sharepoint + - オンプレミス版 SharePoint Server をクロール 開発・運用ツール ---------------- @@ -113,9 +116,6 @@ * - コネクタ - プラグイン - 説明 - * - SharePoint - - fess-ds-sharepoint - - SharePointリポジトリからデータを取得(レガシー版) * - Wikipedia - fess-ds-wikipedia - Wikipediaのコンテンツを取得 diff --git a/ja/15.9/config/datastore/ds-sharepoint.rst b/ja/15.9/config/datastore/ds-sharepoint.rst new file mode 100644 index 00000000..7230ed9b --- /dev/null +++ b/ja/15.9/config/datastore/ds-sharepoint.rst @@ -0,0 +1,885 @@ +========================= +SharePoint Serverコネクタ +========================= + +概要 +==== + +SharePoint Serverコネクタは、オンプレミス版の SharePoint Server(2013、2016、2019、Subscription +Edition)が REST/OData API(および 2013 向けの XML/Atom API)で公開するドキュメントライブラリの +ファイルやリストの項目を取得し、 |Fess| のインデックスに登録する機能を提供します。 + +この機能には ``fess-ds-sharepoint`` プラグインが必要です。 + +.. note:: + + SharePoint Online(Microsoft 365)をクロールする場合は、このコネクタではなく + :doc:`ds-microsoft365` を使用してください。このコネクタの OAuth 認証は Azure ACS の + アプリケーション専用認証にのみ対応しており、Microsoft Graph API との連携機能はありません。 + +対応バージョン: SharePoint Server 2013 / 2016 / 2019 / Subscription Edition (SE) + +取得できるコンテンツ +==================== + +- ドキュメントライブラリのファイル +- リストの項目(リストアイテム) +- リスト項目の添付ファイル + +前提条件 +======== + +1. プラグインのインストールが必要です +2. クロールに使用するアカウントに、クロール対象のサイト・リスト・ドキュメントライブラリへの + 読み取り権限が必要です +3. NTLM / Kerberos(SPNEGO)/ OAuth(ACS)のいずれか1つの認証方式を選択し、 + 必要な資格情報を用意してください + +プラグインのインストール +------------------------ + +管理画面の「システム」→「プラグイン」からインストールします: + +1. ``fess-ds-sharepoint-X.X.X.jar`` をダウンロード +2. ``$FESS_HOME/app/WEB-INF/lib`` (または ``/usr/share/fess/app/WEB-INF/lib`` )に配置 +3. |Fess| を再起動 + +または、詳細は :doc:`../../admin/plugin-guide` を参照してください。 + +設定 +==== + +管理画面から「クローラー」→「データストア」→「新規作成」で設定します。 + +基本設定 +-------- + +.. list-table:: + :header-rows: 1 + :widths: 25 75 + + * - 項目 + - 設定例 + * - 名前 + - SharePoint + * - ハンドラ名 + - SharePointDataStore + * - 有効 + - オン + +パラメーター設定 +---------------- + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.doclib_path=/Shared Documents + +パラメーター一覧 +~~~~~~~~~~~~~~~~ + +**URL / サイト** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - パラメーター + - 必須 + - 説明 + * - ``url`` + - はい + - SharePointサーバーのベースURL(例: ``http://sharepoint.example.com/``) + * - ``site.name`` + - 条件付き + - ``/sites//`` 配下のサイトコレクション名。``site.path`` を設定した場合は不要 + * - ``site.path`` + - いいえ + - サイトのサーバー相対マネージドパス(例: ``/teams/eng``、ルートサイトコレクションは ``/``)。 + 設定するとハードコードされた ``/sites/`` プレフィックスの代わりにこの値がそのまま使われ、 + ``site.name`` は不要になる + * - ``site.list_id`` + - いいえ + - GUIDでリストを1つ指定してクロール(リストクロールモード) + * - ``site.list_name`` + - いいえ + - 表示名でリストを1つ指定してクロール(リストクロールモード) + * - ``site.doclib_path`` + - いいえ + - サイト配下のドキュメントライブラリのパス(ドキュメントライブラリクロールモード。例: ``/Shared Documents``) + * - ``site.exclude_list`` + - いいえ + - 除外するリストのエンティティ型名の正規表現(カンマ区切り)。サイト全体クロール時のみ有効 + * - ``site.exclude_folder`` + - いいえ + - 除外するトップレベルフォルダ名の正規表現(カンマ区切り)。サイト全体クロール時のみ有効 + * - ``site.crawl_subsites`` + - いいえ + - サブサイトを再帰的にクロールするか(デフォルト: ``false``)。詳細は + `サブサイトとマネージドパス`_ を参照 + * - ``site.max_depth`` + - いいえ + - ``site.crawl_subsites`` が辿るサブサイトの階層数(デフォルト: ``10``)。ルートを深さ0とする + +**認証** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - パラメーター + - 必須 + - 説明 + * - ``auth.ntlm.user`` + - いいえ + - NTLMユーザー名。設定するとNTLM認証が有効になる(``DOMAIN\user`` 形式可) + * - ``auth.ntlm.password`` + - いいえ + - NTLMパスワード + * - ``auth.ntlm.domain`` + - いいえ + - Windowsドメイン。NTLMの独立したフィールドとして送信される + * - ``auth.ntlm.workstation`` + - いいえ + - NTLMネゴシエーションで送信されるワークステーション名 + * - ``auth.kerberos.principal`` + - いいえ + - クライアントプリンシパル(``user@REALM`` 形式)。設定するとKerberos/SPNEGO認証が有効になる + * - ``auth.kerberos.keytab`` + - いいえ + - プリンシパルの鍵を持つキータブファイルのパス。``auth.kerberos.password`` とは排他 + * - ``auth.kerberos.password`` + - いいえ + - プリンシパルのパスワード。キータブ未設定時のみ使用される + * - ``auth.kerberos.strip_port`` + - いいえ + - サービスプリンシパル名からポート番号を除去するか(デフォルト: ``true``) + * - ``auth.kerberos.use_canonical_hostname`` + - いいえ + - サービスプリンシパル名を組み立てる前に対象ホストを正規名に解決するか(デフォルト: ``false``) + * - ``auth.kerberos.krb5_conf`` + - いいえ + - ``krb5.conf`` のパス。``java.security.krb5.conf`` が未設定の場合のみ適用される + * - ``auth.kerberos.debug`` + - いいえ + - ``Krb5LoginModule`` のデバッグ出力を有効にするか(デフォルト: ``false``) + * - ``auth.oauth.client_id`` + - いいえ + - Azure ACSのアプリケーション専用OAuthクライアントID。設定するとOAuth認証が有効になる + * - ``auth.oauth.client_secret`` + - いいえ + - OAuthクライアントシークレット + * - ``auth.oauth.tenant`` + - いいえ + - テナント名(``.sharepoint.com`` を除いた部分) + * - ``auth.oauth.realm`` + - いいえ + - Azure ADのレルム(ディレクトリID) + +``auth.kerberos.principal`` 、``auth.ntlm.user`` 、``auth.oauth.client_id`` のうち +**設定できるのは1つだけ** です。詳細は `認証`_ を参照してください。 + +**リスト** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - パラメーター + - 必須 + - 説明 + * - ``list.items.number_per_page`` + - いいえ + - ``GetListItems`` のページサイズ(デフォルト: ``100``) + * - ``list.item.content.include_fields`` + - いいえ + - 指定した場合、これらのリスト項目フィールドのみを ``content`` に連結する(カンマ区切り) + * - ``list.item.content.exclude_fields`` + - いいえ + - 組み込みで除外される多数の定型フィールドに加えて ``content`` から除外するフィールド名パターン + (カンマ区切り、各要素は正規表現) + * - ``list.is_sub_page`` + - いいえ + - リスト項目をSitePages/wikiのサブページとして扱うか(デフォルト: ``false``)。ページングの + フォールバックとWebリンクの形式に影響する + +**HTTP** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - パラメーター + - 必須 + - 説明 + * - ``http.connection_timeout`` + - いいえ + - HTTP接続タイムアウト(ミリ秒)。コネクションプール待機のタイムアウトとしても使われる + (デフォルト: ``30000``) + * - ``http.socket_timeout`` + - いいえ + - HTTPソケット(読み取り)タイムアウト(ミリ秒、デフォルト: ``30000``) + * - ``proxy_host`` + - いいえ + - HTTPプロキシホスト + * - ``proxy_port`` + - 条件付き + - HTTPプロキシポート。``proxy_host`` 指定時は必須(デフォルト: ``-1`` = プロキシなし) + +**フィルタリングとコンテンツ** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - パラメーター + - 必須 + - 説明 + * - ``include_pattern`` + - いいえ + - クロール対象とする項目の値が一致すべき正規表現。検索結果に表示されるURLではなく、 + ファイルやリスト項目添付ファイルのサーバー相対パス、リスト項目の場合は ``FileRef`` + と比較される点に注意 + * - ``exclude_pattern`` + - いいえ + - 一致した項目をクロール対象から除外する正規表現 + * - ``supported_mimetypes`` + - いいえ + - クロール対象とするファイルのMIMEタイプが一致すべき正規表現(カンマ区切り、デフォルト: ``.*``) + * - ``max_content_length`` + - いいえ + - クロールするファイルの最大サイズ(バイト)。超過したファイルは失敗ではなくスキップされる + (デフォルト: ``-1`` = 無制限) + * - ``extractor_name`` + - いいえ + - エクストラクタファクトリがMIMEタイプをマッピングできない場合のみ使われるフォールバックの + エクストラクタ(デフォルト: ``tikaExtractor``) + +**動作** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - パラメーター + - 必須 + - 説明 + * - ``sp.version`` + - いいえ + - ``2013`` を指定すると SharePoint 2013向けのXML/Atom、 + ``GetXxxByServerRelativeUrl`` 系APIに切り替わる(未指定時は SharePoint Online / + 2016以降のREST方言) + * - ``retry_limit`` + - いいえ + - SharePointサーバー/クライアント例外発生時のクロール単位あたり最大リトライ回数 + (デフォルト: ``2``) + * - ``role.skip`` + - いいえ + - 項目ごとの権限取得を完全にスキップするか(デフォルト: ``false``)。詳細は `権限`_ を参照 + * - ``ignore_error`` + - いいえ + - ファイルのコンテンツ抽出失敗時に、クロール対象を失敗させる代わりにログを出して + スキップするか(デフォルト: ``false``) + * - ``default_permissions`` + - いいえ + - SharePointから取得した権限に加えて、すべてのドキュメントの権限リストに + マージされるパーミッション文字列(カンマ区切り) + * - ``delete_old_docs`` + - いいえ + - 今回のクロールで再取得されなかったドキュメントを削除するか(コア側デフォルト: ``true``)。 + このプラグインは、いずれかのクロール対象が失敗した場合は今回の実行に限りこの値を + 強制的に ``false`` にする + * - ``number_of_threads`` + - いいえ + - 同時に処理するクロール対象の数(デフォルト: ``1`` = スレッドプールなし)。 + プロセッサ数の2倍が上限。詳細は `並列クロールと負荷`_ を参照 + * - ``script_type`` + - いいえ + - データ設定のスクリプトに使うスクリプトエンジン(デフォルト: ``groovy``) + * - ``readInterval`` + - いいえ + - 連続するクロール結果の間の待機時間(ミリ秒、デフォルト: ``0``)。他のパラメーターと違い + camelCase表記である点に注意 + +スクリプト設定 +-------------- + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + role=role + +利用可能なフィールド +~~~~~~~~~~~~~~~~~~~~ + +.. list-table:: + :header-rows: 1 + :widths: 16 20 32 32 + + * - キー + - リスト項目(ItemCrawl) + - ドキュメントライブラリファイル(FolderCrawl→FileCrawl) + - 添付ファイル(ItemAttachmentsCrawl→FileCrawl) + * - ``url`` + - Webリンク + - ファイルURL + - ファイルURL + * - ``host`` + - ホスト名 + - ホスト名 + - ホスト名 + * - ``site`` + - サーバー相対パス(``FileRef``) + - サーバー相対パス + - サーバー相対パス + * - ``title`` + - ``Title`` フィールド、無ければ ``FileLeafRef``/ファイル名 + - ドキュメントライブラリファイル自身の ``Title`` 値(あれば)、無ければファイル名 + - ファイル名 + * - ``titleWithListName`` + - ``"[リスト名] タイトル"`` + - ``"[リスト名] ファイル名"`` (ドキュメントライブラリクロールではリスト名は常に空なので + 実質ファイル名のみ) + - ``"[リスト名] ファイル名"`` + * - ``listName`` + - リストの表示名、または ``""`` + - 常に ``""`` + - 実際のリスト名 + * - ``content`` + - フィールド値の連結 + - 抽出されたテキスト + - 抽出されたテキスト + * - ``digest`` + - ``content`` の要約 + - ``content`` の要約 + - ``content`` の要約 + * - ``content_length`` + - ``content.length()`` + - ``content.length()`` + - ``content.length()`` + * - ``last_modified`` + - 一覧取得結果から + - 一覧取得結果から + - 一覧取得結果から + * - ``created`` + - 一覧取得結果から + - 一覧取得結果から + - 一覧取得結果から + * - ``mimetype`` + - 常に ``text/html`` + - 検出値 + - 検出値 + * - ``filetype`` + - ``mimetype`` から導出 + - ``mimetype`` から導出 + - ``mimetype`` から導出 + * - ``role`` + - 権限リスト(空でない場合のみ) + - 権限リスト(空でない場合のみ) + - 権限リスト(空でない場合のみ) + * - ``list_name`` + - あり + - **なし** + - あり + * - ``list_id`` + - あり + - **なし** + - あり + * - ``item_id`` + - あり + - **なし** + - あり + +.. note:: + + ``content_length`` は ``content.length()`` 、つまり抽出・連結されたテキストの + 文字数(UTF-16コード単位)であり、ファイルのバイトサイズではありません。Box や + Google Drive、Dropboxコネクタの ``file.size`` (サービス側のメタデータによる実際の + バイトサイズ)とは値の性質が異なるため、両者を比較しないでください。 + +**動的キー: ``val_*``** + +リスト項目の ``FieldValuesAsText`` (SharePointがそのアイテムについて返す生のフィールド値マップ。 +``odata.metadata`` などのOData用メタデータキーを含む)の各キーは、2つの名前で公開されます: +プレフィックスなし(上記の固定キーと同名でない場合のみ)と、常に ``val_`` プレフィックス付きの +両方です。例えば ``Status`` フィールドは ``Status`` と ``val_Status`` の両方になります。 + +``val_*`` キーが存在するのは **リスト項目クロール(ItemCrawl)のみ** です。ドキュメントライブラリ +ファイル(FolderCrawl→FileCrawl)やリスト項目の添付ファイル(ItemAttachmentsCrawl→FileCrawl)では +``val_*`` キーは一切生成されません。 + +認証 +==== + +3つの認証方式があり、**設定できるのはそのうち1つだけ** です。``auth.kerberos.principal`` 、 +``auth.ntlm.user`` 、``auth.oauth.client_id`` のうち2つ以上を設定すると、リクエストが送信される +前にデータ設定ジョブがバリデーションエラーで失敗します。これは意図的な制限です。HTTPクライアントに +登録される資格情報は1つだけであり、その資格情報が登録されるスコープは ``Negotiate`` チャレンジにも +``NTLM`` チャレンジにも同じように一致してしまうため、複数設定するとログからは原因の分からない401が +返るだけになります。 + +NTLM +---- + +:: + + auth.ntlm.user={SharePointユーザー名} + auth.ntlm.password={パスワード} + auth.ntlm.domain={Windowsドメイン。省略可、デフォルトは未設定} + auth.ntlm.workstation={NTLMネゴシエーションで送信するワークステーション名。省略可、デフォルトは未設定} + +``auth.ntlm.domain`` と ``auth.ntlm.workstation`` はどちらもデフォルトで未設定であり、これまでと +同じ資格情報が組み立てられます。ユーザー名に ``DOMAIN\user`` の形でドメインを書き込む方法も +引き続き有効です。``auth.ntlm.domain`` を設定すると、ドメインをNTLMの独立したフィールドとして +送信するようになります。結合形式を拒否するサーバーではこちらを使ってください。 + +Kerberos(SPNEGO) +------------------ + +**サポート範囲は次の構成に限られます:** クローラーJVMは1つ、Fessインスタンスごとに ``krb5.conf`` +は1つ、認証はキータブまたはパスワード、委任(delegation)なし、チャネルバインディングなし、 +NTLM・OAuthとは排他。これ以外の構成はサポート対象外です。 + +:: + + auth.kerberos.principal={クライアントプリンシパル。user@REALM の形式で書く。設定するとKerberosが有効になる} + auth.kerberos.keytab={プリンシパルの鍵を持つキータブファイルのパス。auth.kerberos.password とは排他} + auth.kerberos.password={プリンシパルのパスワード。キータブ未設定時のみ使用} + auth.kerberos.strip_port={true/false。サービスプリンシパル名からポート番号を除去するか。デフォルトtrue} + auth.kerberos.use_canonical_hostname={true/false。対象ホストを正規名に解決してからSPNを組み立てるか。デフォルトfalse} + auth.kerberos.krb5_conf={krb5.confのパス。java.security.krb5.conf が未設定の場合のみ適用} + auth.kerberos.debug={true/false。Krb5LoginModuleのデバッグ出力。デフォルトfalse} + +- **``krb5.conf`` は ``jvm.crawler.options`` に設定します** + (例: ``-Djava.security.krb5.conf=/path/to/krb5.conf``)。データストアのクロールは + クローラーの **子プロセス** で実行されるため、webapp側にしか影響しない設定をしても効果がなく、 + webappの再起動でも反映されません。反映させるにはクロールジョブを再実行する必要があります。 + ``auth.kerberos.krb5_conf`` は、まだ何もこのプロパティを設定していない場合の簡易手段であり、 + **既に設定済みの値を上書きすることは決してありません** (このプロパティはJVM全体で共有され、 + 1つのクローラーJVMがクロールジョブのすべてのデータ設定を実行するため)。上書きしなかった場合は + 両方のパスを記載した警告がログに出力されます。 +- **``krb5.conf`` の ``[libdefaults]`` に ``udp_preference_limit = 1`` を設定してください。** + これがないとJDKはまずUDPで問い合わせ、KDCが応答しない場合(到達不能、ファイアウォールが + UDP 88をドロップしている、応答がデータグラムサイズを超えている、など)にTCPへフォールバックする + 前に30秒間隔で3回リトライします。ログに何も残らないまま認証1回あたり1分半ほどクロールが + 止まって見えるときは、たいていこれが原因です。 +- **プリンシパルは常に ``user@REALM`` の形式で書いてください。** ``default_realm`` はJVM全体で + 共有される設定であり、複数のレルムにまたがる複数のSharePointファームが1つの ``krb5.conf`` を + 共有することもあるため、レルムを省略した ``user`` はそのファイルがたまたま指しているレルムに + 対して解決されてしまいます。 +- **``auth.kerberos.use_canonical_hostname`` はデフォルト ``false``** です。Apache HttpClient + 自身のデフォルトとは意図的に異なります。有効にすると、サービスプリンシパル名を組み立てる前に + 対象ホストが逆引きDNSにかけられ、代替アクセスマッピングやロードバランサー配下では、どのSPNも + 登録されていない名前が生成されてしまい、結果として起こる失敗からはDNSが原因だとまったく + 分かりません。SPNが本当に正規名に対して登録されている場合にのみ有効にしてください。 +- **IIS Extended Protectionが ``tokenChecking=Require`` に設定されている場合は動作しません。** + Apache HttpClientはバージョン4.5系・5.x系のどちらもチャネルバインディングに対応していません。 + IISの既定値は ``None`` なので通常は影響を受けませんが、``Require`` になっている環境に対する + 回避策はありません。 +- **チケットはクロール用のHTTPクライアントを構築する際に一度だけ取得され、以後更新されません。** + チケットの有効期間よりも長く動くクロールは、途中から認証に失敗するようになります。 +- **``auth.kerberos.password`` は、``auth.ntlm.password`` と同様に平文で保存・表示されます。** + Fessにはデータストアハンドラのパラメーターをマスキングする仕組みがなく、データ設定編集画面は + これらをプレーンテキストのテキストエリアとして描画します。可能な場合は + ``auth.kerberos.keytab`` を使い、キータブファイルには制限的なパーミッションを設定してください。 +- ``auth.kerberos.debug=true`` にすると、``Krb5LoginModule`` はFessのログではなく + クローラープロセスの標準出力に書き込みます。 + +OAuth(ACS) +------------ + +:: + + auth.oauth.client_id={OAuthクライアントID} + auth.oauth.client_secret={OAuthクライアントシークレット} + auth.oauth.tenant={テナント名。.sharepoint.com を除いた部分} + auth.oauth.realm={Azure ADのレルム(ディレクトリID)} + +``auth.oauth.client_id`` を設定すると、Windows Azure Access Control Service +(``https://accounts.accesscontrol.windows.net/{realm}/tokens/OAuth/2``)に対する +クライアントクレデンシャル(アプリケーション専用)フローが有効になります。アクセストークンは +クロール用のHTTPクライアント構築時に一度だけ取得され、すべてのリクエストに ``Bearer`` +``Authorization`` ヘッダーとして付与されます。401が返った場合は1回だけ更新して再試行します。 +**MicrosoftはACSを非推奨としており廃止が予定されています。** OAuthを設定したクロールのたびに +この旨の警告がログに出力されます。このプラグインにはEntra IDのアプリ登録(証明書またはクライアント +シークレットによる)フローは実装されておらず、レガシーなACSアプリケーション専用認証のみに +対応しています。 + +OAuthを有効にする判定は ``auth.oauth.client_id`` の有無だけで行われます。``client_secret`` 、 +``tenant`` 、``realm`` は無条件に読み込まれ、省略すると空のまま黙って使われるため、専用の +バリデーションメッセージなしにトークン取得が失敗することがあります。 + +**``sp.version=2013`` とOAuthの組み合わせは一度も機能したことがありません。** このプラグインが +SharePoint 2013向けに行うすべてのAPI呼び出しはXML/Atomクライアントを経由しますが、そのクライアントの +どのコードパスもOAuthトークンをリクエストに付与しません。そのため両方を設定すると、すべての +リクエストが未認証のまま送信されます。クロールはこの事実をそのままログに警告として出力しますが、 +ジョブを失敗させることはありません。SharePoint 2013には ``auth.ntlm.*`` を使用してください。 + +権限 +==== + +``role.skip=true`` (デフォルト ``false``)を設定すると、項目ごとの権限取得を完全にスキップします: +``GetListItemRole`` は一切呼び出されず、項目に ``role`` キーが設定されることもなく、ドキュメントには +データ設定自体の静的な権限設定と、設定していれば ``default_permissions`` だけが適用されます — +SharePoint由来の権限はまったく反映されません。 + +権限を取得する場合、SharePoint自身のユーザー・セキュリティグループ・SharePointグループは展開され、 +Fessの検索用ロールにマッピングされます: + +- **オンプレミスAD** のアカウントやグループ(ログイン名にバックスラッシュを含み、Azureの + クレームプレフィックスで始まらないもの)は、標準のADユーザー/グループ用ロールヘルパーで + マッピングされます。 +- **Azure AD(Entra ID)** のアカウント(ログイン名が ``i:0#.f|membership|`` で始まるもの)は + **2通り** にマッピングされます — Azureクレームの完全な値による1つと、そのクレームの ``@`` より + 前のADアカウント部分による1つで、同じユーザーに対してEntra ID形式とAD形式の両方のロールが + 追加されます。いくつかのクレーム形式プレフィックス(特別な「全員」グループである + ``spo-grid-all-users`` を含む)のいずれかでAzureと判定されたセキュリティグループも、同様に + 両方の形式でマッピングされます。 +- **SharePointグループ** は自身のメンバーシップ(ユーザー、セキュリティグループ、ネストした + グループ)を再帰的に展開します。互いを含み合うグループ間の無限再帰を止めるための、訪問済み + グループのガードも備えています。 + +``default_permissions`` (カンマ区切り)は、上記すべての **後に** マージされます。そのため +SharePointが項目に対して権限を一切返さなかった場合(``role.skip=true`` の場合と「SharePointが +何も返さなかった」場合の両方に該当)でも適用されます。最終的な権限リストは、データ設定自体の +静的な権限設定・SharePoint由来のロール(``role.skip`` していない場合)・``default_permissions`` +の和集合を重複排除したものになります。 + +サブサイトとマネージドパス +========================== + +``site.path`` を設定すると、ハードコードされた ``/sites/`` プレフィックスの代わりにサーバー相対 +マネージドパスがそのまま使われ、``site.name`` は不要になります。 + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - シナリオ + - 設定 + * - ルートサイトコレクション + - ``site.path=/`` + * - ``/teams/eng`` サイト + - ``site.path=/teams/eng`` + * - 従来どおり ``/sites/mysite/`` + - ``site.name=mysite`` (``site.path`` は設定しない) + +``site.crawl_subsites`` (デフォルト ``false``)を有効にすると、サイト全体クロール( +``site.list_name`` も ``site.doclib_path`` も設定していないクロール)が、``_api/web/webinfos`` +で見つかったサブサイトへ再帰的に展開されます。未設定のままであれば、クロールがこれまでどおり +まったく同じリクエストしか出さないこと(``webinfos`` すら一度も要求しないこと)は保証されています。 + +サブサイトのドキュメントは、ルートサイトのものと同じデータ設定内に、それぞれのサーバー相対パスの +もとでインデックス登録されます。あるドキュメントがサブサイト由来かルートサイト由来かをインデックス側 +から区別する情報はありません。 + +``site.max_depth`` (デフォルト ``10``)は、``site.crawl_subsites=true`` のときにルートサイトから +何階層下のサブサイトまで辿るかを制限します。ルートサイト自身が深さ0なので、``site.max_depth=1`` は +ルートの直接の子サイトまでしか辿りません。``site.crawl_subsites=true`` のまま ``site.max_depth`` に +``1`` 未満を設定すると、この機能は実質的に無効化され(サブサイトは一切クロールされません)、 +クロール開始時に警告としてログに出力されます。 + +サブサイトを有効にすると、発見されたサブサイト数にほぼ比例して(``site.max_depth`` を上限として) +**クロール全体の所要時間が増大します** — サブサイトごとに、フォルダ一覧・リスト一覧の取得、 +そして深さの上限に達していなければ ``webinfos`` の呼び出しが、ルートサイトのクロールに +加えて発生するためです。 + +例に示したとおり、`並列クロールと負荷`_ の節で説明する ``number_of_threads`` や ``readInterval`` は +サブサイトを含むクロール全体にも同様に適用されます。 + +並列クロールと負荷 +================== + +``number_of_threads`` (デフォルト ``1``)は、同時に処理するクロール対象の数です。デフォルトの +ままではこれまでどおり、すべての対象がクロールスレッド上で処理され、**スレッドプールはまったく +作成されません。** + +この値は、Fessを実行しているマシンの **プロセッサ数の2倍を上限** としてキャップされます。データ +設定がホストの処理能力を超える並列度を要求できないようにするためです。``1`` 未満の値や、空・ +解析不能な値は、尊重されたり失敗させられたりするのではなく ``1`` にフォールバックします。 +キャップされた場合や ``1`` 未満だった場合は要求値と実際の値の両方がログに出力され、解析できない +場合は警告がログに出力されます。**空欄の場合は何もログに出力されません** — 空欄はそのパラメーターが +単に設定されていないことを意味するためです。 + +HTTPコネクションプールはこの値に連動してサイズが調整されます。Apache HttpClientは既定では1ルート +あたり2コネクションしか許可せず、このクロールは丸ごと1つのルートとして扱われるため、コネクション +プールを大きくしなければ3番目以降のスレッドはリクエストを送る代わりにコネクション待ちに費やす +時間の方が長くなってしまいます。 + +**``readInterval`` は、この値を何に設定しても、ドキュメントの受け渡しを1件ずつのペースで +制御し続けます。** スレッドはクロールの発見・取得を速くしますが、ドキュメントがインデクサーに +届く速さを速くするわけではありません。これは意図的な設計です — 運用者が設定した間隔をスレッド数で +割ってしまうと、その間隔で制限しようとしていた負荷そのものを逆に増幅させてしまうためです。 +1件のドキュメント処理を終えたワーカーは、それより前のドキュメントがまだ受け渡し中であれば、 +ただ待機します。 + +``number_of_threads`` を上げることで実際に増えるのは、**SharePointに対するリクエストの +レート** です。後述の503バックオフと ``X-SharePointHealthScore`` による待機は、クロール対象 +ごとに、それをクロールしているスレッド上で適用されるため、``n`` 個のスレッドは単一スレッドの +クロールの最大 ``n`` 倍のリクエストを送ることになります — ファームが「今は忙しい」と伝えている +最中も含めてです。オンプレミスファームに対しては、この値は段階的に上げてください。 + +スレッド数を増やしても効果に上限がある理由が2つあります: + +- **各SharePointグループのメンバーシップは、最初に読み込まれるときだけ1スレッドずつ順番に + 読み込まれます。** 権限はクロール全体で共有されるキャッシュを通じて解決され、そのキャッシュは + グループのメンバー取得の間ロックで保護されます。このロックがあるおかげで、あるスレッドが + 「メンバーがまだ読み込み中のグループ」を別のスレッドに渡してしまい、そのグループが保護している + 項目を権限抜きでインデックスしてしまう事態を防いでいます。一度キャッシュに載ったグループへの + 以降の参照は安価な参照になるため、これは **コールドキャッシュのコスト** です — 異なるグループが + 多いサイトのクロールは序盤が単一スレッドに近い速度になり、少数のグループを項目が共有している + サイトではほとんど影響がありません。権限をまったく読まない ``role.skip=true`` はこのコストを + 完全に回避します。 +- サイトごとの発見処理は逐次的です。1サイトのフォルダ一覧とリスト一覧は1つのクロール対象なので、 + その対象の処理が終わって発見結果がキューに入るまで、スレッド間で分担する作業がありません。 + +**503応答** は他のエラーと同様に ``retry_limit`` 回までリトライされますが、リトライのたびに +待機時間が増加します: 2秒、4秒、8秒と30秒を上限に倍増し、それぞれ実際の待機時間はその値の +70〜129%でランダム化されます。503を返し続けるクロール対象は、実際にリトライが行われるたびに +この待機を払いますが、最後のリトライの後には払いません — ``retry_limit`` に達して結局 +諦められる対象を、無意味に遅延させることはしません。 + +**すべてのレスポンス** — 成功・失敗を問わず、クロールが破棄しようとしている一覧のページも含めて +— は ``X-SharePointHealthScore`` レスポンスヘッダー(0がアイドル、10が非常に忙しい)を +チェックされます。スコアが9以上になると、クロールは次の処理を行う前に待機します: スコア9は +上記の最初の503リトライと同じ約2秒、スコア10は約4秒、以降9を超えるごとに倍増します。**これには +上限がなく、クロール全体を通じて積み重なります** — 継続的に高負荷なファームがヘルススコア9で +張り付いていると、このデータストアが送る **リクエスト1件ごとに** 約2秒が加算され続け +(フォルダ一覧・リスト一覧の1ページごとも含めて)、本来なら数時間で終わるはずのクロールが +桁違いに長くなることがあります。クロールが予想外に大幅に遅くなった場合は、まず何よりも先に +その時間帯のファームのヘルススコアを確認してください。 + +設定例 +====== + +いずれもNTLM認証を前提とした例です。Kerberos・OAuthを使う場合は `認証`_ を参照して +``auth.ntlm.*`` の行を置き換えてください。 + +リストのクロール +---------------- + +パラメーター: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.list_name=Tasks + +スクリプト: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +ドキュメントライブラリのクロール +-------------------------------- + +パラメーター: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.doclib_path=/Shared Documents + +スクリプト: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +``/teams/`` サイトのクロール +---------------------------- + +``site.path`` を使うと、``/sites/`` 以外のマネージドパスにあるサイトのドキュメントライブラリを +直接指定できます。 + +パラメーター: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.path=/teams/eng + site.doclib_path=/Shared Documents + +スクリプト: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +サブサイト再帰クロール +---------------------- + +ルートサイトコレクションを起点に、深さ3階層までサブサイトを辿ります。 + +パラメーター: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.path=/ + site.crawl_subsites=true + site.max_depth=3 + +スクリプト: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + role=role + +制限事項 +======== + +- **増分クロール・差分クロールには一切対応していません。** 変更トークンや差分クエリ、 + 「前回以降に更新された項目だけ」のフィルタリングはこのプラグインのどこにも存在せず、 + 実行のたびに設定されたすべてのリスト・フォルダ・ファイルを完全に一覧取得します。 + ``delete_old_docs`` は、今回の完全クロールで再び見つからなかったドキュメントを削除するか + どうかを制御するだけの後始末であり、差分取得ではありません。 +- **ファイル名・フォルダ名中の ``%`` と ``#``** は、デフォルト(``2013`` 以外)の + コードパスでサポートされています。この2文字をファイル名・フォルダ名に使えるのは + SharePoint Server 2019とSubscription Editionだけで、2016は明示的に拒否し、2013も拒否します。 + デフォルトのコードパスは、デコード済みパスを受け取る ``...ByServerRelativePath(decodedUrl=...)`` + 系エンドポイントでこうしたファイルにアクセスし、インデックスに登録するリンクの中でも + この2文字をエスケープします。**``sp.version=2013`` ではこうしたファイルにアクセスできません。** + 古い ``...ByServerRelativeUrl(...)`` 系エンドポイントを使い、これは引数をエンコード済みURLとして + 解釈するためです。これは不具合ではなく意図的な制限です。SharePoint 2013のファーム自体が + そうした名前を保持できないため、問題になるのは ``sp.version=2013`` を2019や + Subscription Editionのサーバーに対して使った場合だけであり、その組み合わせは推奨されません。 + 詳細は `Use of # and % characters in file and folder names + `__ + と `File names - expanded support for special characters + `__ + を参照してください。 +- **IIS Extended Protectionが ``tokenChecking=Require`` の環境ではサポートできません。** + Apache HttpClientはバージョン4.5系・5.x系のどちらもチャネルバインディングを実装していません。 + IISの既定値は ``None`` なのでほとんどのファームは影響を受けませんが、``Require`` に設定された + ファームに対する回避策はありません。 +- **データ設定パラメーターに書いたパスワードは平文で保存・表示されます。** これは + ``auth.ntlm.password`` と ``auth.kerberos.password`` の両方に当てはまります。Fessには + データストアハンドラのパラメーターをマスキングする仕組みがなく、データ設定編集画面はこれらを + プレーンテキストのテキストエリアとして描画します。Kerberosが使える環境では + ``auth.kerberos.password`` より ``auth.kerberos.keytab`` を優先し、キータブファイルには + 制限的なパーミッションを設定してください。 +- **``sp.version=2013`` とOAuthの組み合わせは一度も機能したことがありません。** SharePoint + 2013向けのすべてのAPI呼び出しはXML/Atomクライアントを経由しますが、そのクライアントのどの + コードパスもOAuthトークンをリクエストに付与しないため、両方を設定するとすべてのリクエストが + 未認証のまま送信されます。SharePoint 2013には ``auth.ntlm.*`` を使用してください。 +- **``/sites/`` および ``site.path`` で設定した1つのマネージドパス以外は、自動的には + 発見されません。** ``site.crawl_subsites`` は設定したルートサイトからの再帰のみを行い、 + ``site.path`` はそこで設定した1つのマネージドパスだけに到達します。ファーム上の + すべてのマネージドパスを網羅するものではありません。 + +トラブルシューティング +====================== + +認証が無言で失敗する場合 +------------------------ + +**症状**: 401などが返るが、ログにはっきりした原因が出ない + +**確認事項**: + +1. ``auth.kerberos.principal`` 、``auth.ntlm.user`` 、``auth.oauth.client_id`` のうち + 複数を設定していないか確認する(2つ以上設定するとジョブ開始前にバリデーションエラーになる) +2. Kerberosを使う場合、``jvm.crawler.options`` に + ``-Djava.security.krb5.conf=...`` が設定されているか確認する。webapp側にしか設定していないと + 反映されない。設定変更後はクロールジョブを再実行する(webappの再起動では反映されない) +3. Kerberosの ``krb5.conf`` の ``[libdefaults]`` に ``udp_preference_limit = 1`` が + 設定されているか確認する。無い場合、KDCが無応答だと認証1回あたり約90秒 + (30秒×3回のUDPリトライ)ハングしたように見えることがある +4. プリンシパルが ``user@REALM`` 形式で書かれているか確認する(レルムを省略すると + ``krb5.conf`` の ``default_realm`` に依存してしまう) +5. OAuthの場合、``client_secret`` / ``tenant`` / ``realm`` が空でないか確認する + (存在チェックは ``client_id`` にしか行われないため、他が空でも起動時エラーにならない) +6. IIS側のExtended Protectionが ``tokenChecking=Require`` になっていないか確認する + (この場合は回避策がない) +7. 長時間動作するクロールでは、Kerberosチケットの有効期間が途中で切れ、後半だけ認証に + 失敗することがある(チケットはHTTPクライアント構築時に一度だけ取得され、更新されない) + +クロールが遅い場合(503とヘルススコア) +--------------------------------------- + +**症状**: クロールが予想より大幅に遅い、またはタイムアウトする + +**確認事項**: + +1. その時間帯のSharePointファームの ``X-SharePointHealthScore`` を確認する。9以上になると + リクエストのたびに待機が発生し(9で約2秒、10で約4秒、以降倍増、合計に上限なし)、 + 高負荷なファームではクロール全体が桁違いに長くなることがある +2. 503応答が続いていないか確認する。503は ``retry_limit`` 回までリトライされ、 + そのたびに2秒→4秒→8秒(上限30秒)の待機が入る +3. ``number_of_threads`` を上げすぎていないか確認する。スレッド数はSharePointへの + リクエスト数にほぼそのまま比例するため、ヘルススコアの悪化を招くことがある。 + オンプレミスファームに対しては段階的に増やす +4. ``site.crawl_subsites=true`` の場合、サブサイト数にほぼ比例してクロール全体の + 所要時間が伸びる。``site.max_depth`` で範囲を絞れないか検討する + +インデックスが0件の場合 +----------------------- + +**症状**: クロールは正常終了するが検索結果が0件 + +**確認事項**: + +1. クローラーのログ(``app/WEB-INF/env/crawler/resources/log4j2.xml`` で + ``org.codelibs.fess.ds`` を ``DEBUG`` にして確認)にエラーや警告が出ていないか確認する +2. ``url`` / ``site.name`` (または ``site.path``)/ ``site.list_name`` などの + パラメーターにタイプミスがないか確認する。``site.path`` を設定した場合は ``site.name`` は + 不要になる点に注意する +3. 認証が実際に成功しているか確認する(401が返っていないか)。``role.skip`` や + ``default_permissions`` の設定ミスより、そもそもリクエストが認証されていないケースが多い +4. ``include_pattern`` / ``exclude_pattern`` を設定している場合は、これらが検索結果に表示 + されるURLではなく、サーバー相対パス(ドキュメントライブラリのファイルやリスト項目添付 + ファイルの場合)または ``FileRef`` (リスト項目の場合)に対してマッチする点を確認する。 + フルURLを想定したパターンになっていないか見直す +5. ``supported_mimetypes`` や ``max_content_length`` の設定で対象ファイルが除外されて + いないか確認する +6. ``site.exclude_list`` / ``site.exclude_folder`` の正規表現が意図せず対象を除外して + いないか確認する + +参考情報 +======== + +- :doc:`ds-overview` - データストアコネクタ概要 +- :doc:`ds-microsoft365` - Microsoft 365コネクタ(SharePoint Online向け) +- :doc:`../../admin/dataconfig-guide` - データストア設定ガイド +- :doc:`../../admin/plugin-guide` - プラグイン管理ガイド diff --git a/ja/15.9/config/datastore/index.rst b/ja/15.9/config/datastore/index.rst index e6b2feca..46eae749 100644 --- a/ja/15.9/config/datastore/index.rst +++ b/ja/15.9/config/datastore/index.rst @@ -25,6 +25,7 @@ ds-atlassian ds-slack + ds-sharepoint .. toctree:: :maxdepth: 2 diff --git a/ko/15.9/config/datastore/ds-overview.rst b/ko/15.9/config/datastore/ds-overview.rst index 2d9e4ea1..30479720 100644 --- a/ko/15.9/config/datastore/ds-overview.rst +++ b/ko/15.9/config/datastore/ds-overview.rst @@ -60,6 +60,9 @@ * - :doc:`ds-slack` - fess-ds-slack - Slack의 메시지 및 파일을 크롤링 + * - :doc:`ds-sharepoint` + - fess-ds-sharepoint + - 온프레미스 SharePoint Server 사이트를 크롤링 개발·운영 도구 ---------------- diff --git a/ko/15.9/config/datastore/ds-sharepoint.rst b/ko/15.9/config/datastore/ds-sharepoint.rst new file mode 100644 index 00000000..3d909e31 --- /dev/null +++ b/ko/15.9/config/datastore/ds-sharepoint.rst @@ -0,0 +1,923 @@ +======================== +SharePoint Server 커넥터 +======================== + + +개요 +==== + +SharePoint Server 커넥터는 온프레미스 **SharePoint Server** (2013, 2016, 2019, Subscription +Edition)가 REST/OData API(2013의 경우 XML/Atom API)로 제공하는 문서 라이브러리 파일과 목록 +항목을 가져와 |Fess| 인덱스에 등록하는 기능을 제공합니다. + +이 기능을 사용하려면 ``fess-ds-sharepoint`` 플러그인이 필요합니다. + +.. note:: + + SharePoint Online(Microsoft 365)을 크롤링하려는 경우에는 이 커넥터가 아니라 + :doc:`ds-microsoft365` 를 사용하세요. 이 커넥터의 OAuth 인증은 Azure ACS의 애플리케이션 + 전용 인증에만 대응하며, Microsoft Graph API와의 연동 기능은 없습니다. + +지원 버전: SharePoint Server 2013 / 2016 / 2019 / Subscription Edition(SE) + +지원 콘텐츠 +=========== + +- 문서 라이브러리 파일 +- 목록 항목 +- 목록 항목의 첨부 파일 + +전제 조건 +========= + +1. 플러그인 설치가 필요합니다 +2. 크롤링에 사용하는 계정에 크롤링 대상 사이트·목록·문서 라이브러리에 대한 읽기 권한이 + 필요합니다 +3. NTLM, Kerberos(SPNEGO), OAuth(ACS) 중 정확히 하나의 인증 방식을 선택하고 해당 자격 + 증명을 준비해 두어야 합니다 + +플러그인 설치 +------------- + +관리 화면의 "시스템" → "플러그인"에서 설치합니다: + +1. ``fess-ds-sharepoint-X.X.X.jar`` 다운로드 +2. ``$FESS_HOME/app/WEB-INF/lib`` (또는 ``/usr/share/fess/app/WEB-INF/lib``)에 배치 +3. |Fess| 재시작 + +자세한 내용은 :doc:`../../admin/plugin-guide` 를 참조하세요. + +설정 방법 +========= + +관리 화면에서 "크롤러" → "데이터 스토어" → "새로 만들기"에서 이 커넥터를 설정합니다. + +기본 설정 +--------- + +.. list-table:: + :header-rows: 1 + :widths: 25 75 + + * - 항목 + - 설정 예 + * - 이름 + - SharePoint + * - 핸들러 이름 + - SharePointDataStore + * - 활성화 + - 켬 + +파라미터 설정 +------------- + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.doclib_path=/Shared Documents + +파라미터 목록 +~~~~~~~~~~~~~ + +**URL / 사이트** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - 파라미터 + - 필수 + - 설명 + * - ``url`` + - 예 + - SharePoint 서버의 기본 URL(예: ``http://sharepoint.example.com/``) + * - ``site.name`` + - 조건부 + - ``/sites//`` 하위에서 크롤링할 사이트 모음 이름. ``site.path`` 를 설정한 + 경우 불필요 + * - ``site.path`` + - 아니오 + - 사이트의 서버 상대 관리되는 경로(예: ``/teams/eng``. 루트 사이트 모음은 ``/`` 를 + 사용). 설정하면 하드코딩된 ``/sites/`` 프리픽스 대신 이 값이 그대로 사용되며, + ``site.name`` 은 더 이상 필요하지 않음 + * - ``site.list_id`` + - 아니오 + - GUID로 목록 하나를 지정해 크롤링(목록 크롤 모드) + * - ``site.list_name`` + - 아니오 + - 표시 이름으로 목록 하나를 지정해 크롤링(목록 크롤 모드) + * - ``site.doclib_path`` + - 아니오 + - 사이트 하위의 문서 라이브러리 경로(문서 라이브러리 크롤 모드, 예: + ``/Shared Documents``) + * - ``site.exclude_list`` + - 아니오 + - 제외할 목록 엔터티 타입 이름의 정규식 패턴(쉼표 구분). 사이트 전체 크롤링에만 + 적용됨 + * - ``site.exclude_folder`` + - 아니오 + - 제외할 최상위 폴더 이름의 정규식 패턴(쉼표 구분). 사이트 전체 크롤링에만 적용됨 + * - ``site.crawl_subsites`` + - 아니오 + - 사이트의 하위 사이트까지 재귀적으로 크롤링할지 여부(기본값: ``false``). 자세한 + 내용은 `하위 사이트와 관리되는 경로`_ 참조 + * - ``site.max_depth`` + - 아니오 + - ``site.crawl_subsites`` 가 재귀적으로 탐색할 수 있는 하위 사이트 단계 수 + (기본값: ``10``). 루트는 깊이 0 + +**인증** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - 파라미터 + - 필수 + - 설명 + * - ``auth.ntlm.user`` + - 아니오 + - NTLM 사용자 이름. 설정하면 NTLM 인증이 활성화됨(``DOMAIN\user`` 형식 가능) + * - ``auth.ntlm.password`` + - 아니오 + - NTLM 비밀번호 + * - ``auth.ntlm.domain`` + - 아니오 + - Windows 도메인. NTLM의 독립된 필드로 전송됨 + * - ``auth.ntlm.workstation`` + - 아니오 + - NTLM 협상 과정에서 전송되는 워크스테이션 이름 + * - ``auth.kerberos.principal`` + - 아니오 + - 클라이언트 프린시펄(``user@REALM`` 형식). 설정하면 Kerberos/SPNEGO 인증이 + 활성화됨 + * - ``auth.kerberos.keytab`` + - 아니오 + - 프린시펄의 키를 담은 키탭 파일 경로. ``auth.kerberos.password`` 와는 배타적 + * - ``auth.kerberos.password`` + - 아니오 + - 프린시펄의 비밀번호. 키탭이 설정되지 않은 경우에만 사용됨 + * - ``auth.kerberos.strip_port`` + - 아니오 + - 서비스 프린시펄 이름에서 포트를 제거할지 여부(기본값: ``true``) + * - ``auth.kerberos.use_canonical_hostname`` + - 아니오 + - 서비스 프린시펄 이름을 만들기 전에 대상 호스트를 정규 이름으로 해석할지 여부 + (기본값: ``false``) + * - ``auth.kerberos.krb5_conf`` + - 아니오 + - ``krb5.conf`` 경로. ``java.security.krb5.conf`` 가 아직 설정되지 않은 경우에만 + 적용됨 + * - ``auth.kerberos.debug`` + - 아니오 + - ``Krb5LoginModule`` 의 디버그 출력을 활성화할지 여부(기본값: ``false``) + * - ``auth.oauth.client_id`` + - 아니오 + - Azure ACS 애플리케이션 전용 OAuth 클라이언트 ID. 설정하면 OAuth 인증이 활성화됨 + * - ``auth.oauth.client_secret`` + - 아니오 + - OAuth 클라이언트 시크릿 + * - ``auth.oauth.tenant`` + - 아니오 + - 테넌트 이름(``.sharepoint.com`` 제외) + * - ``auth.oauth.realm`` + - 아니오 + - Azure AD 렐름/디렉터리 ID + +``auth.kerberos.principal``, ``auth.ntlm.user``, ``auth.oauth.client_id`` 중 **정확히 +하나만** 설정할 수 있습니다. 자세한 내용은 `인증`_ 을 참조하세요. + +**목록** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - 파라미터 + - 필수 + - 설명 + * - ``list.items.number_per_page`` + - 아니오 + - ``GetListItems`` 의 페이지 크기(기본값: ``100``) + * - ``list.item.content.include_fields`` + - 아니오 + - 필드 이름(쉼표 구분). 설정하면 이 목록 항목 필드들만 ``content`` 에 연결됨 + * - ``list.item.content.exclude_fields`` + - 아니오 + - 필드 이름 패턴(쉼표 구분, 각 요소는 정규식으로 처리됨). 내장된 다수의 표준 + 필드에 더해 ``content`` 에서 제외됨 + * - ``list.is_sub_page`` + - 아니오 + - 목록 항목을 SitePages/wiki 하위 페이지로 취급할지 여부. 페이징 폴백과 웹 링크 + 형식에 영향을 줌(기본값: ``false``) + +**HTTP** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - 파라미터 + - 필수 + - 설명 + * - ``http.connection_timeout`` + - 아니오 + - HTTP 연결 타임아웃(밀리초). 커넥션 풀 대기 타임아웃으로도 사용됨 + (기본값: ``30000``) + * - ``http.socket_timeout`` + - 아니오 + - HTTP 소켓(읽기) 타임아웃(밀리초, 기본값: ``30000``) + * - ``proxy_host`` + - 아니오 + - HTTP 프록시 호스트 + * - ``proxy_port`` + - 조건부 + - HTTP 프록시 포트. ``proxy_host`` 를 설정한 경우 필수(기본값: ``-1`` = 프록시 없음) + +**필터링과 콘텐츠** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - 파라미터 + - 필수 + - 설명 + * - ``include_pattern`` + - 아니오 + - 크롤링 대상이 되려면 항목의 값이 일치해야 하는 정규식. 여기서 말하는 "값"이 + 무엇인지는 이 표 아래의 참고 사항을 확인 + * - ``exclude_pattern`` + - 아니오 + - 일치하는 항목을 크롤링 대상에서 제외하는 정규식 + * - ``supported_mimetypes`` + - 아니오 + - 파일의 MIME 타입이 하나 이상 일치해야 하는 정규식(쉼표 구분, 기본값: ``.*``) + * - ``max_content_length`` + - 아니오 + - 파일의 최대 크기(바이트). 초과한 파일은 실패가 아니라 스킵됨 + (기본값: ``-1`` = 무제한) + * - ``extractor_name`` + - 아니오 + - 익스트랙터 팩토리가 매핑하지 못하는 MIME 타입에만 사용되는 폴백 익스트랙터 + (기본값: ``tikaExtractor``) + +**동작** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - 파라미터 + - 필수 + - 설명 + * - ``sp.version`` + - 아니오 + - ``2013`` 을 지정하면 SharePoint 2013용 XML/Atom, + ``GetXxxByServerRelativeUrl`` 계열 API로 전환됨(미설정 시 SharePoint Online / + 2016 이후 REST 방식) + * - ``retry_limit`` + - 아니오 + - SharePoint 서버/클라이언트 예외 발생 시 크롤링 단위당 최대 재시도 횟수 + (기본값: ``2``) + * - ``role.skip`` + - 아니오 + - 항목별 권한 가져오기를 완전히 건너뛸지 여부(기본값: ``false``). 자세한 내용은 + `권한`_ 참조 + * - ``ignore_error`` + - 아니오 + - 파일의 콘텐츠 추출에 실패했을 때 크롤링 대상을 실패시키는 대신 로그를 남기고 + 건너뛸지 여부(기본값: ``false``) + * - ``default_permissions`` + - 아니오 + - SharePoint가 반환한 권한에 더해, 모든 문서의 권한 목록에 병합할 권한 문자열 + (쉼표 구분) + * - ``delete_old_docs`` + - 아니오 + - 이번 실행에서 다시 가져오지 못한 문서를 삭제할지 여부(코어 기본값: ``true``). + 이 플러그인은 크롤링 대상 중 하나라도 실패하면 이번 실행에 한해 이 값을 강제로 + ``false`` 로 설정함 + * - ``number_of_threads`` + - 아니오 + - 동시에 처리할 크롤링 대상 수(기본값: ``1`` = 스레드 풀 없음). 프로세서 수의 + 2배가 상한. 자세한 내용은 `병렬 크롤링과 부하`_ 참조 + * - ``script_type`` + - 아니오 + - 데이터 설정 스크립트에 사용할 스크립트 엔진(기본값: ``groovy``) + * - ``readInterval`` + - 아니오 + - 연속된 크롤링 결과 사이의 대기 시간(밀리초, 기본값: ``0``). 다른 파라미터와 + 달리 camelCase 표기임에 유의 + +스크립트 설정 +------------- + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + role=role + +사용 가능한 필드 +~~~~~~~~~~~~~~~~ + +.. list-table:: + :header-rows: 1 + :widths: 16 20 32 32 + + * - 키 + - 목록 항목(ItemCrawl) + - 문서 라이브러리 파일(FolderCrawl→FileCrawl) + - 첨부 파일(ItemAttachmentsCrawl→FileCrawl) + * - ``url`` + - 웹 링크 + - 파일 URL + - 파일 URL + * - ``host`` + - 호스트 이름 + - 호스트 이름 + - 호스트 이름 + * - ``site`` + - 서버 상대 경로(``FileRef``) + - 서버 상대 경로 + - 서버 상대 경로 + * - ``title`` + - ``Title`` 필드, 없으면 ``FileLeafRef``/파일명 + - 문서 라이브러리 파일 자체의 ``Title`` 목록 값(있는 경우), 없으면 파일명 + - 파일명 + * - ``titleWithListName`` + - ``"[목록명] 제목"`` + - ``"[목록명] 파일명"`` (문서 라이브러리 크롤에서는 목록명이 항상 비어 있으므로 + 실질적으로 파일명만 표시됨) + - ``"[목록명] 파일명"`` + * - ``listName`` + - 목록 표시 이름, 또는 ``""`` + - 항상 ``""`` + - 실제 목록 이름 + * - ``content`` + - 필드 값의 연결 + - 추출된 텍스트 + - 추출된 텍스트 + * - ``digest`` + - ``content`` 의 요약 + - ``content`` 의 요약 + - ``content`` 의 요약 + * - ``content_length`` + - ``content.length()`` + - ``content.length()`` + - ``content.length()`` + * - ``last_modified`` + - 목록 조회 결과에서 가져옴 + - 목록 조회 결과에서 가져옴 + - 목록 조회 결과에서 가져옴 + * - ``created`` + - 목록 조회 결과에서 가져옴 + - 목록 조회 결과에서 가져옴 + - 목록 조회 결과에서 가져옴 + * - ``mimetype`` + - 항상 ``text/html`` + - 감지된 값 + - 감지된 값 + * - ``filetype`` + - ``mimetype`` 에서 파생 + - ``mimetype`` 에서 파생 + - ``mimetype`` 에서 파생 + * - ``role`` + - 권한 목록(비어 있지 않은 경우에만) + - 권한 목록(비어 있지 않은 경우에만) + - 권한 목록(비어 있지 않은 경우에만) + * - ``list_name`` + - 있음 + - **없음** + - 있음 + * - ``list_id`` + - 있음 + - **없음** + - 있음 + * - ``item_id`` + - 있음 + - **없음** + - 있음 + +.. note:: + + ``content_length`` 는 ``content.length()``, 즉 추출·연결된 텍스트의 문자 수 + (UTF-16 코드 단위)이며 파일의 바이트 크기가 아닙니다. 이는 Box, Google Drive, + Dropbox 커넥터의 ``file.size`` (각 서비스 자체의 파일 메타데이터에서 가져오는 실제 + 바이트 크기)와는 값의 성격이 다르므로, 이 커넥터의 ``content_length`` 를 그것들과 + 비교하지 마세요. + +**동적 키: ``val_*``** + +목록 항목의 ``FieldValuesAsText`` (SharePoint가 해당 항목에 대해 반환하는 원시 필드 값 +맵으로, ``odata.metadata`` 등 OData 메타데이터 키도 포함됨)의 각 키는 두 가지 이름으로 +노출됩니다: 프리픽스 없이(위의 고정 키와 이름이 겹치지 않는 경우에만) 한 번, 그리고 +무조건 ``val_`` 프리픽스를 붙여서 한 번입니다. 예를 들어 ``Status`` 필드는 ``Status`` +와 ``val_Status`` 양쪽 모두로 노출됩니다. + +``val_*`` 키는 **목록 항목 크롤(ItemCrawl) 경로에서만** 존재합니다. 문서 라이브러리 +파일(FolderCrawl→FileCrawl)이나 목록 항목의 첨부 파일(ItemAttachmentsCrawl→FileCrawl) +에서는 ``val_*`` 키가 전혀 생성되지 않습니다. + +인증 +==== + +인증 방식은 3가지가 있으며, **설정할 수 있는 것은 그중 하나뿐입니다**. +``auth.kerberos.principal``, ``auth.ntlm.user``, ``auth.oauth.client_id`` 중 2개 이상을 +설정하면, 어떤 요청도 보내지기 전에 데이터 설정 잡이 유효성 검사 오류로 실패합니다. 이는 +의도된 동작입니다: HTTP 클라이언트에 등록되는 자격 증명은 하나뿐이며, 그 자격 증명이 +등록되는 스코프는 ``Negotiate`` 챌린지에도 ``NTLM`` 챌린지에도 똑같이 일치해 버리기 +때문에, 여러 개를 설정하면 로그만 봐서는 원인을 알 수 없는 401이 반환될 뿐입니다. + +NTLM +---- + +:: + + auth.ntlm.user={SharePoint 사용자 이름} + auth.ntlm.password={비밀번호} + auth.ntlm.domain={Windows 도메인. 선택 사항, 기본값은 미설정} + auth.ntlm.workstation={NTLM 협상에서 전송할 워크스테이션 이름. 선택 사항, 기본값은 미설정} + +``auth.ntlm.domain`` 과 ``auth.ntlm.workstation`` 은 둘 다 기본값이 미설정이며, 이는 이 +커넥터가 지금까지 항상 만들어 온 것과 정확히 같은 자격 증명을 구성합니다. 사용자 이름에 +``DOMAIN\user`` 형식으로 도메인을 적어 넣는 방법도 계속 사용할 수 있습니다. +``auth.ntlm.domain`` 을 설정하면 도메인을 NTLM의 독립된 필드로 전송하게 되며, 이는 +결합된 형식을 거부하는 서버에서 필요로 하는 방식입니다. + +Kerberos(SPNEGO) +---------------- + +**지원 범위:** 크롤러 JVM 1개, Fess 인스턴스당 ``krb5.conf`` 1개, 키탭 또는 비밀번호, +위임(delegation) 없음, 채널 바인딩 없음, NTLM·OAuth와는 배타적 관계. 이 범위를 벗어나는 +구성은 지원되지 않습니다. + +:: + + auth.kerberos.principal={클라이언트 프린시펄. user@REALM 형식으로 작성. 설정하면 Kerberos가 활성화됨} + auth.kerberos.keytab={프린시펄의 키를 담은 키탭 파일 경로. auth.kerberos.password 와는 배타적} + auth.kerberos.password={프린시펄의 비밀번호. 키탭이 설정되지 않은 경우에만 사용} + auth.kerberos.strip_port={true 또는 false. 서비스 프린시펄 이름에서 포트를 제거할지 여부. 기본값은 true} + auth.kerberos.use_canonical_hostname={true 또는 false. 서비스 프린시펄 이름을 위해 대상 호스트를 정규 이름으로 해석할지 여부. 기본값은 false} + auth.kerberos.krb5_conf={krb5.conf 경로. java.security.krb5.conf 가 아직 설정되지 않은 경우에만 적용} + auth.kerberos.debug={true 또는 false. Krb5LoginModule 디버그 출력. 기본값은 false} + +- **``krb5.conf`` 는 ``jvm.crawler.options`` 에 설정합니다** + (예: ``-Djava.security.krb5.conf=/path/to/krb5.conf``). 데이터 스토어 크롤링은 + 크롤러의 **자식 프로세스** 에서 실행되므로, webapp 쪽에만 영향을 주는 설정을 해도 + 효과가 없고, webapp을 재시작해도 반영되지 않습니다 — 반영하려면 크롤링 잡을 다시 + 실행해야 합니다. ``auth.kerberos.krb5_conf`` 는 아직 이 프로퍼티가 설정되어 있지 + 않을 때를 위한 편의 기능으로, **이미 설정된 값을 덮어쓰는 일은 절대 없습니다** + (이 프로퍼티는 JVM 전역이며, 하나의 크롤러 JVM이 크롤링 잡의 모든 데이터 설정을 + 실행하기 때문입니다). 덮어쓰지 않은 경우에는 두 경로를 모두 명시한 경고가 로그에 + 출력됩니다. +- **``krb5.conf`` 의 ``[libdefaults]`` 에 ``udp_preference_limit = 1`` 을 설정하세요.** + 설정하지 않으면 JDK는 먼저 UDP로 시도하며, KDC가 응답하지 않는 경우(도달 불가, + 방화벽이 UDP 88을 차단, 응답이 데이터그램 크기를 초과 등) TCP로 폴백하기 전에 30초 + 간격으로 3회 재시도합니다. 로그에 아무것도 남지 않은 채 인증 1회당 약 1분 반씩 + 크롤링이 멈춘 것처럼 보인다면 대개 이것이 원인입니다. +- **프린시펄은 항상 ``user@REALM`` 형식으로 작성하세요.** ``default_realm`` 은 JVM + 전역 설정이며, 서로 다른 렐름에 속한 여러 SharePoint 팜이 하나의 ``krb5.conf`` 를 + 공유해야 하는 경우도 있으므로, 렐름을 생략한 ``user`` 는 그 파일이 우연히 지정하고 + 있는 렐름을 기준으로 해석되어 버립니다. +- **``auth.kerberos.use_canonical_hostname`` 은 기본값이 ``false``** 입니다. Apache + HttpClient 자체의 기본값과는 의도적으로 다릅니다. 활성화하면 서비스 프린시펄 이름을 + 만들기 전에 대상 호스트가 역방향 DNS 조회를 거치는데, 대체 액세스 매핑이나 로드 + 밸런서 뒤에서는 어떤 SPN도 등록되어 있지 않은 이름이 만들어질 수 있으며, 그 결과로 + 발생하는 실패는 DNS가 원인이라는 것을 전혀 알려주지 않습니다. SPN이 실제로 정규 + 이름으로 등록되어 있는 경우에만 활성화하세요. +- **IIS Extended Protection이 ``tokenChecking=Require`` 로 설정되어 있으면 동작할 수 + 없습니다.** Apache HttpClient는 4.5 계열과 5.x 계열 모두 채널 바인딩을 지원하지 + 않습니다. IIS의 기본값은 ``None`` 이므로 보통은 문제가 되지 않지만, ``Require`` 로 + 설정된 경우에는 우회 방법이 없습니다. +- **티켓은 크롤링용 HTTP 클라이언트를 만들 때 한 번만 획득되며, 이후 갱신되지 + 않습니다.** 티켓 유효 기간보다 오래 걸리는 크롤링은 도중부터 인증에 실패하기 + 시작합니다. +- **``auth.kerberos.password`` 는 ``auth.ntlm.password`` 와 마찬가지로 평문으로 + 저장·표시됩니다.** Fess에는 데이터 스토어 핸들러 파라미터를 마스킹하는 기능이 없어, + 데이터 설정 편집 화면은 이를 일반 텍스트 영역으로 렌더링합니다. 가능하면 + ``auth.kerberos.keytab`` 을 사용하고, 키탭 파일에는 제한적인 권한을 설정하세요. +- ``auth.kerberos.debug=true`` 로 설정하면 ``Krb5LoginModule`` 은 Fess 로그가 아니라 + 크롤러 프로세스의 표준 출력에 기록합니다. + +OAuth(ACS) +---------- + +:: + + auth.oauth.client_id={OAuth 클라이언트 ID} + auth.oauth.client_secret={OAuth 클라이언트 시크릿} + auth.oauth.tenant={테넌트 이름. .sharepoint.com 제외} + auth.oauth.realm={Azure AD 렐름/디렉터리 ID} + +``auth.oauth.client_id`` 를 설정하면 Windows Azure Access Control Service +(``https://accounts.accesscontrol.windows.net/{realm}/tokens/OAuth/2``)에 대한 클라이언트 +자격 증명(애플리케이션 전용) 플로우가 활성화됩니다. 액세스 토큰은 크롤링용 HTTP +클라이언트를 만들 때 한 번만 취득되어 모든 요청에 ``Bearer`` ``Authorization`` 헤더로 +부여되며, 401이 반환되면 한 번만 갱신하여 재시도합니다. **Microsoft는 ACS를 지원 중단 +(deprecated) 처리했으며 폐지가 예정되어 있습니다.** OAuth를 설정한 크롤링을 실행할 +때마다 이 사실을 알리는 경고가 로그에 출력됩니다. 이 플러그인에는 Entra ID 앱 등록 +(인증서 또는 클라이언트 시크릿 방식) 플로우는 구현되어 있지 않으며, 레거시 ACS +애플리케이션 전용 인증만 지원합니다. + +OAuth를 연결할지 판단할 때는 ``auth.oauth.client_id`` 의 존재 여부만 확인합니다. +``client_secret``, ``tenant``, ``realm`` 은 무조건 읽어 들이며, 생략하면 그대로 빈 +값이 되어 전용 검증 메시지 없이 토큰 취득이 실패할 수 있습니다. + +**``sp.version=2013`` 과 OAuth의 조합은 한 번도 동작한 적이 없습니다.** 이 플러그인이 +SharePoint 2013을 대상으로 수행하는 모든 API 호출은 XML/Atom 클라이언트를 경유하는데, +그 클라이언트의 어떤 코드 경로도 OAuth 토큰을 요청에 부여하지 않습니다 — 따라서 둘 다 +설정하면 모든 요청이 미인증 상태로 전송됩니다. 크롤링은 이 사실을 그대로 경고로 로그에 +남기고 ``auth.ntlm.*`` 를 대안으로 명시하지만, 잡을 실패시키지는 않습니다. SharePoint +2013에는 ``auth.ntlm.*`` 를 사용하세요. + +권한 +==== + +``role.skip=true`` (기본값 ``false``)로 설정하면 항목별 권한 가져오기를 완전히 +건너뜁니다: ``GetListItemRole`` 호출이 전혀 이루어지지 않고, 항목에 ``role`` 키가 +설정되는 일도 없으며, 문서에는 데이터 설정 자체의 정적 Permission 설정과, 설정되어 +있다면 ``default_permissions`` 만 반영됩니다 — SharePoint에서 유래한 권한은 전혀 +반영되지 않습니다. + +권한을 가져올 때는 SharePoint 자체의 사용자, 보안 그룹, SharePoint 그룹이 전개되어 +Fess의 검색 권한으로 매핑됩니다: + +- **온프레미스 AD** 계정이나 그룹(로그인 이름에 백슬래시를 포함하고, Azure 클레임 + 프리픽스로 시작하지 않는 것)은 표준 AD 사용자/그룹 권한 헬퍼를 통해 매핑됩니다. +- **Azure AD(Entra ID)** 계정(로그인 이름이 ``i:0#.f|membership|`` 로 시작하는 것)은 + **두 가지 방식으로** 매핑됩니다 — Azure 클레임의 전체 값으로 한 번, 그 클레임의 + ``@`` 앞부분에 해당하는 AD 계정 부분으로 한 번, 이렇게 매핑되어 같은 사용자에 대해 + Entra ID 형식과 AD 형식 양쪽의 권한이 추가됩니다. 여러 클레임 형식 프리픽스 중 + 하나(특별한 "전체 사용자" 그룹인 ``spo-grid-all-users`` 포함)로 Azure로 판별된 보안 + 그룹도 동일하게 두 형식 모두로 매핑됩니다. +- **SharePoint 그룹** 은 자신의 멤버십(사용자, 보안 그룹, 중첩된 그룹)이 재귀적으로 + 전개됩니다. 서로를 포함하는 그룹 사이의 무한 재귀를 막기 위한 방문 완료 그룹 가드도 + 갖추고 있습니다. + +``default_permissions`` (쉼표 구분)는 위의 모든 처리가 끝난 **뒤에** 병합되며, +SharePoint가 해당 항목에 대해 권한을 전혀 반환하지 않은 경우(``role.skip=true`` 인 +경우와 "SharePoint가 아무것도 반환하지 않은" 경우 양쪽 모두에 해당)에도 적용됩니다. +최종 권한 목록은 데이터 설정 자체의 정적 Permission 설정, SharePoint에서 유래한 권한 +(건너뛰지 않은 경우), 그리고 ``default_permissions`` 의 합집합에서 중복을 제거한 +것입니다. + +하위 사이트와 관리되는 경로 +=========================== + +``site.path`` 를 설정하면 지정한 서버 상대 관리되는 경로가 하드코딩된 ``/sites/`` +프리픽스 대신 그대로 사용되며, ``site.name`` 은 더 이상 필요하지 않습니다. + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - 시나리오 + - 설정 + * - 루트 사이트 모음 + - ``site.path=/`` + * - ``/teams/eng`` 사이트 + - ``site.path=/teams/eng`` + * - 기존 방식의 ``/sites/mysite/`` 형태 + - ``site.name=mysite`` (``site.path`` 는 설정하지 않음) + +``site.crawl_subsites`` (기본값 ``false``)를 설정하면, 사이트 전체 크롤링 +(``site.list_name`` 도 ``site.doclib_path`` 도 설정하지 않은 크롤링)이 +``_api/web/webinfos`` 로 찾아낸 사이트의 하위 사이트까지 재귀적으로 크롤링하게 +됩니다. 설정하지 않은 채로 두면 크롤링은 지금까지와 정확히 같은 요청만 보내며, +``webinfos`` 자체를 전혀 요청하지 않는 것도 포함해 그대로 유지됩니다. + +하위 사이트의 문서는 루트 사이트의 문서와 같은 데이터 설정 안에, 각자의 서버 상대 +경로 아래에 색인됩니다 — 어떤 문서가 루트가 아니라 하위 사이트에서 왔는지를 나타내는 +정보는 인덱스 어디에도 없습니다. + +``site.max_depth`` (기본값 ``10``)는 ``site.crawl_subsites=true`` 일 때 루트 +사이트로부터 몇 단계 아래의 하위 사이트까지 크롤링할지를 제한합니다. 루트 사이트 +자체가 깊이 0이므로, ``site.max_depth=1`` 은 루트의 직계 하위 사이트까지만 크롤링하고 +그 이상은 진행하지 않습니다. ``site.crawl_subsites=true`` 인 상태에서 +``site.max_depth`` 를 ``1`` 미만으로 설정하면 이 기능은 사실상 다시 꺼진 상태가 되어 +(하위 사이트가 전혀 크롤링되지 않음), 크롤링 시작 시 경고로 로그에 남습니다. + +하위 사이트 크롤링을 켜면, 발견된 하위 사이트 수(``site.max_depth`` 로 제한됨)에 +거의 비례해 **크롤링 전체 소요 시간이 늘어납니다**: 하위 사이트마다 자체적인 전체 +폴더 목록 조회, 목록 조회, 그리고 (깊이 제한에 도달하지 않았다면) 자체적인 +``webinfos`` 호출이, 루트 사이트 크롤링이 이미 수행하는 모든 작업에 더해서 +발생하기 때문입니다. + +`병렬 크롤링과 부하`_ 에서 설명하는 ``number_of_threads`` 와 ``readInterval`` 은, +하위 사이트를 재귀적으로 도는 크롤링에도 다른 어떤 크롤링과 마찬가지로 동일하게 +적용됩니다. + +병렬 크롤링과 부하 +================== + +``number_of_threads`` (기본값 ``1``)는 동시에 처리할 크롤링 대상의 수입니다. +기본값에서는 크롤링이 지금까지와 정확히 똑같이 동작합니다: 모든 대상이 크롤링 +스레드 위에서 처리되며 **스레드 풀은 전혀 생성되지 않습니다.** + +이 값은 Fess를 실행 중인 머신의 **프로세서 수의 2배를 상한** 으로 제한됩니다. +데이터 설정이 호스트가 처리할 수 있는 것보다 많은 동시성을 요구할 수 없도록 하기 +위해서입니다. ``1`` 미만의 값, 또는 비어 있거나 해석할 수 없는 값은 그대로 +반영되거나 잡을 실패시키는 대신 ``1`` 로 폴백합니다. 값이 상한에 걸렸거나 ``1`` +미만이었던 경우에는 요청된 값과 실제 값이 함께 로그에 남고, 해석할 수 없는 +값이었던 경우에는 경고가 로그에 남습니다. **비어 있는 값은 아무것도 로그에 남기지 +않습니다** — 빈 값은 그 파라미터가 단순히 설정되지 않았음을 의미하기 때문입니다. + +HTTP 커넥션 풀 크기도 이 값에 맞춰 조정됩니다. Apache HttpClient는 기본적으로 +라우트당 2개의 커넥션만 허용하며, 크롤링 전체가 하나의 라우트로 취급됩니다: 이 +값을 올리지 않으면 세 번째 이후의 스레드는 요청을 보내는 대신 커넥션을 기다리며 +크롤링 시간을 소모하게 됩니다. + +**``readInterval`` 은 이 값을 무엇으로 설정하든, 문서 전달 속도를 1건씩의 간격으로 +계속 제어합니다.** 스레드는 크롤링의 탐색과 가져오기를 더 빠르게 하지만, 문서가 +인덱서에 도달하는 속도를 더 빠르게 하지는 않습니다. 이는 의도된 설계입니다 — +운영자가 설정한 간격을 스레드 수로 나눠 버리면, 그 간격으로 제한하려던 부하를 +오히려 그대로 배가시키게 되기 때문입니다. 앞선 문서들이 아직 전달 중일 때 한 +문서의 처리를 끝낸 워커는 그냥 대기합니다. + +``number_of_threads`` 를 올렸을 때 실제로 배가되는 것은 SharePoint에 대한 **요청 +빈도** 입니다. 아래에서 설명하는 503 백오프와 ``X-SharePointHealthScore`` 대기는 +크롤링 대상별로, 그것을 크롤링하는 스레드 위에서 적용되므로, ``n`` 개의 스레드는 +단일 스레드 크롤링의 최대 ``n`` 배에 달하는 요청을 보내게 됩니다 — 팜이 "지금 +바쁘다"고 신호를 보내는 동안도 예외는 아닙니다. 온프레미스 팜에서는 이 값을 +단계적으로 올리세요. + +스레드를 늘려도 실제로 얻을 수 있는 효과에는 다음 두 가지가 상한을 둡니다: + +- **각 SharePoint 그룹의 멤버십은 처음 읽힐 때만 한 번에 하나의 스레드씩 순서대로 + 읽힙니다.** 권한은 크롤링 전체가 공유하는 캐시를 통해 해결되며, 이 캐시는 + 그룹의 멤버 조회 동안 유지되는 단일 잠금으로 보호됩니다. 이 잠금 덕분에, 어떤 + 스레드가 멤버를 아직 읽는 중인 그룹을 다른 스레드에 넘겨서, 그 그룹이 보호하는 + 항목을 권한 없이 색인해 버리는 사태를 막을 수 있습니다. 그룹이 한 번 캐시되면 + 이후의 모든 참조는 저렴한 조회가 되므로, 이는 **콜드 캐시 비용** 입니다 — + 서로 다른 그룹이 많은 사이트의 크롤링은 초반 몇 분 동안 ``n`` 개 스레드보다는 + 단일 스레드에 가까운 속도를 보이며, 항목들이 소수의 그룹을 공유하는 사이트에서는 + 거의 영향이 없습니다. 권한을 전혀 읽지 않는 ``role.skip=true`` 는 이 비용을 + 완전히 피합니다. +- 발견 처리는 사이트별로 순차적입니다: 한 사이트의 폴더 목록과 목록 조회는 하나의 + 크롤링 대상이므로, 그 대상의 처리가 끝나 발견 결과가 큐에 들어갈 때까지 + 스레드끼리 나눠 가질 작업이 없습니다. + +**503 응답** 은 다른 오류와 마찬가지로 ``retry_limit`` 횟수까지 재시도되지만, +재시도마다 대기 시간이 늘어납니다: 2초, 4초, 8초로 30초를 상한으로 두 배씩 +늘어나며, 각각 실제 값의 70~129%로 무작위화됩니다. 계속 503을 반환하는 크롤링 +대상은 실제로 재시도가 이루어질 때마다 이 대기를 치르지만, 마지막 재시도 이후에는 +치르지 않습니다. + +**모든 응답** — 성공이든 실패든, 크롤링이 곧 버릴 예정인 목록의 한 페이지까지 +포함해서 — 은 ``X-SharePointHealthScore`` 응답 헤더(0이 유휴, 10이 매우 바쁨)를 +검사받습니다. 점수가 9 이상이면 크롤링은 다음 작업을 하기 전에 대기합니다: 점수 +9는 약 2초, 점수 10은 약 4초 대기하며, 9를 넘는 매 점수마다 두 배씩 늘어납니다. +**이 대기는 상한 없이 크롤링 전체에 걸쳐 누적됩니다** — 지속적인 고부하 상태에서 +헬스 스코어 9에 머무는 팜은 이 커넥터가 보내는 **요청 하나하나마다** 약 2초를 +더하게 되며(모든 폴더·목록 조회의 모든 페이지 포함), 원래는 몇 시간이면 끝날 +크롤링을 훨씬 더 오래 걸리게 만들 수 있습니다. 크롤링이 예상외로 자릿수 단위로 +느려졌다면, 다른 원인을 의심하기 전에 먼저 해당 시간대의 팜 헬스 스코어를 +확인하세요. + +설정 예 +======= + +아래 예시는 모두 NTLM을 사용한다고 가정합니다. 대신 Kerberos나 OAuth를 사용하려면 +`인증`_ 을 참조해 ``auth.ntlm.*`` 줄을 교체하세요. + +목록 크롤 +--------- + +파라미터: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.list_name=Tasks + +스크립트: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +문서 라이브러리 크롤 +-------------------- + +파라미터: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.doclib_path=/Shared Documents + +스크립트: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +``/teams/`` 사이트 크롤링 +------------------------- + +``site.path`` 를 사용하면 ``/sites/`` 이외의 관리되는 경로 아래에 있는 사이트의 +문서 라이브러리를 직접 지정할 수 있습니다. + +파라미터: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.path=/teams/eng + site.doclib_path=/Shared Documents + +스크립트: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +하위 사이트 재귀 크롤링 +----------------------- + +루트 사이트 모음에서 시작해 하위 사이트를 최대 3단계 깊이까지 따라갑니다. + +파라미터: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.path=/ + site.crawl_subsites=true + site.max_depth=3 + +스크립트: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + role=role + +제한 사항 +========= + +- **어떤 형태의 증분·차등 크롤링도 지원하지 않습니다.** 변경 토큰, 델타 쿼리, "마지막 + 수정 이후"와 같은 필터링이 이 커넥터 어디에도 없으며, 실행할 때마다 설정된 모든 + 목록·폴더·파일을 완전히 나열합니다. ``delete_old_docs`` 는 이번 전체 크롤링에서 + 다시 발견되지 않은 문서를 이후에 삭제할지 여부만 제어하는 사후 정리 기능일 뿐, + 증분 가져오기가 아닙니다. +- **파일/폴더 이름 중의 ``%`` 와 ``#``** 은 기본(``2013`` 이외의) 코드 경로에서 + 지원됩니다. 이 두 문자를 파일/폴더 이름에 사용할 수 있는 것은 SharePoint Server + 2019와 Subscription Edition뿐이며, 2016은 명시적으로 거부하고 2013도 거부합니다. + 기본 코드 경로는 디코딩된 경로를 받는 ``...ByServerRelativePath(decodedUrl=...)`` + 계열 엔드포인트로 이런 파일에 접근하며, 색인에 등록하는 링크에서도 이 두 문자를 + 이스케이프합니다. **``sp.version=2013`` 으로는 이런 파일에 접근할 수 없습니다.** + 예전의 ``...ByServerRelativeUrl(...)`` 계열 엔드포인트를 사용하는데, 이 엔드포인트는 + 인자를 이미 인코딩된 URL로 해석하기 때문입니다. 이는 결함이 아니라 의도적인 + 제한입니다. SharePoint 2013 팜 자체가 그런 이름을 보관할 수 없으므로, 문제가 되는 + 것은 ``sp.version=2013`` 을 2019나 Subscription Edition 서버에 대해 사용하는 + 경우뿐이며, 그 조합은 권장되지 않습니다. 자세한 내용은 + `Use of # and % characters in file and folder names + `__ + 과 `File names - expanded support for special characters + `__ + 을 참조하세요. +- **IIS Extended Protection의 ``tokenChecking=Require`` 는 지원할 수 없습니다.** + Apache HttpClient는 4.5 계열과 5.x 계열 모두 Extended Protection의 ``Require`` + 설정이 의존하는 채널 바인딩을 구현하지 않습니다. IIS는 이 설정의 기본값이 + ``None`` 이므로 대부분의 팜에는 영향이 없지만, ``Require`` 로 설정된 팜에 대한 + 우회 방법은 없습니다. +- **데이터 설정 파라미터에 입력한 비밀번호는 평문으로 저장·표시됩니다.** 이는 + ``auth.ntlm.password`` 와 ``auth.kerberos.password`` 모두에 해당합니다: Fess에는 + 데이터 스토어 핸들러 파라미터를 마스킹하는 기능이 없어, 데이터 설정 편집 화면은 + 이를 일반 텍스트 영역으로 렌더링합니다. Kerberos를 사용할 수 있는 환경에서는 + ``auth.kerberos.password`` 보다 ``auth.kerberos.keytab`` 을 우선하고, 키탭 + 파일에는 제한적인 권한을 설정하세요. +- **``sp.version=2013`` 과 OAuth의 조합은 한 번도 동작한 적이 없습니다.** SharePoint + 2013을 대상으로 하는 모든 API 호출은 XML/Atom 클라이언트를 경유하며, 그 + 클라이언트의 어떤 코드 경로도 OAuth 토큰을 요청에 부여하지 않으므로, 둘 다 + 설정하면 모든 요청이 미인증 상태로 전송됩니다. SharePoint 2013에는 + ``auth.ntlm.*`` 를 사용하세요. +- **``/sites/`` 및 ``site.path`` 로 설정한 하나의 관리되는 경로 이외는 자동으로 + 발견되지 않습니다.** ``site.crawl_subsites`` 는 설정한 루트 사이트로부터의 + 재귀만 수행하며, ``site.path`` 는 설정한 그 하나의 관리되는 경로에만 도달할 + 뿐, 팜에 있는 모든 관리되는 경로를 아우르지는 않습니다. + +문제 해결 +========= + +인증이 조용히 실패하는 경우 +--------------------------- + +**증상**: 요청이 401(또는 유사한 오류)로 돌아오지만 로그에 명확한 원인이 나타나지 +않음 + +**확인 사항**: + +1. ``auth.kerberos.principal``, ``auth.ntlm.user``, ``auth.oauth.client_id`` 중 + 2개 이상이 설정되어 있지 않은지 확인(2개 이상 설정하면 크롤링 시작 전에 + 유효성 검사 오류로 잡이 실패함) +2. Kerberos를 사용하는 경우, ``jvm.crawler.options`` 에 + ``-Djava.security.krb5.conf=...`` 가 설정되어 있는지 확인. webapp 쪽에만 + 영향을 주는 곳에 설정해도 효과가 없음. 변경 후에는 크롤링 잡을 다시 실행 + (webapp 재시작으로는 반영되지 않음) +3. Kerberos를 사용하는 경우, ``krb5.conf`` 의 ``[libdefaults]`` 에 + ``udp_preference_limit = 1`` 이 설정되어 있는지 확인. 설정하지 않으면 KDC가 + 응답하지 않을 때 인증 1회당 약 90초(30초 UDP 재시도 3회) 동안 로그에 아무것도 + 남기지 않은 채 멈출 수 있음 +4. 프린시펄이 ``user@REALM`` 형식으로 작성되어 있는지 확인(렐름을 생략한 + ``user`` 는 공유된 ``krb5.conf`` 가 우연히 지정하고 있는 ``default_realm`` 을 + 기준으로 해석됨) +5. OAuth를 사용하는 경우, ``client_secret``, ``tenant``, ``realm`` 이 비어 있지 + 않은지 확인(``client_id`` 의 존재 여부만 검증되므로 나머지는 아무 말 없이 빈 + 값일 수 있음) +6. IIS Extended Protection이 ``tokenChecking=Require`` 로 설정되어 있지 않은지 + 확인(이 설정에는 우회 방법이 없음) +7. 오래 실행되는 크롤링의 경우, 도중부터 실패하기 시작했는지 확인(Kerberos 티켓은 + HTTP 클라이언트 생성 시 한 번만 획득되고 이후 갱신되지 않으므로, 티켓 유효 + 기간보다 오래 걸리는 크롤링은 도중부터 실패하기 시작함) + +크롤링이 느린 경우(503과 헬스 스코어) +------------------------------------- + +**증상**: 크롤링이 예상보다 훨씬 오래 걸리거나 타임아웃됨 + +**확인 사항**: + +1. 느려진 시간대의 SharePoint 팜 ``X-SharePointHealthScore`` 를 확인. 점수가 9 + 이상이면 모든 요청 전에 대기가 추가되며(9에서 약 2초, 10에서 약 4초, 이후 + 배증, 합계 상한 없음), 원래 몇 시간이면 끝날 크롤링이 훨씬 더 오래 걸리게 + 될 수 있음 +2. 503 응답이 반복되고 있지 않은지 확인. 503은 ``retry_limit`` 횟수까지 + 재시도되며, 재시도마다 2초, 4초, 8초(상한 30초) 순으로 대기함 +3. ``number_of_threads`` 를 지나치게 높이지 않았는지 확인. 스레드 수가 늘어나면 + SharePoint에 대한 요청 수도 거의 그만큼 늘어나므로 헬스 스코어가 더 나빠질 + 수 있음. 온프레미스 팜에서는 단계적으로 올릴 것 +4. ``site.crawl_subsites=true`` 인 경우, 전체 크롤링 시간이 발견된 하위 사이트 + 수에 거의 비례해 늘어난다는 점에 유의. ``site.max_depth`` 로 범위를 좁히는 + 것을 고려 + +색인되는 문서가 없음 +-------------------- + +**증상**: 크롤링은 정상적으로 끝나지만 검색 결과가 0건 + +**확인 사항**: + +1. 크롤러 로그에서 오류나 경고를 확인 + (``app/WEB-INF/env/crawler/resources/log4j2.xml`` 에서 ``org.codelibs.fess.ds`` + 를 ``DEBUG`` 로 설정) +2. ``url``, ``site.name`` (또는 ``site.path``), ``site.list_name`` 에 오타가 + 없는지 확인(``site.path`` 를 설정하면 ``site.name`` 은 필요 없다는 점에 유의) +3. 인증이 실제로 성공하고 있는지 확인(401이 발생하지 않는지) — 애초에 인증되지 + 않는 요청 쪽이, ``role.skip`` 이나 ``default_permissions`` 설정 오류보다 + 훨씬 흔한 원인임 +4. ``include_pattern`` 이나 ``exclude_pattern`` 을 설정한 경우, 이들은 검색 + 결과에 표시되는 URL이 아니라 서버 상대 경로(문서 라이브러리 파일이나 목록 + 항목 첨부 파일의 경우) 또는 ``FileRef`` (목록 항목의 경우)에 대해 매칭된다는 + 점에 유의. 전체 URL을 가정한 패턴이 되어 있지 않은지 확인 +5. ``supported_mimetypes`` 나 ``max_content_length`` 설정으로 원하는 파일이 + 제외되고 있지 않은지 확인 +6. ``site.exclude_list`` 나 ``site.exclude_folder`` 가 의도치 않게 대상을 + 제외하고 있지 않은지 확인 + +참고 정보 +========= + +- :doc:`ds-overview` - 데이터 스토어 커넥터 개요 +- :doc:`ds-microsoft365` - Microsoft 365 커넥터(SharePoint Online용) +- :doc:`../../admin/dataconfig-guide` - 데이터 스토어 설정 가이드 +- :doc:`../../admin/plugin-guide` - 플러그인 관리 가이드 diff --git a/ko/15.9/config/datastore/index.rst b/ko/15.9/config/datastore/index.rst index 357899f6..95c422f9 100644 --- a/ko/15.9/config/datastore/index.rst +++ b/ko/15.9/config/datastore/index.rst @@ -25,6 +25,7 @@ ds-atlassian ds-slack + ds-sharepoint .. toctree:: :maxdepth: 2 diff --git a/zh-cn/15.9/config/datastore/ds-overview.rst b/zh-cn/15.9/config/datastore/ds-overview.rst index 1f985955..27cd27ca 100644 --- a/zh-cn/15.9/config/datastore/ds-overview.rst +++ b/zh-cn/15.9/config/datastore/ds-overview.rst @@ -60,6 +60,9 @@ * - :doc:`ds-slack` - fess-ds-slack - 抓取Slack的消息和文件 + * - :doc:`ds-sharepoint` + - fess-ds-sharepoint + - 抓取本地部署(on-premises)SharePoint Server 开发运维工具 ---------------- diff --git a/zh-cn/15.9/config/datastore/ds-sharepoint.rst b/zh-cn/15.9/config/datastore/ds-sharepoint.rst new file mode 100644 index 00000000..9dde0ff5 --- /dev/null +++ b/zh-cn/15.9/config/datastore/ds-sharepoint.rst @@ -0,0 +1,830 @@ +======================= +SharePoint Server连接器 +======================= + +概述 +==== + +SharePoint Server连接器提供从本地部署的 **SharePoint Server** (2013、2016、2019 或 Subscription +Edition)通过其 REST/OData API(2013 版本还包括 XML/Atom API)获取文档库文件和列表项,并注册到 +|Fess| 索引的功能。 + +此功能需要 ``fess-ds-sharepoint`` 插件。 + +.. note:: + + 如果需要爬取 SharePoint Online(Microsoft 365),请使用 :doc:`ds-microsoft365`,而不是本连接器。 + 本连接器的 OAuth 支持仅针对 Azure ACS 应用程序专用认证,不具备 Microsoft Graph API 集成功能。 + +支持的版本: SharePoint Server 2013 / 2016 / 2019 / Subscription Edition (SE) + +支持的内容 +========== + +- 文档库文件 +- 列表项 +- 列表项附件 + +前提条件 +======== + +1. 需要安装插件 +2. 用于爬取的账户需要拥有对目标站点、列表和文档库的读取权限 +3. 从 NTLM、Kerberos(SPNEGO)、OAuth(ACS)中选择且只能选择一种认证方式,并准备好相应的凭据 + +插件安装 +-------- + +从管理界面的「系统」→「插件」进行安装: + +1. 下载 ``fess-ds-sharepoint-X.X.X.jar`` +2. 将其放置到 ``$FESS_HOME/app/WEB-INF/lib`` (或 ``/usr/share/fess/app/WEB-INF/lib`` )下 +3. 重启 |Fess| + +详情请参阅 :doc:`../../admin/plugin-guide`。 + +配置方法 +======== + +从管理界面的「爬虫」→「数据存储」→「新建」配置本连接器。 + +基本设置 +-------- + +.. list-table:: + :header-rows: 1 + :widths: 25 75 + + * - 项目 + - 设置示例 + * - 名称 + - SharePoint + * - 处理器名称 + - SharePointDataStore + * - 启用 + - 开 + +参数设置 +-------- + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.doclib_path=/Shared Documents + +参数列表 +~~~~~~~~ + +**URL / 站点** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - 参数 + - 必需 + - 说明 + * - ``url`` + - 是 + - SharePoint 服务器的基础 URL,例如 ``http://sharepoint.example.com/`` + * - ``site.name`` + - 有条件 + - 在 ``/sites//`` 下爬取的站点集合名称。设置了 ``site.path`` 时则不需要 + * - ``site.path`` + - 否 + - 站点的服务器相对托管路径(例如 ``/teams/eng``;根站点集合使用 ``/``)。设置后将按原样 + 使用该值代替硬编码的 ``/sites/`` 前缀,此时不再需要 ``site.name`` + * - ``site.list_id`` + - 否 + - 通过 GUID 指定单个列表进行爬取(列表爬取模式) + * - ``site.list_name`` + - 否 + - 通过显示名称指定单个列表进行爬取(列表爬取模式) + * - ``site.doclib_path`` + - 否 + - 站点下的文档库路径(文档库爬取模式),例如 ``/Shared Documents`` + * - ``site.exclude_list`` + - 否 + - 要排除的列表实体类型名称的正则表达式(逗号分隔)。仅在整站爬取时生效 + * - ``site.exclude_folder`` + - 否 + - 要排除的顶级文件夹名称的正则表达式(逗号分隔)。仅在整站爬取时生效 + * - ``site.crawl_subsites`` + - 否 + - 是否递归爬取站点的子站点(默认: ``false``)。详见 `子站点和托管路径`_ + * - ``site.max_depth`` + - 否 + - ``site.crawl_subsites`` 可递归的子站点层数(默认: ``10``);根站点的深度为 0 + +**认证** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - 参数 + - 必需 + - 说明 + * - ``auth.ntlm.user`` + - 否 + - NTLM 用户名。设置后即启用 NTLM 认证(可使用 ``DOMAIN\user`` 形式) + * - ``auth.ntlm.password`` + - 否 + - NTLM 密码 + * - ``auth.ntlm.domain`` + - 否 + - Windows 域,作为独立的 NTLM 字段发送 + * - ``auth.ntlm.workstation`` + - 否 + - NTLM 协商过程中发送的工作站名称 + * - ``auth.kerberos.principal`` + - 否 + - 客户端主体,格式为 ``user@REALM``。设置后即启用 Kerberos/SPNEGO 认证 + * - ``auth.kerberos.keytab`` + - 否 + - 保存该主体密钥的 keytab 文件路径。与 ``auth.kerberos.password`` 互斥 + * - ``auth.kerberos.password`` + - 否 + - 该主体的密码,仅在未设置 keytab 时使用 + * - ``auth.kerberos.strip_port`` + - 否 + - 是否从服务主体名称中去除端口号(默认: ``true``) + * - ``auth.kerberos.use_canonical_hostname`` + - 否 + - 在构建服务主体名称之前,是否将目标主机解析为其规范名称(默认: ``false``) + * - ``auth.kerberos.krb5_conf`` + - 否 + - ``krb5.conf`` 文件的路径。仅在尚未设置 ``java.security.krb5.conf`` 时应用 + * - ``auth.kerberos.debug`` + - 否 + - 是否启用 ``Krb5LoginModule`` 的调试输出(默认: ``false``) + * - ``auth.oauth.client_id`` + - 否 + - Azure ACS 应用程序专用 OAuth 客户端 ID。设置后即启用 OAuth 认证 + * - ``auth.oauth.client_secret`` + - 否 + - OAuth 客户端密钥 + * - ``auth.oauth.tenant`` + - 否 + - 租户名称(不含 ``.sharepoint.com``) + * - ``auth.oauth.realm`` + - 否 + - Azure AD 领域(目录 ID) + +``auth.kerberos.principal`` 、``auth.ntlm.user`` 、``auth.oauth.client_id`` 三者中 **只能设置一个**。详见下文的 `认证`_ 一节。 + +**列表** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - 参数 + - 必需 + - 说明 + * - ``list.items.number_per_page`` + - 否 + - ``GetListItems`` 的分页大小(默认: ``100``) + * - ``list.item.content.include_fields`` + - 否 + - 字段名列表(逗号分隔);设置后,仅将这些列表项字段拼接到 ``content`` 中 + * - ``list.item.content.exclude_fields`` + - 否 + - 字段名模式(逗号分隔,每个元素作为正则表达式处理),在内置的大量标准字段之外,从 + ``content`` 中额外排除的字段 + * - ``list.is_sub_page`` + - 否 + - 是否将列表项视为 SitePages/wiki 子页面,这会影响分页回退方式和 Web 链接的形式 + (默认: ``false``) + +**HTTP** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - 参数 + - 必需 + - 说明 + * - ``http.connection_timeout`` + - 否 + - HTTP 连接超时时间(毫秒);同时也用作连接池等待超时时间(默认: ``30000``) + * - ``http.socket_timeout`` + - 否 + - HTTP 套接字(读取)超时时间(毫秒,默认: ``30000``) + * - ``proxy_host`` + - 否 + - HTTP 代理主机 + * - ``proxy_port`` + - 有条件 + - HTTP 代理端口;设置了 ``proxy_host`` 时必需(默认: ``-1`` = 不使用代理) + +**筛选与内容** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - 参数 + - 必需 + - 说明 + * - ``include_pattern`` + - 否 + - 项目的值必须匹配该正则表达式才会被爬取。该值具体指什么,参见本表下方的说明 + * - ``exclude_pattern`` + - 否 + - 匹配该正则表达式的项目将被排除,不会被爬取 + * - ``supported_mimetypes`` + - 否 + - 文件的 MIME 类型必须至少匹配其中一个的正则表达式(逗号分隔,默认: ``.*``) + * - ``max_content_length`` + - 否 + - 文件的最大大小(字节);超出限制的文件会被跳过而非判定为失败(默认: ``-1`` = 无限制) + * - ``extractor_name`` + - 否 + - 仅当提取器工厂无法映射某 MIME 类型时使用的备用提取器(默认: ``tikaExtractor``) + +**行为** + +.. list-table:: + :header-rows: 1 + :widths: 25 15 60 + + * - 参数 + - 必需 + - 说明 + * - ``sp.version`` + - 否 + - 设置为 ``2013`` 可切换到面向 SharePoint 2013 的 XML/Atom、``GetXxxByServerRelativeUrl`` + 系列 API(未设置时使用 SharePoint Online / 2016 以后版本的 REST 方言) + * - ``retry_limit`` + - 否 + - 出现 SharePoint 服务器/客户端异常时,每个爬取单元的最大重试次数(默认: ``2``) + * - ``role.skip`` + - 否 + - 是否完全跳过逐项权限的获取(默认: ``false``)。详见 `权限`_ + * - ``ignore_error`` + - 否 + - 文件内容提取失败时,是否记录日志并跳过,而不是使该爬取目标失败(默认: ``false``) + * - ``default_permissions`` + - 否 + - 权限字符串(逗号分隔),会在 SharePoint 返回的权限之外,合并到每个文档的角色列表中 + * - ``delete_old_docs`` + - 否 + - 本次运行中未被重新获取的文档是否会被删除(核心默认值: ``true``)。只要本次运行中有 + 任意爬取目标失败,本插件就会将该值强制设为 ``false`` + * - ``number_of_threads`` + - 否 + - 同时处理的爬取目标数量(默认: ``1`` = 不使用线程池),上限为处理器核心数的两倍。 + 详见 `并行爬取与负载`_ + * - ``script_type`` + - 否 + - 数据设置中脚本所使用的脚本引擎(默认: ``groovy``) + * - ``readInterval`` + - 否 + - 连续爬取结果之间的等待时间(毫秒,默认: ``0``)。请注意,与本表中其他参数不同, + 该参数使用驼峰命名 + +脚本设置 +-------- + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + role=role + +可用字段 +~~~~~~~~ + +.. list-table:: + :header-rows: 1 + :widths: 16 20 32 32 + + * - 键 + - 列表项(ItemCrawl) + - 文档库文件(FolderCrawl→FileCrawl) + - 附件(ItemAttachmentsCrawl→FileCrawl) + * - ``url`` + - Web 链接 + - 文件 URL + - 文件 URL + * - ``host`` + - 主机名 + - 主机名 + - 主机名 + * - ``site`` + - 服务器相对路径(``FileRef``) + - 服务器相对路径 + - 服务器相对路径 + * - ``title`` + - ``Title`` 字段,否则为 ``FileLeafRef``/文件名 + - 文档库文件自身的 ``Title`` 列表值(如果存在),否则为文件名 + - 文件名 + * - ``titleWithListName`` + - ``"[listName] title"`` + - ``"[listName] filename"`` (文档库爬取时列表名始终为空,因此实际上只是文件名) + - ``"[listName] filename"`` + * - ``listName`` + - 列表显示名称,或 ``""`` + - 始终为 ``""`` + - 实际的列表名称 + * - ``content`` + - 字段值的拼接 + - 提取的文本 + - 提取的文本 + * - ``digest`` + - ``content`` 的摘要 + - ``content`` 的摘要 + - ``content`` 的摘要 + * - ``content_length`` + - ``content.length()`` + - ``content.length()`` + - ``content.length()`` + * - ``last_modified`` + - 来自列表获取结果 + - 来自列表获取结果 + - 来自列表获取结果 + * - ``created`` + - 来自列表获取结果 + - 来自列表获取结果 + - 来自列表获取结果 + * - ``mimetype`` + - 始终为 ``text/html`` + - 检测得出 + - 检测得出 + * - ``filetype`` + - 由 ``mimetype`` 派生 + - 由 ``mimetype`` 派生 + - 由 ``mimetype`` 派生 + * - ``role`` + - 权限列表,仅在非空时设置 + - 权限列表,仅在非空时设置 + - 权限列表,仅在非空时设置 + * - ``list_name`` + - 有 + - **无** + - 有 + * - ``list_id`` + - 有 + - **无** + - 有 + * - ``item_id`` + - 有 + - **无** + - 有 + +.. note:: + + ``content_length`` 是 ``content.length()``,即提取或拼接后文本的字符数(UTF-16 代码单元数), + 并非文件的字节大小。这与 Box、Google Drive、Dropbox 连接器中 ``file.size`` 的含义不同 —— + 后者是各服务自身文件元数据中的实际字节大小。请勿将本连接器的 ``content_length`` + 与它们进行比较。 + +**动态键: ``val_*``** + +列表项 ``FieldValuesAsText`` (SharePoint 针对该项目返回的原始字段值映射,包括 +``odata.metadata`` 等 OData 元数据键)中的每个键都会以两种名称公开:一种不带前缀 +(仅当该名称尚未是上述固定键之一时才会公开),另一种始终带有 ``val_`` 前缀 —— +例如 ``Status`` 字段会同时以 ``Status`` 和 ``val_Status`` 两种形式出现。 + +``val_*`` 键仅存在于 **列表项爬取路径(ItemCrawl)** 中。文档库文件 +(FolderCrawl→FileCrawl)和列表项附件(ItemAttachmentsCrawl→FileCrawl) +都不会产生任何 ``val_*`` 键。 + +认证 +==== + +共有三种认证方式可供选择,且 **只能配置其中一种**。如果 ``auth.kerberos.principal`` 、 +``auth.ntlm.user`` 、``auth.oauth.client_id`` 中设置了多个,数据设置作业会在发出任何请求之前 +就以校验错误失败。这是有意为之的限制:HTTP 客户端只会注册一份凭据,而该凭据注册所在的作用域 +对 ``Negotiate`` 挑战和 ``NTLM`` 挑战都同样适用,因此如果配置了多个认证方式,就会产生日志中 +毫无线索可寻的 401 错误。 + +NTLM +---- + +:: + + auth.ntlm.user={SharePoint 用户名} + auth.ntlm.password={密码} + auth.ntlm.domain={Windows 域。可选,默认未设置} + auth.ntlm.workstation={NTLM 协商中发送的工作站名称。可选,默认未设置} + +``auth.ntlm.domain`` 和 ``auth.ntlm.workstation`` 默认均为未设置,此时构建出的凭据与本连接器 +一直以来构建的完全相同。将域写入用户名中的 ``DOMAIN\user`` 形式仍然有效。设置 +``auth.ntlm.domain`` 后,域会改为作为独立的 NTLM 字段发送,这正是拒绝组合形式的服务器 +所需要的方式。 + +Kerberos(SPNEGO) +------------------ + +**支持的范围仅限于以下配置:** 单个爬虫 JVM、每个 Fess 实例一个 ``krb5.conf``、认证方式为 +keytab 或密码、不支持委派(delegation)、不支持通道绑定(channel binding),且与 NTLM、 +OAuth 互斥。超出此范围的配置均不受支持。 + +:: + + auth.kerberos.principal={客户端主体,格式为 user@REALM。设置后即启用 Kerberos。} + auth.kerberos.keytab={保存该主体密钥的 keytab 文件路径。与 auth.kerberos.password 互斥。} + auth.kerberos.password={该主体的密码。仅在未设置 keytab 时使用。} + auth.kerberos.strip_port={true 或 false。是否从服务主体名称中去除端口号。默认值为 true。} + auth.kerberos.use_canonical_hostname={true 或 false。是否将目标主机解析为其规范名称用于服务主体名称。默认值为 false。} + auth.kerberos.krb5_conf={krb5.conf 文件的路径。仅在尚未设置 java.security.krb5.conf 时应用。} + auth.kerberos.debug={true 或 false。Krb5LoginModule 的调试输出。默认值为 false。} + +- **``krb5.conf`` 应配置在 ``jvm.crawler.options`` 中**,写作 + ``-Djava.security.krb5.conf=/path/to/krb5.conf``。数据存储爬取运行在爬虫的 **子进程** 中, + 因此在只影响 webapp 的地方设置该项不会有任何效果,重启 webapp 也不会使其生效 —— 必须重新 + 运行爬取作业才能生效。``auth.kerberos.krb5_conf`` 是在该属性尚未被设置时使用的便捷方式: + 它 **绝不会覆盖已经设置的值** (因为该属性是 JVM 全局的,一个爬虫 JVM 会运行一次爬取作业中 + 的所有数据设置)。当它因此未能覆盖时,会在日志中记录一条同时列出两个路径的警告。 +- **请在 ``krb5.conf`` 的 ``[libdefaults]`` 中设置 ``udp_preference_limit = 1``。** 如果不 + 设置,JDK 会先尝试 UDP,当 KDC 无响应时(不可达、防火墙丢弃了 UDP 88 端口,或响应大小超过 + 数据报大小限制),会以每次 30 秒的间隔重试三次,然后才回退到 TCP。如果爬取看起来在每次认证 + 时都会卡住约一分半钟,且日志中没有任何记录,通常就是这个原因。 +- **务必将主体写成 ``user@REALM`` 的形式。** ``default_realm`` 是 JVM 全局设置,而多个位于 + 不同领域(realm)的 SharePoint 场可能需要共享同一个 ``krb5.conf``,因此省略领域的 ``user`` + 会按照该文件当时所指定的领域进行解析。 +- **``auth.kerberos.use_canonical_hostname`` 默认值为 ``false``**,这是特意做出的与 Apache + HttpClient 自身默认值不同的选择。启用后,会在构建服务主体名称之前对目标主机执行反向 DNS + 解析,在存在备用访问映射(alternate access mapping)或位于负载均衡器之后的环境中,可能会 + 解析出一个未注册任何 SPN 的名称 —— 而由此产生的失败完全看不出与 DNS 有关。只有在 SPN + 确实是针对规范名称注册的情况下,才应启用此项。 +- **IIS Extended Protection 设置为 ``tokenChecking=Require`` 时无法工作。** Apache HttpClient + 的 4.5 系列和 5.x 系列均不支持通道绑定(channel binding)。IIS 该设置的默认值为 ``None``, + 因此通常不会遇到这个问题,但一旦遇到就没有变通方法。 +- **票据仅在构建爬取用的 HTTP 客户端时获取一次,此后不会更新。** 运行时间超过票据有效期的 + 爬取,会从中途开始出现认证失败。 +- **``auth.kerberos.password`` 与 ``auth.ntlm.password`` 一样,会以明文形式保存和显示。** + Fess 没有为数据存储处理器参数提供掩码机制,数据设置编辑界面会将它们渲染为普通文本区域。 + 请优先使用 ``auth.kerberos.keytab``,并为 keytab 文件设置严格的访问权限。 +- 设置 ``auth.kerberos.debug=true`` 会使 ``Krb5LoginModule`` 将输出写入爬虫进程的标准输出, + 而不是 Fess 日志。 + +OAuth(ACS) +------------ + +:: + + auth.oauth.client_id={OAuth 客户端 ID} + auth.oauth.client_secret={OAuth 客户端密钥} + auth.oauth.tenant={租户名称,不含 .sharepoint.com} + auth.oauth.realm={Azure AD 领域(目录 ID)} + +设置 ``auth.oauth.client_id`` 后,会针对 Windows Azure Access Control Service +(``https://accounts.accesscontrol.windows.net/{realm}/tokens/OAuth/2``)启用客户端凭据 +(应用程序专用)流程。访问令牌会在构建爬取用的 HTTP 客户端时获取一次,并以 ``Bearer`` +``Authorization`` 请求头的形式附加到每个请求上;遇到 401 时会更新令牌并重试一次。 +**Microsoft 已将 ACS 标记为已弃用,并计划将其淘汰。** 每次以 OAuth 方式配置爬取时, +本连接器都会记录一条相应的警告。这里没有实现 Entra ID 应用注册(基于证书或客户端密钥) +流程 —— 仅支持旧版的 ACS 应用程序专用认证。 + +在启用 OAuth 之前,只会检查 ``auth.oauth.client_id`` 是否存在;``client_secret`` 、 +``tenant`` 、``realm`` 则是无条件读取的,如果省略就会在不产生任何专门校验提示的情况下 +悄然保持为空,从而导致令牌获取失败。 + +**``sp.version=2013`` 与 OAuth 从未能一起正常工作。** 本连接器针对 SharePoint 2013 发出的 +所有 API 调用都会经过 XML/Atom 客户端,而该客户端的任何代码路径都不会为请求附加 OAuth +令牌 —— 因此两者同时设置时,所有请求都会以未认证状态发出。爬取过程会准确记录这一情况的 +警告日志,并指出 ``auth.ntlm.*`` 作为替代方案;但不会使该作业失败。SharePoint 2013 请 +使用 ``auth.ntlm.*``。 + +权限 +==== + +设置 ``role.skip=true`` (默认 ``false``)会完全跳过逐项权限的获取:不会调用 +``GetListItemRole``,也不会为该项目设置 ``role`` 键,文档最终只会带有数据设置本身的 +静态权限设置,以及(如果配置了的话)``default_permissions`` —— 完全不会带有任何 +来自 SharePoint 的权限。 + +获取角色时,SharePoint 自身的用户、安全组和 SharePoint 组都会被展开,并映射为 Fess +的搜索角色: + +- **本地 AD** 账户或组(登录名包含反斜杠,且不以 Azure 声明前缀开头)通过标准的 AD + 用户/组角色辅助工具进行映射。 +- **Azure AD(Entra ID)** 账户(登录名以 ``i:0#.f|membership|`` 开头)会被 **映射两次** —— + 一次使用其完整的 Azure 声明值,一次使用该声明中 ``@`` 之前的 AD 账户部分,因此同一个用户 + 会同时获得 Entra ID 形式和 AD 形式两种角色。被判定为 Azure 类型的安全组(通过若干种声明式 + 前缀之一识别,其中包括特殊的「全体成员」组 ``spo-grid-all-users``)也会以相同方式, + 同时以两种形式进行映射。 +- **SharePoint 组** 会递归展开其自身的成员关系(用户、安全组、嵌套组),并设有已访问组的 + 防护机制,以阻止相互包含的组之间发生无限递归。 + +``default_permissions`` (逗号分隔)会在上述所有映射之后进行合并,即使 SharePoint 对该 +项目完全没有返回任何角色(``role.skip=true`` 和「SharePoint 未返回任何内容」这两种情况 +都属于此类)也同样会应用。最终的角色列表是数据设置的静态权限设置、SharePoint 派生的角色 +(除非被跳过)以及 ``default_permissions`` 三者的并集,并经过去重。 + +子站点和托管路径 +================ + +设置 ``site.path`` 后,会按原样使用指定的服务器相对托管路径,代替硬编码的 ``/sites/`` +前缀,此时不再需要 ``site.name``。 + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - 场景 + - 设置 + * - 根站点集合 + - ``site.path=/`` + * - ``/teams/eng`` 站点 + - ``site.path=/teams/eng`` + * - 传统的 ``/sites/mysite/`` 形式 + - ``site.name=mysite`` (不设置 ``site.path``) + +设置 ``site.crawl_subsites`` (默认 ``false``)会使整站爬取(即既未设置 ``site.list_name`` +也未设置 ``site.doclib_path`` 的爬取)递归进入通过 ``_api/web/webinfos`` 发现的站点子站点。 +保持未设置时,爬取发出的请求与以往完全相同,包括从不请求 ``webinfos``。 + +子站点的文档会与根站点的文档一起归入同一个数据设置,各自使用自己的服务器相对路径 —— +索引中没有任何信息能标记出某个文档是来自子站点而非根站点。 + +``site.max_depth`` (默认 ``10``)限制了在 ``site.crawl_subsites=true`` 时,从根站点向下 +爬取的子站点层数上限。根站点自身的深度为 0,因此 ``site.max_depth=1`` 只会爬取根站点的 +直接子站点,不再继续深入。在 ``site.crawl_subsites=true`` 的情况下将 ``site.max_depth`` +设置为小于 ``1`` 的值,会使该功能实际上被关闭 —— 不会爬取任何子站点 —— 并会在爬取开始 +时记录一条警告。 + +启用子站点爬取会使爬取的总耗时大致按发现的子站点数量(受 ``site.max_depth`` 限制)成倍 +增加:除了根站点爬取本身已有的所有工作之外,每个子站点还会各自产生一次完整的文件夹列表 +获取、列表获取,以及(如果尚未达到深度上限)一次 ``webinfos`` 调用。 + +`并行爬取与负载`_ 一节中介绍的 ``number_of_threads`` 和 ``readInterval``,对包含子站点 +递归的爬取同样适用,与其他任何爬取一样。 + +并行爬取与负载 +============== + +``number_of_threads`` (默认 ``1``)是同时处理的爬取目标数量。在默认值下,爬取的运行方式 +与以往完全相同:每个目标都在爬取线程上处理,**完全不会创建线程池**。 + +该值的上限为运行 Fess 的机器处理器核心数的两倍,因此数据设置无法请求超出主机处理能力的 +并发度。小于 ``1`` 的值,或空白、无法解析的值,都会回退为 ``1``,而不会被直接采用或使作业 +失败。被限制到上限的值,或小于 ``1`` 的值,会同时记录请求值和实际值;无法解析的值会记录 +一条警告。空白值不会记录任何日志,因为字段为空只是表示该参数根本没有设置。 + +HTTP 连接池的大小会随之调整。Apache HttpClient 默认每条路由(route)只允许 2 个连接, +而整次爬取被视为一条路由:如果不提高这个上限,第三个及以后的线程就会把大部分时间花在 +等待连接上,而不是发出请求。 + +**无论 ``readInterval`` 设置为多少,它仍然会以每个间隔一个文档的节奏控制文档的移交。** +线程能让爬取的发现和获取速度更快,但不会让文档到达索引器的速度更快。这是有意为之的设计 —— +如果把运维人员配置的间隔除以线程数,恰好会把该间隔原本要限制的负载放大相同的倍数。如果某个 +worker 完成了一个文档处理,而之前的文档仍在移交中,它就只能等待。 + +提高 ``number_of_threads`` **确实会** 成倍增加的是针对 SharePoint 的请求速率。下文所述的 +503 退避等待和 ``X-SharePointHealthScore`` 等待都是按每个爬取目标、在爬取该目标的线程上 +应用的,因此 ``n`` 个线程发出的请求最多可达单线程爬取的 ``n`` 倍 —— 包括在场(farm)正在 +表明自己很忙的那段时间里也是如此。对于本地部署的场,请逐步提高该值。 + +有两个因素限制了增加线程数实际能带来的收益: + +- **每个 SharePoint 组的成员关系首次被读取时,都是一次一个线程地读取。** 权限的解析要经过 + 整次爬取共享的缓存,该缓存在一个组的成员查找期间由单一的锁保护。这把锁可以防止某个线程把 + 一个成员仍在读取中的组交给另一个线程,从而避免在不带有任何权限的情况下索引该组所保护的 + 项目。一个组一旦被缓存,之后对它的每次引用都只是一次廉价的查找,因此这是一种 **冷缓存 + 成本**:拥有众多不同组的站点,其爬取的最初几分钟表现更接近单线程而非 ``n`` 线程,而项目 + 共享少数几个组的站点则几乎感觉不到差异。完全不读取权限的 ``role.skip=true`` 可以完全 + 避免这一成本。 +- 每个站点的发现过程是串行的:一个站点的文件夹列表和列表获取本身就是一个爬取目标,因此 + 在该目标完成并将发现结果加入队列之前,线程之间没有任何工作可以分担。 + +**503 响应** 与其他错误一样会被重试,最多重试 ``retry_limit`` 次,但每次重试前的等待时间 +会递增:2 秒、4 秒、8 秒,以 30 秒为上限逐次翻倍,且每次都会在该值的 70%-129% 范围内 +随机化。持续返回 503 的爬取目标,会在它实际获得的每一次重试之前都付出这一等待,但最后一次 +重试之后不会再等待。 + +**每一个响应** —— 无论成功与否,包括爬取即将丢弃的某个列表分页 —— 都会被检查 +``X-SharePointHealthScore`` 响应头(0 表示空闲,10 表示非常繁忙)。分数达到 9 或以上时, +爬取会在做任何其他事情之前先等待:分数为 9 时等待约 2 秒,为 10 时约 4 秒,此后每增加 +1 分等待时间翻倍。**这种等待会在整个爬取过程中不断累积,且没有总量上限**:一个持续处于 +高负载、健康分数维持在 9 的场,会给本连接器发出的 **每一个** 请求都额外增加约 2 秒 —— +包括每个文件夹和列表分页的获取 —— 这可能会让原本只需数小时的爬取耗时大幅延长。如果爬取 +的速度出现数量级的意外下降,请先检查该时段场的健康分数,再考虑其他原因。 + +配置示例 +======== + +以下示例均假定使用 NTLM。如果要改用 Kerberos 或 OAuth,请参见 `认证`_ 并替换 +``auth.ntlm.*`` 相关的行。 + +列表爬取 +-------- + +参数: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.list_name=Tasks + +脚本: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +文档库爬取 +---------- + +参数: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.name=mysite + site.doclib_path=/Shared Documents + +脚本: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +爬取 ``/teams/`` 站点 +--------------------- + +``site.path`` 可让你直接指向位于 ``/sites/`` 以外的托管路径下某个站点中的文档库。 + +参数: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.path=/teams/eng + site.doclib_path=/Shared Documents + +脚本: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + +递归子站点爬取 +-------------- + +从根站点集合开始,沿子站点递归爬取,最深至第 3 层。 + +参数: + +:: + + url=http://sharepoint.example.com/ + auth.ntlm.user=DOMAIN\svc-fess + auth.ntlm.password=changeit + site.path=/ + site.crawl_subsites=true + site.max_depth=3 + +脚本: + +:: + + url=url + title=title + content=content + digest=digest + content_length=content.length() + last_modified=last_modified + role=role + +限制事项 +======== + +- **完全不支持任何形式的增量或差分爬取。** 本连接器中不存在任何变更令牌(change-token)、 + 差分查询(delta-query)或「自上次以来已修改」之类的过滤机制 —— 每次运行都会对配置要 + 到达的每个列表、文件夹和文件进行完整列举。``delete_old_docs`` 只是控制本次完整爬取 + 未再次发现的文档事后是否会被删除,这只是善后清理,并不是增量获取。 +- **文件名/文件夹名中的 ``%`` 和 ``#``** 在默认(非 ``2013``)代码路径下受支持。只有 + SharePoint Server 2019 和 Subscription Edition 才允许名称中出现这两个字符;2016 明确 + 仍然拒绝,2013 同样拒绝。默认代码路径通过接收已解码路径的 + ``...ByServerRelativePath(decodedUrl=...)`` 系列端点访问这类文件,并且在建立索引所用的 + 链接中也会对这两个字符进行转义。**``sp.version=2013`` 无法访问这类文件**,因为它使用更老的 + ``...ByServerRelativeUrl(...)`` 系列端点,而这些端点会把参数当作已编码的 URL 来解释。 + 这是有意的限制而非缺陷:SharePoint 2013 的场本身就无法保存这样的名称,因此只有把 + ``sp.version=2013`` 指向 2019 或 Subscription Edition 服务器时才有影响,而这种组合并不推荐。 + 参见 `Use of # and % characters in file and folder names + `__ + 和 `File names - expanded support for special characters + `__。 +- **无法支持 IIS Extended Protection 的 ``tokenChecking=Require`` 设置。** Apache + HttpClient 的 4.5 系列和 5.x 系列都没有实现通道绑定(channel binding),而 + Extended Protection 在 ``Require`` 级别正是依赖于此。IIS 该设置的默认值为 ``None``, + 因此大多数场不受影响,但对于设置为 ``Require`` 的场,没有任何变通方法。 +- **数据设置参数中的密码会以明文形式保存和显示。** 这一点对 ``auth.ntlm.password`` 和 + ``auth.kerberos.password`` 都同样适用:Fess 没有为数据存储处理器参数提供掩码机制, + 数据设置编辑界面会将它们渲染在普通文本区域中。在可以使用 Kerberos 的环境中,请优先 + 使用 ``auth.kerberos.keytab`` 而非 ``auth.kerberos.password``,并为 keytab 文件设置 + 严格的访问权限。 +- **``sp.version=2013`` 与 OAuth 从未能一起正常工作。** 所有 SharePoint 2013 的 API 调用 + 都经过 XML/Atom 客户端,而该客户端的任何代码路径都不会为请求附加 OAuth 令牌,因此两者 + 同时设置时所有请求都会以未认证状态发出。SharePoint 2013 请使用 ``auth.ntlm.*``。 +- **除 ``/sites/`` 以及通过 ``site.path`` 设置的那一个托管路径之外,其余托管路径仍然不会 + 被自动发现。** ``site.crawl_subsites`` 只会从你所配置的根站点开始递归,而 ``site.path`` + 也只能到达你所设置的那一个托管路径,并不会覆盖场上的所有托管路径。 + +故障排除 +======== + +认证静默失败 +------------ + +**症状**: 请求返回 401(或类似错误),但日志中没有明确的原因说明 + +**确认事项**: + +1. 检查 ``auth.kerberos.principal`` 、``auth.ntlm.user`` 、``auth.oauth.client_id`` 中 + 是否设置了多个 —— 设置两个及以上会在爬取开始前就以校验错误使作业失败 +2. 对于 Kerberos,确认 ``-Djava.security.krb5.conf=...`` 已设置在 ``jvm.crawler.options`` + 中。设置在只影响 webapp 的地方不会有任何效果。更改后需要重新运行爬取作业 —— 重启 + webapp 不会使其生效 +3. 对于 Kerberos,确认 ``krb5.conf`` 的 ``[libdefaults]`` 中已设置 + ``udp_preference_limit = 1``。如果没有设置,KDC 无响应时会导致每次认证卡住约 90 秒 + (3 次 30 秒的 UDP 重试),且日志中不会留下任何记录 +4. 确认主体已写成 ``user@REALM`` 的形式 —— 省略领域的 ``user`` 会按照共享的 + ``krb5.conf`` 当时指定的 ``default_realm`` 进行解析 +5. 对于 OAuth,确认 ``client_secret`` 、``tenant`` 、``realm`` 均不为空 —— 只有 + ``client_id`` 的存在性会被校验,其余几项可能在毫无提示的情况下为空 +6. 确认 IIS Extended Protection 未设置为 ``tokenChecking=Require`` —— 该设置没有 + 任何变通方法 +7. 对于长时间运行的爬取,检查是否从中途才开始出现失败 —— Kerberos 票据仅在构建 + HTTP 客户端时获取一次,此后不会更新,因此运行时间超过票据有效期的爬取会从中途开始 + 失败 + +爬取速度缓慢(503 与健康分数) +------------------------------ + +**症状**: 爬取耗时远超预期,或发生超时 + +**确认事项**: + +1. 检查该缓慢时段内 SharePoint 场的 ``X-SharePointHealthScore``。分数达到 9 或以上 + 会在每个请求前增加等待(9 时约 2 秒,10 时约 4 秒,此后逐次翻倍,且无总量上限), + 可能会让原本只需数小时的爬取耗时大幅延长 +2. 检查是否反复出现 503 响应。503 最多会被重试 ``retry_limit`` 次,每次重试前依次 + 等待 2 秒、4 秒、8 秒(上限 30 秒) +3. 检查 ``number_of_threads`` 是否设置得过高。线程数越多,针对 SharePoint 的请求量 + 大致会成比例增加,这可能会推高健康分数。对于本地部署的场,请逐步提高该值 +4. 如果设置了 ``site.crawl_subsites=true``,请记住爬取总耗时大致会随发现的子站点 + 数量增长 —— 可以考虑通过 ``site.max_depth`` 缩小范围 + +没有任何内容被索引 +------------------ + +**症状**: 爬取正常结束,但搜索结果为 0 件 + +**确认事项**: + +1. 检查爬虫日志中是否有错误或警告(可在 + ``app/WEB-INF/env/crawler/resources/log4j2.xml`` 中将 ``org.codelibs.fess.ds`` + 设置为 ``DEBUG``) +2. 检查 ``url`` 、``site.name`` (或 ``site.path``)、``site.list_name`` 是否有 + 拼写错误 —— 注意一旦设置了 ``site.path``,就不再需要 ``site.name`` +3. 确认认证确实成功(没有 401)—— 请求从未通过认证,是比 ``role.skip`` 或 + ``default_permissions`` 配置错误更常见得多的原因 +4. 如果设置了 ``include_pattern`` 或 ``exclude_pattern``,请注意它们匹配的是 + 服务器相对路径(对于文档库文件或列表项附件)或 ``FileRef`` (对于列表项)—— + 而不是搜索结果中显示的 URL。检查是否误写成了针对完整 URL 的模式 +5. 检查 ``supported_mimetypes`` 或 ``max_content_length`` 是否排除了你期望看到 + 的文件 +6. 检查 ``site.exclude_list`` 或 ``site.exclude_folder`` 是否无意中排除了目标 + +参考信息 +======== + +- :doc:`ds-overview` - 数据存储连接器概述 +- :doc:`ds-microsoft365` - Microsoft 365 连接器(用于 SharePoint Online) +- :doc:`../../admin/dataconfig-guide` - 数据存储配置指南 +- :doc:`../../admin/plugin-guide` - 插件管理指南 diff --git a/zh-cn/15.9/config/datastore/index.rst b/zh-cn/15.9/config/datastore/index.rst index 09337c97..029a4e9e 100644 --- a/zh-cn/15.9/config/datastore/index.rst +++ b/zh-cn/15.9/config/datastore/index.rst @@ -25,6 +25,7 @@ ds-atlassian ds-slack + ds-sharepoint .. toctree:: :maxdepth: 2