001/*- 002 * #%L 003 * Smile CDR - CDR 004 * %% 005 * Copyright (C) 2016 - 2024 Smile CDR, Inc. 006 * %% 007 * All rights reserved. 008 * #L% 009 */ 010package ca.cdr.api.security.permission; 011 012import jakarta.annotation.Nonnull; 013import org.apache.commons.lang3.StringUtils; 014 015import java.util.Optional; 016 017import static org.apache.commons.lang3.StringUtils.defaultString; 018 019/** 020 * A narrowing filter - does a resource (of type), have a search param (by name), in a ValueSet (via url). 021 * E.g. <code>Observation/code/https://example/valueSetUrl</code> 022 */ 023@SuppressWarnings("JavadocLinkAsPlainText") 024public final class CodeInValueSet extends PermissionArgumentValue 025 implements PermissionArgumentFormat.ICodeInValueSetRestriction { 026 // final for optimizer 027 private final String myResourceType; 028 private final String mySearchParamName; 029 private final String myValueSetUrl; 030 031 CodeInValueSet(String theResourceType, String theSearchParamName, String theValueSetUrl) { 032 myResourceType = theResourceType; 033 mySearchParamName = theSearchParamName; 034 myValueSetUrl = theValueSetUrl; 035 } 036 037 @Override 038 public String getResourceType() { 039 return myResourceType; 040 } 041 042 @Override 043 public String getSearchParamName() { 044 return mySearchParamName; 045 } 046 047 @Override 048 public String getValueSetUrl() { 049 return myValueSetUrl; 050 } 051 052 @Override 053 @Nonnull 054 String getStringValue() { 055 return myResourceType + "/" + mySearchParamName + "/" + myValueSetUrl; 056 } 057 058 static final class Format implements PermissionArgumentFormat<CodeInValueSet> { 059 060 @Override 061 public @Nonnull Optional<CodeInValueSet> parse(String theArgumentString, @Nonnull IdBuilder theIdBuilder) { 062 if (StringUtils.isEmpty(theArgumentString)) { 063 return Optional.empty(); 064 } 065 066 int firstSlashIdx = defaultString(theArgumentString).indexOf('/'); 067 if (firstSlashIdx < 1) { 068 return Optional.empty(); 069 } 070 071 String resourceType = theArgumentString.substring(0, firstSlashIdx); 072 073 int secondSlashIdx = theArgumentString.indexOf('/', firstSlashIdx + 1); 074 if (secondSlashIdx < 1) { 075 return Optional.empty(); 076 } 077 078 String searchParamName = theArgumentString.substring(firstSlashIdx + 1, secondSlashIdx); 079 String valueSetUrl = theArgumentString.substring(secondSlashIdx + 1); 080 081 // todo jr beef up the validation - since the / delimiter also occurs within the url, 082 // there are many ways an invalid argument can still slip through the current set of checks 083 if (StringUtils.isNoneBlank(resourceType, searchParamName, valueSetUrl)) { 084 return Optional.of(new CodeInValueSet(resourceType, searchParamName, valueSetUrl)); 085 } 086 087 return Optional.empty(); 088 } 089 090 @Override 091 public Class<CodeInValueSet> getType() { 092 return CodeInValueSet.class; 093 } 094 095 @Override 096 public @Nonnull String format(@Nonnull CodeInValueSet theArgument) { 097 return theArgument.getStringValue(); 098 } 099 } 100}