Skip to content

Commit 69dee01

Browse files
committed
Merge remote-tracking branch 'IQSS/develop' into Arch4-fix_isArchivable_caching
2 parents ad0476a + cc37d4d commit 69dee01

14 files changed

Lines changed: 154 additions & 30 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
This release fixes a bug where the value of the dataverse.auth.oidc.enabled setting, available when Provisioning an authentication provider via JVM options (see ref: https://guides.dataverse.org/en/latest/installation/oidc.html#provision-via-jvm-options) was not being not being propagated to the current Dataverse user interface (where enabled=false providers are not displayed for login/registration) or represented in the GET api/admin/authenticationProviders API call.
2+
3+
A new JVM setting ('dataverse.auth.oidc.hidden-jsf') was added to hide an enabled OIDC Provider from the JSF UI.
4+
5+
For Dataverse instances deploying both the current JSF UI and the new SPA UI, this fix allows the OIDC Keycloak provider configured for the SPA to be hidden in the JSF UI (useful in cases where it would duplicate other configured providers).
6+
7+
Note: The API to create a new Auth Provider can only be used to create a provider for both JSF and SPA. Use JVM / MicroProfile config setting to create SPA only providers.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Changes in v6.9 that significantly improved re-indexing performance and lowered memory use in situations
2+
such as when a user's role on the root collection were changed, also slowed reindexing of individual
3+
datasets ater editing and publication.
4+
5+
This release restores/improves the individual dataset reindexing performance while retaining the
6+
benefits of the earlier update.

doc/sphinx-guides/source/api/native-api.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7654,6 +7654,8 @@ Add new authentication provider. The POST data is in JSON format, similar to the
76547654
76557655
POST http://$SERVER/api/admin/authenticationProviders
76567656
7657+
.. note:: This endpoint will create providers for both JSF and SPA. Use :ref:`jvm-options` / *MicroProfile Config* if you need to create SPA only providers.
7658+
76577659
Show Authentication Provider
76587660
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
76597661

doc/sphinx-guides/source/installation/oidc.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ The following options are available:
151151
- Enable or disable provisioning the provider via MicroProfile.
152152
- N
153153
- ``false``
154+
* - ``dataverse.auth.oidc.hidden-jsf``
155+
- Show or Hide the provider from the JSF UI via MicroProfile.
156+
- N
157+
- ``false``
154158
* - ``dataverse.auth.oidc.client-id``
155159
- The client-id of the application to identify it at your provider.
156160
- Y
@@ -187,4 +191,4 @@ The following options are available:
187191
- Tune the maximum age, in seconds, of all OIDC providers' verifier cache entries. Default is 5 minutes, equivalent to lifetime
188192
of many OIDC access tokens.
189193
- N
190-
- 300
194+
- 300

docker-compose-dev.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ services:
2222
DATAVERSE_MAIL_SYSTEM_EMAIL: "dataverse@localhost"
2323
DATAVERSE_MAIL_MTA_HOST: "smtp"
2424
DATAVERSE_AUTH_OIDC_ENABLED: "1"
25+
DATAVERSE_AUTH_OIDC_HIDDEN_JSF: "1"
2526
DATAVERSE_AUTH_OIDC_CLIENT_ID: test
2627
DATAVERSE_AUTH_OIDC_CLIENT_SECRET: 94XHrfNRwXsjqTqApRrwWmhDLDHpIYV8
2728
DATAVERSE_AUTH_OIDC_AUTH_SERVER_URL: http://keycloak.mydomain.com:8090/realms/test

src/main/java/edu/harvard/iq/dataverse/LoginPage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public List<AuthenticationProviderDisplayInfo> listAuthenticationProviders() {
143143
Collections.sort(idps, Comparator.comparing(AuthenticationProvider::getOrder).thenComparing(AuthenticationProvider::getId));
144144

145145
for (AuthenticationProvider idp : idps) {
146-
if (idp != null) {
146+
if (idp != null && !idp.isHidden()) {
147147
infos.add(idp.getInfo());
148148
}
149149
}

src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticationProvider.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
* "authenticationProvider.name." + "ship" ->
1919
* (c) Bundle.properties entry: "authenticationProvider.name.shib=Shibboleth"
2020
*
21-
* {@code AuthenticationPrvider}s are normally registered at startup in {@link AuthenticationServiceBean#startup()}.
21+
* {@code AuthenticationProvider}s are normally registered at startup in {@link AuthenticationServiceBean#startup()}.
2222
*
2323
* @author michael
2424
*/
@@ -33,9 +33,13 @@ public interface AuthenticationProvider {
3333
default boolean isUserInfoUpdateAllowed() { return false; };
3434
default boolean isUserDeletionAllowed() { return false; };
3535
default boolean isOAuthProvider() { return false; };
36-
37-
38-
36+
default boolean isEnabled() {
37+
return true;
38+
};
39+
default boolean isHidden() {
40+
return false;
41+
};
42+
3943
/**
4044
* Some providers (e.g organizational ones) provide verified email addresses.
4145
* @return {@code true} if we can treat email addresses coming from this provider as verified, {@code false} otherwise.

src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/AbstractOAuth2AuthenticationProvider.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ public String toString() {
9393
protected String clientSecret;
9494
protected String baseUserEndpoint;
9595
protected String redirectUrl;
96+
protected boolean enabled = true;
97+
protected boolean hidden = false; // Special flag to hide this provider in JSF UI
9698

9799
/**
98100
* List of scopes to be requested for authorization at identity provider.
@@ -272,6 +274,21 @@ public String getSubTitle() {
272274

273275
public String getSpacedScope() { return String.join(" ", getScope()); }
274276

277+
public void setEnabled(boolean enabled) {
278+
this.enabled = enabled;
279+
}
280+
281+
public boolean isEnabled() {
282+
return enabled;
283+
}
284+
285+
public void setHidden(boolean hidden) {
286+
this.hidden = hidden;
287+
}
288+
public boolean isHidden() {
289+
return hidden;
290+
}
291+
275292
@Override
276293
public int hashCode() {
277294
int hash = 7;

src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/oidc/OIDCAuthenticationProviderFactory.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public AuthenticationProvider buildProvider( AuthenticationProviderRow aRow ) th
4949
oidc.setId(aRow.getId());
5050
oidc.setTitle(aRow.getTitle());
5151
oidc.setSubTitle(aRow.getSubtitle());
52+
oidc.setEnabled(aRow.isEnabled());
5253

5354
return oidc;
5455
}
@@ -70,6 +71,8 @@ public static AuthenticationProvider buildFromSettings() throws AuthorizationSet
7071
oidc.setId("oidc-mpconfig");
7172
oidc.setTitle(JvmSettings.OIDC_TITLE.lookupOptional().orElse("OpenID Connect"));
7273
oidc.setSubTitle(JvmSettings.OIDC_SUBTITLE.lookupOptional().orElse("OpenID Connect"));
74+
oidc.setEnabled(JvmSettings.OIDC_ENABLED.lookupOptional(Boolean.class).orElse(true));
75+
oidc.setHidden(JvmSettings.OIDC_HIDDEN_JSF.lookupOptional(Boolean.class).orElse(false));
7376

7477
return oidc;
7578
}

src/main/java/edu/harvard/iq/dataverse/search/SolrIndexServiceBean.java

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package edu.harvard.iq.dataverse.search;
22

33
import edu.harvard.iq.dataverse.DataFile;
4+
import edu.harvard.iq.dataverse.DataFileServiceBean;
45
import edu.harvard.iq.dataverse.Dataset;
56
import edu.harvard.iq.dataverse.DatasetServiceBean;
67
import edu.harvard.iq.dataverse.DatasetVersion;
@@ -53,6 +54,8 @@ public class SolrIndexServiceBean {
5354
@EJB
5455
SearchPermissionsServiceBean searchPermissionsService;
5556
@EJB
57+
DataFileServiceBean dataFileService;
58+
@EJB
5659
DataverseServiceBean dataverseService;
5760
@EJB
5861
DatasetServiceBean datasetService;
@@ -365,8 +368,30 @@ public IndexResponse indexPermissionsOnSelfAndChildren(DvObject definitionPoint)
365368
indexPermissionsForOneDvObject(definitionPoint);
366369
numObjects++;
367370

368-
// Process the dataset's files in a new transaction
369-
self.indexDatasetFilesInNewTransaction(dataset.getId(), counter, fileQueryMin);
371+
/**
372+
* Prepare the data needed for the new transaction. For performance reasons, indexDatasetFilesInNewTransaction does not merge the dataset or versions into the new transaction (we only read info, there
373+
* are no changes to write). However, there are two ways the code here is used. In one case, indexing content and permissions, the versions and fileMetadatas in them are already loaded. In the other
374+
* case, indexing permissions only, the fileMetadatas are not yet loaded, and we may need them, but only if there are fewer than fileQueryMin. For each version that will get reindexed (at most two of
375+
* them), the code below does a lightweight query to see how many fileMetadatas exist in it and, if it is equal to or below fileQueryMin, calls getFileMetadatas().size() to assure they are loaded
376+
* (before we pass the version into a new transaction where it will be detached and fileMetadatas can't be loaded). Calling getFileMetadas.size() should be lightweight when the fileMetadatas are
377+
* loaded (first case) and done only when needed for the second case.
378+
*
379+
**/
380+
Map<DatasetVersion.VersionState, Boolean> desiredCards = searchPermissionsService.getDesiredCards(dataset);
381+
List<DatasetVersion> versionsToIndex = new ArrayList<>();
382+
for (DatasetVersion version : versionsToReIndexPermissionsFor(dataset)) {
383+
if (desiredCards.get(version.getVersionState())) {
384+
int fileCount = dataFileService.findCountByDatasetVersionId(version.getId()).intValue();
385+
if (fileCount <= fileQueryMin) {
386+
// IMPORTANT: This triggers the loading of fileMetadatas within the current transaction
387+
version.getFileMetadatas().size();
388+
}
389+
versionsToIndex.add(version);
390+
}
391+
}
392+
393+
// Process the dataset's files in a new transaction, passing the pre-loaded data
394+
self.indexDatasetFilesInNewTransaction(versionsToIndex, counter, fileQueryMin);
370395
} else {
371396
// For other types (like files), just index in a new transaction
372397
indexPermissionsForOneDvObject(definitionPoint);
@@ -399,15 +424,11 @@ public void indexDatasetBatchInNewTransaction(List<Long> datasetIds, final int[]
399424
}
400425

401426
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
402-
public void indexDatasetFilesInNewTransaction(Long datasetId, final int[] fileCounter, int fileQueryMin) {
403-
Dataset dataset = datasetService.find(datasetId);
404-
if (dataset != null) {
405-
Map<DatasetVersion.VersionState, Boolean> desiredCards = searchPermissionsService.getDesiredCards(dataset);
406-
for (DatasetVersion version : versionsToReIndexPermissionsFor(dataset)) {
407-
if (desiredCards.get(version.getVersionState())) {
408-
processDatasetVersionFiles(version, fileCounter, fileQueryMin);
409-
}
410-
}
427+
public void indexDatasetFilesInNewTransaction(List<DatasetVersion> versions, final int[] fileCounter, int fileQueryMin) {
428+
for (DatasetVersion version : versions) {
429+
// The version object is detached, but its fileMetadatas collection is already loaded.
430+
// We only need its ID and state, which are available.
431+
processDatasetVersionFiles(version, fileCounter, fileQueryMin);
411432
}
412433
}
413434

@@ -421,22 +442,24 @@ private void processDatasetVersionFiles(DatasetVersion version,
421442
// Process files in batches of 100
422443
int batchSize = 100;
423444

424-
if (version.getFileMetadatas().size() > fileQueryMin) {
445+
if (dataFileService.findCountByDatasetVersionId(version.getId()).intValue() > fileQueryMin) {
425446
// For large datasets, use a more efficient SQL query
426-
Stream<DataFileProxy> fileStream = getDataFileInfoForPermissionIndexing(version.getId());
447+
try (Stream<DataFileProxy> fileStream = getDataFileInfoForPermissionIndexing(version.getId())) {
427448

428-
// Process files in batches to avoid memory issues
429-
fileStream.forEach(fileInfo -> {
430-
filesToReindexAsBatch.add(fileInfo);
431-
fileCounter[0]++;
449+
// Process files in batches to avoid memory issues
450+
fileStream.forEach(fileInfo -> {
451+
filesToReindexAsBatch.add(fileInfo);
452+
fileCounter[0]++;
432453

433-
if (filesToReindexAsBatch.size() >= batchSize) {
434-
reindexFilesInBatches(filesToReindexAsBatch, cachedPerms, versionId, solrIdEnd);
435-
filesToReindexAsBatch.clear();
436-
}
437-
});
454+
if (filesToReindexAsBatch.size() >= batchSize) {
455+
reindexFilesInBatches(filesToReindexAsBatch, cachedPerms, versionId, solrIdEnd);
456+
filesToReindexAsBatch.clear();
457+
}
458+
});
459+
}
438460
} else {
439461
// For smaller datasets, process files directly
462+
// We only call getFileMetadatas() in the case where we know they have already been loaded
440463
for (FileMetadata fmd : version.getFileMetadatas()) {
441464
DataFileProxy fileProxy = new DataFileProxy(fmd);
442465
filesToReindexAsBatch.add(fileProxy);

0 commit comments

Comments
 (0)