@@ -88,6 +88,8 @@ public abstract class AbstractOntologyService implements OntologyService {
8888 private boolean searchEnabled = true ;
8989 private Set <String > excludedWordsFromStemming = Collections .emptySet ();
9090 private Set <String > additionalPropertyUris = DEFAULT_ADDITIONAL_PROPERTIES .stream ().map ( Property ::getURI ).collect ( Collectors .toSet () );
91+ @ Nullable
92+ private Set <String > allowedUriPrefixes = null ;
9193
9294 protected AbstractOntologyService ( String ontologyName , String ontologyUrl , boolean ontologyEnabled , @ Nullable String cacheName ) {
9395 this .ontologyName = ontologyName ;
@@ -126,9 +128,9 @@ public synchronized void startInitializationThread( boolean forceLoad, boolean f
126128 try {
127129 this .initialize ( forceLoad , forceIndexing );
128130 } catch ( Exception e ) {
129- log .error ( "Initialization for %s failed." , e );
131+ log .error ( "Initialization of {} failed." , this , e );
130132 }
131- }, getOntologyName () + "_load_thread_" + RandomStringUtils .randomAlphanumeric ( 5 ) );
133+ }, getOntologyName () + "_load_thread_" + RandomStringUtils .insecure (). nextAlphanumeric ( 5 ) );
132134 // To prevent VM from waiting on this thread to shut down (if shutting down).
133135 initializationThread .setDaemon ( true );
134136 initializationThread .start ();
@@ -235,6 +237,19 @@ public void setProcessImports( boolean processImports ) {
235237 this .processImports = processImports ;
236238 }
237239
240+ @ Override
241+ public void setAllowedUriPrefixes ( String ... uriPrefixes ) {
242+ if ( uriPrefixes .length == 0 ) {
243+ throw new IllegalArgumentException ( "At least one URI prefix must be supplied." );
244+ }
245+ this .allowedUriPrefixes = new HashSet <>( Arrays .asList ( uriPrefixes ) );
246+ }
247+
248+ @ Override
249+ public void clearAllowedUriPrefixes () {
250+ this .allowedUriPrefixes = null ;
251+ }
252+
238253 @ Override
239254 public boolean isSearchEnabled () {
240255 return getState ().map ( state -> state .index != null ).orElse ( searchEnabled );
@@ -288,6 +303,7 @@ private synchronized void initialize( @Nullable InputStream stream, boolean forc
288303 boolean processImports = this .processImports ;
289304 boolean searchEnabled = this .searchEnabled ;
290305 Set <String > excludedWordsFromStemming = this .excludedWordsFromStemming ;
306+ Set <String > allowedUriPrefixes = this .allowedUriPrefixes ;
291307
292308 // Detect configuration problems.
293309 if ( StringUtils .isBlank ( ontologyUrl ) ) {
@@ -379,13 +395,15 @@ private synchronized void initialize( @Nullable InputStream stream, boolean forc
379395 }
380396 }
381397
382- this .state = new State ( model , index , excludedWordsFromStemming , additionalRestrictions , languageLevel , inferenceMode , processImports , additionalProperties .stream ().map ( Property ::getURI ).collect ( Collectors .toSet () ), null );
398+ this .state = new State ( model , index , excludedWordsFromStemming , additionalRestrictions , languageLevel ,
399+ inferenceMode , processImports , additionalProperties .stream ().map ( Property ::getURI ).collect ( Collectors .toSet () ),
400+ allowedUriPrefixes , null );
383401 if ( cacheName != null ) {
384402 // now that the terms have been replaced, we can clear old caches
385403 try {
386404 OntologyLoader .deleteOldCache ( cacheName );
387405 } catch ( IOException e ) {
388- log .error ( String . format ( String . format ( "Failed to delete old cache directory for %s ." , this ) , e ) );
406+ log .error ( "Failed to delete old cache directory for {} ." , this , e );
389407 }
390408 }
391409
@@ -422,6 +440,7 @@ public Collection<OntologySearchResult<OntologyIndividual>> findIndividuals( Str
422440 return Collections .emptySet ();
423441 }
424442 return state .index .searchIndividuals ( state .model , search , maxResults ).stream ()
443+ .filter ( i -> state .isUriAllowed ( i .result .getURI () ) )
425444 .map ( i -> as ( i .result , Individual .class ).map ( r -> new OntologySearchResult <>( ( OntologyIndividual ) new OntologyIndividualImpl ( r , state .additionalRestrictions ), i .score ) ) )
426445 .filter ( Optional ::isPresent )
427446 .map ( Optional ::get )
@@ -443,6 +462,7 @@ public Collection<OntologySearchResult<OntologyResource>> findResources( String
443462 return Collections .emptySet ();
444463 }
445464 return state .index .search ( state .model , searchString , maxResults ).stream ()
465+ .filter ( i -> state .isUriAllowed ( i .result .getURI () ) )
446466 .filter ( ( r -> r .result .canAs ( OntClass .class ) || r .result .canAs ( Individual .class ) ) )
447467 .map ( r -> {
448468 if ( r .result .canAs ( OntClass .class ) ) {
@@ -474,6 +494,7 @@ public Collection<OntologySearchResult<OntologyTerm>> findTerm( String search, i
474494 return Collections .emptySet ();
475495 }
476496 return state .index .searchClasses ( state .model , search , maxResults ).stream ()
497+ .filter ( i -> state .isUriAllowed ( i .result .getURI () ) )
477498 .map ( r -> as ( r .result , OntClass .class ).map ( s -> new OntologySearchResult <>( ( OntologyTerm ) new OntologyTermImpl ( s , state .additionalRestrictions ), r .score ) ) )
478499 .filter ( Optional ::isPresent )
479500 .map ( Optional ::get )
@@ -514,6 +535,9 @@ public Set<String> getAllURIs() {
514535 @ Override
515536 public OntologyResource getResource ( String uri ) {
516537 return getState ().map ( state -> {
538+ if ( !state .isUriAllowed ( uri ) ) {
539+ return null ;
540+ }
517541 OntologyResource res ;
518542 Resource resource = state .model .getResource ( uri );
519543 if ( resource .getURI () == null ) {
@@ -536,6 +560,9 @@ public OntologyResource getResource( String uri ) {
536560 @ Override
537561 public OntologyTerm getTerm ( String uri ) {
538562 return getState ().map ( state -> {
563+ if ( !state .isUriAllowed ( uri ) ) {
564+ return null ;
565+ }
539566 OntClass ontCls = state .model .getOntClass ( uri );
540567 // null or bnode
541568 if ( ontCls == null || ontCls .getURI () == null ) {
@@ -564,6 +591,7 @@ public Set<OntologyTerm> getParents( Collection<OntologyTerm> terms, boolean dir
564591 return getState ().map ( state ->
565592 JenaUtils .getParents ( state .model , getOntClassesFromTerms ( state .model , terms ), direct , includeAdditionalProperties ? state .additionalRestrictions : null )
566593 .stream ()
594+ .filter ( o -> state .isUriAllowed ( o .getURI () ) )
567595 .map ( o -> ( OntologyTerm ) new OntologyTermImpl ( o , state .additionalRestrictions ) )
568596 .filter ( o -> keepObsoletes || !o .isObsolete () )
569597 .collect ( Collectors .toSet () ) )
@@ -576,6 +604,7 @@ public Set<OntologyTerm> getChildren( Collection<OntologyTerm> terms, boolean di
576604 return getState ().map ( state ->
577605 JenaUtils .getChildren ( state .model , getOntClassesFromTerms ( state .model , terms ), direct , includeAdditionalProperties ? state .additionalRestrictions : null )
578606 .stream ()
607+ .filter ( o -> state .isUriAllowed ( o .getURI () ) )
579608 .map ( o -> ( OntologyTerm ) new OntologyTermImpl ( o , state .additionalRestrictions ) )
580609 .filter ( o -> keepObsoletes || !o .isObsolete () )
581610 .collect ( Collectors .toSet () )
@@ -667,7 +696,7 @@ public synchronized void index( boolean force ) {
667696 return ;
668697 }
669698 // now we replace the index
670- this .state = new State ( state .model , index , state .excludedWordsFromStemming , state .additionalRestrictions , state .languageLevel , state .inferenceMode , state .processImports , state .additionalPropertyUris , state .alternativeIDs );
699+ this .state = new State ( state .model , index , state .excludedWordsFromStemming , state .additionalRestrictions , state .languageLevel , state .inferenceMode , state .processImports , state .additionalPropertyUris , state .allowedUriPrefixes , state . alternativeIDs );
671700 }
672701
673702 /**
@@ -703,7 +732,7 @@ private State initSearchByAlternativeId( State state ) {
703732 alternativeIDs .put ( baseOntologyUri + alternativeIdModified , ontologyTerm .getUri () );
704733 }
705734 }
706- return new State ( state .model , state .index , state .excludedWordsFromStemming , state .additionalRestrictions , state .languageLevel , state .inferenceMode , state .processImports , state .additionalPropertyUris , alternativeIDs );
735+ return new State ( state .model , state .index , state .excludedWordsFromStemming , state .additionalRestrictions , state .languageLevel , state .inferenceMode , state .processImports , state .additionalPropertyUris , state . allowedUriPrefixes , alternativeIDs );
707736 }
708737
709738 @ Override
@@ -715,8 +744,8 @@ public void close() throws Exception {
715744
716745 @ Override
717746 public String toString () {
718- return String .format ( "%s [url=%s] [language level=%s] [inference mode=%s] [imports=%b] [search=%b]" ,
719- getOntologyName (), getOntologyUrl (), getLanguageLevel (), getInferenceMode (), getProcessImports (), isSearchEnabled () );
747+ return String .format ( "%s [url=%s] [allowed prefixes=%s] [ language level=%s] [inference mode=%s] [imports=%b] [search=%b]" ,
748+ getOntologyName (), getOntologyUrl (), allowedUriPrefixes != null ? String . join ( "," , allowedUriPrefixes ) : "*" , getLanguageLevel (), getInferenceMode (), getProcessImports (), isSearchEnabled () );
720749 }
721750
722751 private Optional <State > getState () {
@@ -747,9 +776,11 @@ private static class State implements AutoCloseable {
747776 private final boolean processImports ;
748777 private final Set <String > additionalPropertyUris ;
749778 @ Nullable
779+ private final Set <String > allowedUriPrefixes ;
780+ @ Nullable
750781 private final Map <String , String > alternativeIDs ;
751782
752- private State ( OntModel model , @ Nullable SearchIndex index , Set <String > excludedWordsFromStemming , Set <Restriction > additionalRestrictions , @ Nullable LanguageLevel languageLevel , InferenceMode inferenceMode , boolean processImports , Set <String > additionalPropertyUris , @ Nullable Map <String , String > alternativeIDs ) {
783+ private State ( OntModel model , @ Nullable SearchIndex index , Set <String > excludedWordsFromStemming , Set <Restriction > additionalRestrictions , @ Nullable LanguageLevel languageLevel , InferenceMode inferenceMode , boolean processImports , Set <String > additionalPropertyUris , @ Nullable Set < String > allowedUriPrefixes , @ Nullable Map <String , String > alternativeIDs ) {
753784 this .model = model ;
754785 this .index = index ;
755786 this .excludedWordsFromStemming = excludedWordsFromStemming ;
@@ -758,9 +789,20 @@ private State( OntModel model, @Nullable SearchIndex index, Set<String> excluded
758789 this .inferenceMode = inferenceMode ;
759790 this .processImports = processImports ;
760791 this .additionalPropertyUris = additionalPropertyUris ;
792+ this .allowedUriPrefixes = allowedUriPrefixes ;
761793 this .alternativeIDs = alternativeIDs ;
762794 }
763795
796+ /**
797+ * Check if this particular state allows a given URI from being returned by the service.
798+ */
799+ public boolean isUriAllowed ( @ Nullable String uri ) {
800+ if ( allowedUriPrefixes == null ) {
801+ return true ;
802+ }
803+ return uri == null || allowedUriPrefixes .stream ().anyMatch ( uri ::startsWith );
804+ }
805+
764806 @ Override
765807 public void close () throws Exception {
766808 try {
0 commit comments