Skip to content

Commit afe911e

Browse files
Merge branch 'master' into submit_collection_endpoint
2 parents 9b8d586 + f5d691f commit afe911e

File tree

15 files changed

+725
-172
lines changed

15 files changed

+725
-172
lines changed

src/main/config/run.properties.example

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,19 @@ facility.YFH.idsUrl = https://your.facility.idsUrl.here
2222
# If the property is not defined, Topcat will use the facility's idsUrl by default.
2323

2424
facility.LILS.downloadType.http = http://localhost:8080
25+
facility.LILS.downloadType.list = http
26+
facility.LILS.downloadType.http.displayName = HTTP
27+
facility.LILS.downloadType.http.description = Example description for HTTP access method.
28+
29+
facility.YFH.downloadType.list = https globus
2530
facility.YFH.downloadType.https = https://your.facility.downloadIdsUrl.here
31+
facility.YFH.downloadType.https.displayName = HTTPS
32+
facility.YFH.downloadType.https.description = Example description for HTTPS access method.
33+
facility.YFH.downloadType.https.disallowedAuthn = simple db
2634
facility.YFH.downloadType.globus = https://your.facility.globusUrl.here
35+
facility.YFH.downloadType.globus.displayName = Globus
36+
facility.YFH.downloadType.globus.description = Example description for Globus access method.
37+
facility.YFH.downloadType.globus.allowedGroupings = principal_beamline_scientists admins
2738

2839
# enable send email
2940
mail.enable=true
@@ -36,6 +47,7 @@ mail.enable=true
3647
# ${fileName} - the download name
3748
# ${size} - the download size
3849
# ${downloadUrl} - the download url
50+
# ${customValue} - optionally set by calls to setDownloadStatus where the status is complete
3951
mail.subject=TopCAT Download ${fileName} Ready
4052

4153
# The email body message for https downloads. All subject tokens as above are available.
@@ -50,6 +62,10 @@ mail.body.smartclient=Hi ${userName}, \n\nYour ${size} SmartClient download ${fi
5062
# The email body message for SCARF downloads. All subject tokens as above are available.
5163
mail.body.scarf=Hi ${userName}, \n\nYour ${size} SCARF download ${fileName} is ready. Please see https:/example.com/#/scarf-faq for more information on how to download using SCARF.\n\nThank you for using TopCAT
5264

65+
# The email body message for Ada downloads. All subject tokens as above are available.
66+
mail.body.ada = Hi ${userName}, \n\nYour ${size} recall ${fileName} is ready as an Ada data share. Please visit https:/example.com to redeem access using the following token.\n\n${customValue}\n\nThank you for using DataGateway
67+
mail.required.ada = true
68+
5369
# The maximum number of datafiles for a getStatus call to the IDS for two level storage
5470
ids.getStatus.max=100
5571

@@ -131,8 +147,8 @@ queue.priority.instrumentScientist.instruments = {"ABC": 1}
131147
queue.priority.instrumentScientist.default = 2
132148
queue.priority.investigationUser.roles = {"ABC": 3}
133149
queue.priority.investigationUser.default = 4
134-
queue.priority.authenticated = 5
135-
queue.priority.default = 0
150+
queue.priority.authenticated = {"orcid": 6, "anon": 0}
151+
queue.priority.default = 5
136152

137153
# Configurable limit for the length of the GET URL for requesting Datafiles by a list of file locations
138154
# The exact limit may depend on the server

src/main/java/org/icatproject/topcat/DownloadBuilder.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ public DownloadBuilder(String sessionId, String email, String fileName, String t
4545
queueFilesMaxFileCount = Long.valueOf(properties.getProperty("queue.files.maxFileCount", "10000"));
4646

4747
this.sessionId = sessionId;
48-
this.email = email;
4948
this.fileName = fileName;
5049
this.transport = validateTransport(transport);
5150
this.facilityName = validateFacilityName(facilityName);
51+
this.email = validateEmail(transport, email);
5252
String icatUrl = getIcatUrl(this.facilityName);
5353

5454
icatClient = new IcatClient(icatUrl, sessionId);
@@ -88,6 +88,27 @@ public static String validateFacilityName(String facilityName) throws BadRequest
8888
}
8989
}
9090

91+
/**
92+
* Validate that the submitted email is not null or empty if mail.required is true.
93+
*
94+
* @param transport Transport mechanism to use (which may require email)
95+
* @param email Users email address, which may be null or empty
96+
* @return The original email, or null if it was an empty string
97+
* @throws BadRequestException if email null or empty and mail.required is true
98+
*/
99+
public static String validateEmail(String transport, String email) throws BadRequestException {
100+
if(email != null && email.equals("")){
101+
email = null;
102+
}
103+
104+
String emailRequired = Properties.getInstance().getProperty("mail.required." + transport, "false");
105+
if (Boolean.parseBoolean(emailRequired) && email == null) {
106+
throw new BadRequestException("email is required for " + transport);
107+
}
108+
109+
return email;
110+
}
111+
91112
/**
92113
* @param facilityName ICAT Facility.name
93114
* @return ICAT server url

src/main/java/org/icatproject/topcat/FacilityMap.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.HashMap;
44
import java.util.Map;
5+
import java.util.Set;
56

67
import org.icatproject.topcat.exceptions.InternalException;
78
import org.slf4j.Logger;
@@ -83,7 +84,14 @@ public String validateFacilityName(String facility) throws InternalException {
8384
}
8485
return facility;
8586
}
86-
87+
88+
/**
89+
* @return All the ICAT Facility.names from the config file
90+
*/
91+
public Set<String> getFacilities() {
92+
return facilityIcatUrl.keySet();
93+
}
94+
8795
public String getIcatUrl( String facility ) throws InternalException{
8896
facility = validateFacilityName(facility);
8997
String url = facilityIcatUrl.get( facility );

src/main/java/org/icatproject/topcat/IcatClient.java

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ public int getQueuePriority(String userName) throws TopcatException {
487487
if (userPriority != null) {
488488
return userPriority;
489489
}
490-
HashMap<Integer, String> mapping = priorityMap.getMapping();
490+
HashMap<Integer, String> mapping = priorityMap.getQueryMapping();
491491
List<Integer> keyList = new ArrayList<>(mapping.keySet());
492492
Collections.sort(keyList);
493493
for (Integer priority : keyList) {
@@ -496,13 +496,7 @@ public int getQueuePriority(String userName) throws TopcatException {
496496
}
497497
}
498498

499-
String anonUserName = Properties.getInstance().getProperty("anonUserName");
500-
if (anonUserName == null || !userName.startsWith(anonUserName)) {
501-
// The anonymous cart username will end with the user's sessionId so cannot do .equals
502-
return priorityMap.getAuthenticatedPriority();
503-
} else {
504-
return priorityMap.getDefaultPriority();
505-
}
499+
return priorityMap.getAuthenticatedPriority(userName);
506500
}
507501

508502
/**
@@ -519,9 +513,29 @@ int checkUser(String userName, String condition) throws TopcatException {
519513
return results.size();
520514
}
521515

516+
/**
517+
* @param userName ICAT User.name
518+
* @param groupings ICAT Grouping.names
519+
* @return whether userName is in any of the named groupings
520+
* @throws TopcatException if the query fails
521+
*/
522+
public boolean isInGroups(String userName, Set<String> groupings) throws TopcatException {
523+
String query = "SELECT userGroup FROM UserGroup userGroup WHERE userGroup.user.name = :user";
524+
query += " AND userGroup.grouping.name IN ('" + String.join("','", groupings) + "')";
525+
JsonArray results = submitQuery(query);
526+
return results.size() > 0;
527+
}
528+
522529
protected String[] getAdminUserNames() throws Exception {
523530
return Properties.getInstance().getProperty("adminUserNames", "").split("([ ]*,[ ]*|[ ]+)");
524531
}
525532

533+
/**
534+
* @param sessionId ICAT sessionId
535+
*/
536+
public void setSessionId(String sessionId) {
537+
this.sessionId = sessionId;
538+
}
539+
526540
}
527541

src/main/java/org/icatproject/topcat/PriorityMap.java

Lines changed: 40 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ public synchronized static PriorityMap getInstance() throws InternalException {
2626
String anonUserName;
2727
boolean anonDownloadEnabled;
2828
private int defaultPriority;
29-
private int authenticatedPriority;
29+
private HashMap<String, Integer> authenticatedMapping = new HashMap<>();
3030
private HashMap<String, Integer> userMapping = new HashMap<>();
31-
private HashMap<Integer, String> mapping = new HashMap<>();
31+
private HashMap<Integer, String> queryMapping = new HashMap<>();
3232
private Logger logger = LoggerFactory.getLogger(PriorityMap.class);
3333

3434
public PriorityMap() {
@@ -40,32 +40,22 @@ public PriorityMap() {
4040
+ "authenticated priority will be used as default level");
4141
}
4242
anonDownloadEnabled = Boolean.parseBoolean(properties.getProperty("anonDownloadEnabled", "true"));
43-
String defaultString;
44-
if (anonDownloadEnabled) {
45-
defaultString = properties.getProperty("queue.priority.default", "0");
46-
defaultPriority = Integer.valueOf(defaultString);
47-
} else {
48-
defaultString = "0";
49-
defaultPriority = 0;
50-
}
43+
String defaultString = properties.getProperty("queue.priority.default", "0");
44+
defaultPriority = Integer.valueOf(defaultString);
45+
5146

52-
String authenticatedString = properties.getProperty("queue.priority.authenticated", defaultString);
53-
setAuthenticatedPriority(authenticatedString);
47+
String authenticatedString = properties.getProperty("queue.priority.authenticated", "{}");
48+
parseObject(authenticatedString, authenticatedMapping);
5449

5550
String userString = properties.getProperty("queue.priority.user", "{}");
56-
JsonReader reader = Json.createReader(new ByteArrayInputStream(userString.getBytes()));
57-
JsonObject object = reader.readObject();
58-
for (String key : object.keySet()) {
59-
int priority = object.getInt(key);
60-
userMapping.put(key, priority);
61-
}
51+
parseObject(userString, userMapping);
6252

6353
String property = "queue.priority.investigationUser.default";
64-
String investigationUserString = properties.getProperty(property, authenticatedString);
54+
String investigationUserString = properties.getProperty(property, defaultString);
6555
updateMapping(Integer.valueOf(investigationUserString), "user.investigationUsers IS NOT EMPTY");
6656

6757
property = "queue.priority.instrumentScientist.default";
68-
String instrumentScientistString = properties.getProperty(property, authenticatedString);
58+
String instrumentScientistString = properties.getProperty(property, defaultString);
6959
updateMapping(Integer.valueOf(instrumentScientistString), "user.instrumentScientists IS NOT EMPTY");
7060

7161
String investigationUserProperty = properties.getProperty("queue.priority.investigationUser.roles");
@@ -93,25 +83,18 @@ public void checkAnonDownloadEnabled(String userName) throws ForbiddenException
9383
}
9484

9585
/**
96-
* Set the minimum priority for all authenticated Users. This cannot be lower
97-
* than the defaultPriority, which will be used instead if this is the case.
86+
* Extracts a String key to priority level mapping for any criteria (user, authn).
9887
*
99-
* @param authenticatedString The value read from the run.properties file
88+
* @param propertyString String representing a JsonObject from the run.properties
89+
* file, or {}
90+
* @param mapping HashMap from String key to numeric priority level
10091
*/
101-
private void setAuthenticatedPriority(String authenticatedString) {
102-
authenticatedPriority = Integer.valueOf(authenticatedString);
103-
if (authenticatedPriority < 1 && defaultPriority >= 1) {
104-
String msg = "queue.priority.authenticated disabled with value " + authenticatedString;
105-
msg += " but queue.priority.default enabled with value " + defaultPriority;
106-
msg += "\nAuthenticated users will use default priority if no superseding priority applies";
107-
logger.warn(msg);
108-
authenticatedPriority = defaultPriority;
109-
} else if (authenticatedPriority >= 1 && authenticatedPriority > defaultPriority) {
110-
String msg = "queue.priority.authenticated enabled with value " + authenticatedString;
111-
msg += " but queue.priority.default supersedes with value " + defaultPriority;
112-
msg += "\nAuthenticated users will use default priority if no superseding priority applies";
113-
logger.warn(msg);
114-
authenticatedPriority = defaultPriority;
92+
private void parseObject(String propertyString, HashMap<String, Integer> mapping) {
93+
JsonReader reader = Json.createReader(new ByteArrayInputStream(propertyString.getBytes()));
94+
JsonObject object = reader.readObject();
95+
for (String key : object.keySet()) {
96+
int priority = object.getInt(key);
97+
mapping.put(key, priority);
11598
}
11699
}
117100

@@ -147,25 +130,25 @@ private void updateMapping(int priority, String newCondition) {
147130
if (priority < 1) {
148131
logger.warn("Non-positive priority found in mapping, ignoring entry");
149132
return;
150-
} else if (authenticatedPriority >= 1 && priority >= authenticatedPriority) {
151-
logger.warn("Priority set in mapping would be superseded by queue.priority.authenticated, ignoring entry");
133+
} else if (defaultPriority >= 1 && priority >= defaultPriority) {
134+
logger.warn("Priority set in mapping would be superseded by queue.priority.default, ignoring entry");
152135
return;
153136
}
154137

155-
String oldCondition = mapping.get(priority);
138+
String oldCondition = queryMapping.get(priority);
156139
if (oldCondition != null) {
157-
mapping.put(priority, oldCondition + " OR " + newCondition);
140+
queryMapping.put(priority, oldCondition + " OR " + newCondition);
158141
} else {
159-
mapping.put(priority, newCondition);
142+
queryMapping.put(priority, newCondition);
160143
}
161144
}
162145

163146
/**
164147
* @return Mapping of priority level to a JPQL condition which defines the Users
165148
* who have this priority
166149
*/
167-
public HashMap<Integer, String> getMapping() {
168-
return mapping;
150+
public HashMap<Integer, String> getQueryMapping() {
151+
return queryMapping;
169152
}
170153

171154
/**
@@ -177,14 +160,23 @@ public Integer getUserPriority(String userName) {
177160
}
178161

179162
/**
180-
* @return The priority which applies to all authenticated users
163+
* @param userName String in the format prefix/userName
164+
* @return Relevant priority if prefix present and recognised,
165+
* otherwise defaultPriority
181166
*/
182-
public int getAuthenticatedPriority() {
183-
return authenticatedPriority;
167+
public Integer getAuthenticatedPriority(String userName) {
168+
int index = userName.indexOf("/");
169+
if (index < 0) {
170+
String format = "No explicit authentication mechanism for {}, using default priority {}";
171+
logger.debug(format, userName, defaultPriority);
172+
return defaultPriority;
173+
}
174+
String prefix = userName.substring(0, index);
175+
return authenticatedMapping.getOrDefault(prefix, defaultPriority);
184176
}
185177

186178
/**
187-
* @return The priority which applies to all users, included anonymous access
179+
* @return The priority which applies to any user without a specific setting.
188180
*/
189181
public int getDefaultPriority() {
190182
return defaultPriority;

src/main/java/org/icatproject/topcat/StatusCheck.java

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -172,13 +172,15 @@ private void performCheck(Download download, IdsClient injectedIdsClient) {
172172
if( idsClient == null ) {
173173
idsClient = new IdsClient(getDownloadUrl(download.getFacilityName(),download.getTransport()));
174174
}
175-
if(!download.getIsEmailSent() && download.getStatus() == DownloadStatus.COMPLETE){
175+
if (download.getStatus() == DownloadStatus.COMPLETE) {
176176
logger.info("Download COMPLETE for " + download.getFileName() + " " + download.getId() + "; checking whether to send email...");
177-
download.setIsEmailSent(true);
178-
em.persist(download);
179-
em.flush();
177+
if (!download.getIsEmailSent()) {
178+
sendDownloadReadyEmail(download);
179+
download.setIsEmailSent(true);
180+
em.persist(download);
181+
em.flush();
182+
}
180183
lastChecks.remove(download.getId());
181-
sendDownloadReadyEmail(download);
182184
} else if(download.getTransport().matches("https|http") && idsClient.isPrepared(download.getPreparedId())){
183185
logger.info("Download (http[s]) for " + download.getFileName() + " " + download.getId() + " is Prepared, so setting COMPLETE and checking email...");
184186
download.setStatus(DownloadStatus.COMPLETE);
@@ -203,7 +205,28 @@ private void performCheck(Download download, IdsClient injectedIdsClient) {
203205
}
204206
}
205207

206-
private void sendDownloadReadyEmail(Download download) throws InternalException{
208+
/**
209+
* Send an email for a completed Download.
210+
*
211+
* @param download Download that has completed
212+
* @throws InternalException If download does not have a valid facilityName
213+
*/
214+
private void sendDownloadReadyEmail(Download download) throws InternalException {
215+
String downloadUrl = getDownloadUrl(download.getFacilityName(),download.getTransport());
216+
downloadUrl += "/ids/getData?preparedId=" + download.getPreparedId();
217+
downloadUrl += "&outname=" + download.getFileName();
218+
sendDownloadReadyEmail(mailSession, download, downloadUrl, null);
219+
}
220+
221+
/**
222+
* Send an email for a completed Download.
223+
*
224+
* @param mailSession Jakarta mail session to use
225+
* @param download Download that has completed
226+
* @param downloadUrl URL that provides the recipient access to their data
227+
* @param customValue Custom value to substitute into the message body, set by Pollcat
228+
*/
229+
public static void sendDownloadReadyEmail(Session mailSession, Download download, String downloadUrl, String customValue) {
207230
EmailValidator emailValidator = EmailValidator.getInstance();
208231
Properties properties = Properties.getInstance();
209232

@@ -216,10 +239,6 @@ private void sendDownloadReadyEmail(Download download) throws InternalException{
216239
userName = fullName;
217240
}
218241

219-
String downloadUrl = getDownloadUrl(download.getFacilityName(),download.getTransport());
220-
downloadUrl += "/ids/getData?preparedId=" + download.getPreparedId();
221-
downloadUrl += "&outname=" + download.getFileName();
222-
223242
Map<String, String> valuesMap = new HashMap<String, String>();
224243
valuesMap.put("email", download.getEmail());
225244
valuesMap.put("userName", userName);
@@ -228,6 +247,7 @@ private void sendDownloadReadyEmail(Download download) throws InternalException{
228247
valuesMap.put("downloadUrl", downloadUrl);
229248
valuesMap.put("fileName", download.getFileName());
230249
valuesMap.put("size", Utils.bytesToHumanReadable(download.getSize()));
250+
valuesMap.put("customValue", customValue);
231251

232252
StringSubstitutor sub = new StringSubstitutor(valuesMap);
233253
String subject = sub.replace(properties.getProperty("mail.subject", "mail.subject not set in run.properties"));

0 commit comments

Comments
 (0)