• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IQSS / dataverse / #22985

23 Aug 2024 06:32PM CUT coverage: 20.61% (-0.2%) from 20.791%
#22985

Pull #10781

github

landreev
added an upfront locks check to the /addGlobusFiles api #10623
Pull Request #10781: Improved handling of Globus uploads

4 of 417 new or added lines in 15 files covered. (0.96%)

4194 existing lines in 35 files now uncovered.

17388 of 84365 relevant lines covered (20.61%)

0.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/main/java/edu/harvard/iq/dataverse/api/Admin.java
1
package edu.harvard.iq.dataverse.api;
2

3
import edu.harvard.iq.dataverse.BannerMessage;
4
import edu.harvard.iq.dataverse.BannerMessageServiceBean;
5
import edu.harvard.iq.dataverse.BannerMessageText;
6
import edu.harvard.iq.dataverse.DataFile;
7
import edu.harvard.iq.dataverse.DataFileServiceBean;
8
import edu.harvard.iq.dataverse.Dataset;
9
import edu.harvard.iq.dataverse.DatasetServiceBean;
10
import edu.harvard.iq.dataverse.DatasetVersion;
11
import edu.harvard.iq.dataverse.DatasetVersionServiceBean;
12
import edu.harvard.iq.dataverse.Dataverse;
13
import edu.harvard.iq.dataverse.DataverseRequestServiceBean;
14
import edu.harvard.iq.dataverse.DataverseServiceBean;
15
import edu.harvard.iq.dataverse.DataverseSession;
16
import edu.harvard.iq.dataverse.DvObject;
17
import edu.harvard.iq.dataverse.DvObjectServiceBean;
18
import edu.harvard.iq.dataverse.api.auth.AuthRequired;
19
import edu.harvard.iq.dataverse.settings.JvmSettings;
20
import edu.harvard.iq.dataverse.util.StringUtil;
21
import edu.harvard.iq.dataverse.validation.EMailValidator;
22
import edu.harvard.iq.dataverse.EjbDataverseEngine;
23
import edu.harvard.iq.dataverse.Template;
24
import edu.harvard.iq.dataverse.TemplateServiceBean;
25
import edu.harvard.iq.dataverse.UserServiceBean;
26
import edu.harvard.iq.dataverse.actionlogging.ActionLogRecord;
27
import edu.harvard.iq.dataverse.api.dto.RoleDTO;
28
import edu.harvard.iq.dataverse.authorization.AuthenticatedUserDisplayInfo;
29
import edu.harvard.iq.dataverse.authorization.AuthenticationProvider;
30
import edu.harvard.iq.dataverse.authorization.UserIdentifier;
31
import edu.harvard.iq.dataverse.authorization.exceptions.AuthenticationProviderFactoryNotFoundException;
32
import edu.harvard.iq.dataverse.authorization.exceptions.AuthorizationSetupException;
33
import edu.harvard.iq.dataverse.authorization.groups.GroupServiceBean;
34
import edu.harvard.iq.dataverse.authorization.providers.AuthenticationProviderRow;
35
import edu.harvard.iq.dataverse.authorization.providers.builtin.BuiltinUser;
36
import edu.harvard.iq.dataverse.authorization.providers.builtin.BuiltinUserServiceBean;
37
import edu.harvard.iq.dataverse.authorization.providers.shib.ShibAuthenticationProvider;
38
import edu.harvard.iq.dataverse.authorization.providers.shib.ShibServiceBean;
39
import edu.harvard.iq.dataverse.authorization.providers.shib.ShibUtil;
40
import edu.harvard.iq.dataverse.authorization.users.ApiToken;
41
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
42
import edu.harvard.iq.dataverse.confirmemail.ConfirmEmailData;
43
import edu.harvard.iq.dataverse.confirmemail.ConfirmEmailException;
44
import edu.harvard.iq.dataverse.confirmemail.ConfirmEmailInitResponse;
45
import edu.harvard.iq.dataverse.dataaccess.DataAccess;
46
import edu.harvard.iq.dataverse.dataaccess.DataAccessOption;
47
import edu.harvard.iq.dataverse.dataaccess.StorageIO;
48
import edu.harvard.iq.dataverse.engine.command.impl.AbstractSubmitToArchiveCommand;
49
import edu.harvard.iq.dataverse.engine.command.impl.PublishDataverseCommand;
50
import edu.harvard.iq.dataverse.settings.Setting;
51
import jakarta.json.Json;
52
import jakarta.json.JsonArrayBuilder;
53
import jakarta.json.JsonObjectBuilder;
54
import jakarta.ws.rs.Consumes;
55
import jakarta.ws.rs.DELETE;
56
import jakarta.ws.rs.GET;
57
import jakarta.ws.rs.POST;
58
import jakarta.ws.rs.PUT;
59
import jakarta.ws.rs.Path;
60
import jakarta.ws.rs.PathParam;
61
import jakarta.ws.rs.container.ContainerRequestContext;
62
import jakarta.ws.rs.core.Context;
63
import jakarta.ws.rs.core.Response;
64
import static edu.harvard.iq.dataverse.util.json.NullSafeJsonBuilder.jsonObjectBuilder;
65

66
import java.io.InputStream;
67
import java.io.StringReader;
68
import java.util.Map;
69
import java.util.Map.Entry;
70
import java.util.logging.Level;
71
import java.util.logging.Logger;
72
import jakarta.ejb.EJB;
73
import jakarta.ejb.Stateless;
74
import jakarta.json.JsonObject;
75
import jakarta.json.JsonReader;
76
import jakarta.validation.ConstraintViolation;
77
import jakarta.validation.ConstraintViolationException;
78
import jakarta.ws.rs.Produces;
79
import jakarta.ws.rs.core.Response.Status;
80

81
import org.apache.commons.io.IOUtils;
82

83
import java.util.List;
84
import edu.harvard.iq.dataverse.authorization.AuthTestDataServiceBean;
85
import edu.harvard.iq.dataverse.authorization.AuthenticationProvidersRegistrationServiceBean;
86
import edu.harvard.iq.dataverse.authorization.DataverseRole;
87
import edu.harvard.iq.dataverse.authorization.RoleAssignee;
88
import edu.harvard.iq.dataverse.authorization.UserRecordIdentifier;
89
import edu.harvard.iq.dataverse.authorization.groups.impl.explicit.ExplicitGroupServiceBean;
90
import edu.harvard.iq.dataverse.authorization.users.User;
91
import edu.harvard.iq.dataverse.dataaccess.ImageThumbConverter;
92
import edu.harvard.iq.dataverse.dataset.DatasetThumbnail;
93
import edu.harvard.iq.dataverse.dataset.DatasetUtil;
94
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
95
import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
96
import edu.harvard.iq.dataverse.engine.command.impl.DeactivateUserCommand;
97
import edu.harvard.iq.dataverse.engine.command.impl.DeleteRoleCommand;
98
import edu.harvard.iq.dataverse.engine.command.impl.DeleteTemplateCommand;
99
import edu.harvard.iq.dataverse.engine.command.impl.RegisterDvObjectCommand;
100
import edu.harvard.iq.dataverse.ingest.IngestServiceBean;
101
import edu.harvard.iq.dataverse.pidproviders.handle.HandlePidProvider;
102
import edu.harvard.iq.dataverse.settings.SettingsServiceBean;
103
import edu.harvard.iq.dataverse.userdata.UserListMaker;
104
import edu.harvard.iq.dataverse.userdata.UserListResult;
105
import edu.harvard.iq.dataverse.util.ArchiverUtil;
106
import edu.harvard.iq.dataverse.util.BundleUtil;
107
import edu.harvard.iq.dataverse.util.FileUtil;
108
import edu.harvard.iq.dataverse.util.SystemConfig;
109
import edu.harvard.iq.dataverse.util.URLTokenUtil;
110
import edu.harvard.iq.dataverse.util.UrlSignerUtil;
111

112
import java.io.FileInputStream;
113
import java.io.IOException;
114
import java.io.OutputStream;
115

116
import static edu.harvard.iq.dataverse.util.json.JsonPrinter.json;
117
import static edu.harvard.iq.dataverse.util.json.JsonPrinter.rolesToJson;
118
import static edu.harvard.iq.dataverse.util.json.JsonPrinter.toJsonArray;
119
import java.util.ArrayList;
120
import java.util.Arrays;
121
import java.util.Date;
122
import jakarta.inject.Inject;
123
import jakarta.json.JsonArray;
124
import jakarta.persistence.Query;
125
import jakarta.ws.rs.QueryParam;
126
import jakarta.ws.rs.WebApplicationException;
127
import jakarta.ws.rs.core.StreamingOutput;
128
import java.nio.file.Paths;
129

130
/**
131
 * Where the secure, setup API calls live.
132
 * 
133
 * @author michael
134
 */
135
@Stateless
136
@Path("admin")
UNCOV
137
public class Admin extends AbstractApiBean {
×
138

UNCOV
139
        private static final Logger logger = Logger.getLogger(Admin.class.getName());
×
140

141
    @EJB
142
    AuthenticationProvidersRegistrationServiceBean authProvidersRegistrationSvc;
143
    @EJB
144
    BuiltinUserServiceBean builtinUserService;
145
    @EJB
146
    ShibServiceBean shibService;
147
    @EJB
148
    AuthTestDataServiceBean authTestDataService;
149
    @EJB
150
    UserServiceBean userService;
151
    @EJB
152
    IngestServiceBean ingestService;
153
    @EJB
154
    DataFileServiceBean fileService;
155
    @EJB
156
    DatasetServiceBean datasetService;
157
    @EJB
158
    DataverseServiceBean dataverseService;
159
    @EJB
160
    DvObjectServiceBean dvObjectService;
161
    @EJB
162
    DatasetVersionServiceBean datasetversionService;
163
    @Inject
164
    DataverseRequestServiceBean dvRequestService;
165
    @EJB
166
    EjbDataverseEngine commandEngine;
167
    @EJB
168
    GroupServiceBean groupService;
169
    @EJB
170
    SettingsServiceBean settingsService;
171
    @EJB
172
    DatasetVersionServiceBean datasetVersionService;
173
    @EJB
174
    ExplicitGroupServiceBean explicitGroupService;
175
    @EJB
176
    BannerMessageServiceBean bannerMessageService;
177
    @EJB
178
    TemplateServiceBean templateService;
179

180
    // Make the session available
181
    @Inject
182
    DataverseSession session;
183

184
        public static final String listUsersPartialAPIPath = "list-users";
185
        public static final String listUsersFullAPIPath = "/api/admin/" + listUsersPartialAPIPath;
186

187
        @Path("settings")
188
        @GET
189
        public Response listAllSettings() {
UNCOV
190
                JsonObjectBuilder bld = jsonObjectBuilder();
×
191
                settingsSvc.listAll().forEach(s -> bld.add(s.getName(), s.getContent()));
×
192
                return ok(bld);
×
193
        }
194

195
        @Path("settings/{name}")
196
        @PUT
197
        public Response putSetting(@PathParam("name") String name, String content) {
UNCOV
198
                Setting s = settingsSvc.set(name, content);
×
199
                return ok(jsonObjectBuilder().add(s.getName(), s.getContent()));
×
200
        }
201

202
        @Path("settings/{name}/lang/{lang}")
203
        @PUT
204
        public Response putSettingLang(@PathParam("name") String name, @PathParam("lang") String lang, String content) {
UNCOV
205
                Setting s = settingsSvc.set(name, lang, content);
×
206
                return ok("Setting " + name + " - " + lang + " - added.");
×
207
        }
208

209
        @Path("settings/{name}")
210
        @GET
211
        public Response getSetting(@PathParam("name") String name) {
UNCOV
212
                String s = settingsSvc.get(name);
×
213

UNCOV
214
                return (s != null) ? ok(s) : notFound("Setting " + name + " not found");
×
215
        }
216

217
        @Path("settings/{name}")
218
        @DELETE
219
        public Response deleteSetting(@PathParam("name") String name) {
UNCOV
220
                settingsSvc.delete(name);
×
221

UNCOV
222
                return ok("Setting " + name + " deleted.");
×
223
        }
224

225
        @Path("settings/{name}/lang/{lang}")
226
        @DELETE
227
        public Response deleteSettingLang(@PathParam("name") String name, @PathParam("lang") String lang) {
UNCOV
228
                settingsSvc.delete(name, lang);
×
229
                return ok("Setting " + name + " - " + lang + " deleted.");
×
230
        }
231
        
232
    @Path("template/{id}")
233
    @DELETE
234
    public Response deleteTemplate(@PathParam("id") long id) {
235
        
UNCOV
236
        AuthenticatedUser superuser = authSvc.getAdminUser();
×
237
        if (superuser == null) {
×
238
            return error(Response.Status.INTERNAL_SERVER_ERROR, "Cannot find superuser to execute DeleteTemplateCommand.");
×
239
        }
240

UNCOV
241
        Template doomed = templateService.find(id);
×
242
        if (doomed == null) {
×
243
            return error(Response.Status.NOT_FOUND, "Template with id " + id + " -  not found.");
×
244
        }
245

UNCOV
246
        Dataverse dv = doomed.getDataverse();
×
247
        List <Dataverse> dataverseWDefaultTemplate = templateService.findDataversesByDefaultTemplateId(doomed.getId());
×
248

249
        try {
UNCOV
250
            commandEngine.submit(new DeleteTemplateCommand(createDataverseRequest(superuser), dv, doomed, dataverseWDefaultTemplate));
×
251
        } catch (CommandException ex) {
×
252
            Logger.getLogger(Admin.class.getName()).log(Level.SEVERE, null, ex);
×
253
            return error(Response.Status.BAD_REQUEST, ex.getLocalizedMessage());
×
254
        }
×
255

UNCOV
256
        return ok("Template " + doomed.getName() + " deleted.");
×
257
    }
258
    
259
    
260
    @Path("templates")
261
    @GET
262
    public Response findAllTemplates() {
UNCOV
263
        return findTemplates("");
×
264
    }
265
    
266
    @Path("templates/{alias}")
267
    @GET
268
    public Response findTemplates(@PathParam("alias") String alias) {
269
        List<Template> templates;
270

UNCOV
271
            if (alias.isEmpty()) {
×
272
                templates = templateService.findAll();
×
273
            } else {
274
                try{
UNCOV
275
                    Dataverse owner = findDataverseOrDie(alias);
×
276
                    templates = templateService.findByOwnerId(owner.getId());
×
277
                } catch (WrappedResponse r){
×
278
                    return r.getResponse();
×
279
                }
×
280
            }
281

UNCOV
282
            JsonArrayBuilder container = Json.createArrayBuilder();
×
283
            for (Template t : templates) {
×
284
                JsonObjectBuilder bld = Json.createObjectBuilder();
×
285
                bld.add("templateId", t.getId());
×
286
                bld.add("templateName", t.getName());
×
287
                Dataverse loopowner = t.getDataverse();
×
288
                if (loopowner != null) {
×
289
                    bld.add("owner", loopowner.getDisplayName());
×
290
                } else {
UNCOV
291
                    bld.add("owner", "This an orphan template, it may be safely removed");
×
292
                }
UNCOV
293
                container.add(bld);
×
294
            }
×
295

UNCOV
296
            return ok(container);
×
297

298
        
299
    }
300

301
        @Path("authenticationProviderFactories")
302
        @GET
303
        public Response listAuthProviderFactories() {
UNCOV
304
                return ok(authSvc.listProviderFactories().stream()
×
305
                                .map(f -> jsonObjectBuilder().add("alias", f.getAlias()).add("info", f.getInfo()))
×
306
                                .collect(toJsonArray()));
×
307
        }
308

309
        @Path("authenticationProviders")
310
        @GET
311
        public Response listAuthProviders() {
UNCOV
312
                return ok(em.createNamedQuery("AuthenticationProviderRow.findAll", AuthenticationProviderRow.class)
×
313
                                .getResultList().stream().map(r -> json(r)).collect(toJsonArray()));
×
314
        }
315

316
        @Path("authenticationProviders")
317
        @POST
318
        public Response addProvider(AuthenticationProviderRow row) {
319
                try {
UNCOV
320
                        AuthenticationProviderRow managed = em.find(AuthenticationProviderRow.class, row.getId());
×
321
                        if (managed != null) {
×
322
                                managed = em.merge(row);
×
323
                        } else {
UNCOV
324
                                em.persist(row);
×
325
                                managed = row;
×
326
                        }
UNCOV
327
                        if (managed.isEnabled()) {
×
328
                                AuthenticationProvider provider = authProvidersRegistrationSvc.loadProvider(managed);
×
329
                                authProvidersRegistrationSvc.deregisterProvider(provider.getId());
×
330
                                authProvidersRegistrationSvc.registerProvider(provider);
×
331
                        }
UNCOV
332
                        return created("/api/admin/authenticationProviders/" + managed.getId(), json(managed));
×
333
                } catch (AuthorizationSetupException e) {
×
334
                        return error(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage());
×
335
                }
336
        }
337

338
        @Path("authenticationProviders/{id}")
339
        @GET
340
        public Response showProvider(@PathParam("id") String id) {
UNCOV
341
                AuthenticationProviderRow row = em.find(AuthenticationProviderRow.class, id);
×
342
                return (row != null) ? ok(json(row))
×
343
                                : error(Status.NOT_FOUND, "Can't find authetication provider with id '" + id + "'");
×
344
        }
345

346
        @POST
347
        @Path("authenticationProviders/{id}/:enabled")
348
        public Response enableAuthenticationProvider_deprecated(@PathParam("id") String id, String body) {
UNCOV
349
                return enableAuthenticationProvider(id, body);
×
350
        }
351

352
        @PUT
353
        @Path("authenticationProviders/{id}/enabled")
354
        @Produces("application/json")
355
        public Response enableAuthenticationProvider(@PathParam("id") String id, String body) {
UNCOV
356
                body = body.trim();
×
357
                if (!Util.isBoolean(body)) {
×
358
                        return error(Response.Status.BAD_REQUEST, "Illegal value '" + body + "'. Use 'true' or 'false'");
×
359
                }
UNCOV
360
                boolean enable = Util.isTrue(body);
×
361

UNCOV
362
                AuthenticationProviderRow row = em.find(AuthenticationProviderRow.class, id);
×
363
                if (row == null) {
×
364
                        return notFound("Can't find authentication provider with id '" + id + "'");
×
365
                }
366

UNCOV
367
                row.setEnabled(enable);
×
368
                em.merge(row);
×
369

UNCOV
370
                if (enable) {
×
371
                        // enable a provider
UNCOV
372
                        if (authSvc.getAuthenticationProvider(id) != null) {
×
373
                                return ok(String.format("Authentication provider '%s' already enabled", id));
×
374
                        }
375
                        try {
UNCOV
376
                                authProvidersRegistrationSvc.registerProvider(authProvidersRegistrationSvc.loadProvider(row));
×
377
                                return ok(String.format("Authentication Provider %s enabled", row.getId()));
×
378

UNCOV
379
                        } catch (AuthenticationProviderFactoryNotFoundException ex) {
×
380
                                return notFound(String.format("Can't instantiate provider, as there's no factory with alias %s",
×
381
                                                row.getFactoryAlias()));
×
382
                        } catch (AuthorizationSetupException ex) {
×
383
                                logger.log(Level.WARNING, "Error instantiating authentication provider: " + ex.getMessage(), ex);
×
384
                                return error(Status.INTERNAL_SERVER_ERROR,
×
385
                                                String.format("Can't instantiate provider: %s", ex.getMessage()));
×
386
                        }
387

388
                } else {
389
                        // disable a provider
UNCOV
390
                        authProvidersRegistrationSvc.deregisterProvider(id);
×
391
                        return ok("Authentication Provider '" + id + "' disabled. "
×
392
                                        + (authSvc.getAuthenticationProviderIds().isEmpty()
×
393
                                                        ? "WARNING: no enabled authentication providers left."
×
394
                                                        : ""));
×
395
                }
396
        }
397

398
        @GET
399
        @Path("authenticationProviders/{id}/enabled")
400
        public Response checkAuthenticationProviderEnabled(@PathParam("id") String id) {
UNCOV
401
                List<AuthenticationProviderRow> prvs = em
×
402
                                .createNamedQuery("AuthenticationProviderRow.findById", AuthenticationProviderRow.class)
×
403
                                .setParameter("id", id).getResultList();
×
404
                if (prvs.isEmpty()) {
×
405
                        return notFound("Can't find a provider with id '" + id + "'.");
×
406
                } else {
UNCOV
407
                        return ok(Boolean.toString(prvs.get(0).isEnabled()));
×
408
                }
409
        }
410

411
        @DELETE
412
        @Path("authenticationProviders/{id}/")
413
        public Response deleteAuthenticationProvider(@PathParam("id") String id) {
UNCOV
414
                authProvidersRegistrationSvc.deregisterProvider(id);
×
415
                AuthenticationProviderRow row = em.find(AuthenticationProviderRow.class, id);
×
416
                if (row != null) {
×
417
                        em.remove(row);
×
418
                }
419

UNCOV
420
                return ok("AuthenticationProvider " + id + " deleted. "
×
421
                                + (authSvc.getAuthenticationProviderIds().isEmpty()
×
422
                                                ? "WARNING: no enabled authentication providers left."
×
423
                                                : ""));
×
424
        }
425

426
    @GET
427
    @Path("authenticatedUsers/{identifier}/")
428
    public Response getAuthenticatedUserByIdentifier(@PathParam("identifier") String identifier) {
UNCOV
429
        AuthenticatedUser authenticatedUser = authSvc.getAuthenticatedUser(identifier);
×
430
        if (authenticatedUser != null) {
×
431
            return ok(json(authenticatedUser));
×
432
        }
UNCOV
433
        return error(Response.Status.BAD_REQUEST, "User " + identifier + " not found.");
×
434
    }
435

436
    @DELETE
437
    @Path("authenticatedUsers/{identifier}/")
438
    public Response deleteAuthenticatedUser(@PathParam("identifier") String identifier) {
UNCOV
439
        AuthenticatedUser user = authSvc.getAuthenticatedUser(identifier);
×
440
        if (user != null) {
×
441
            return deleteAuthenticatedUser(user);
×
442
        }
UNCOV
443
        return error(Response.Status.BAD_REQUEST, "User " + identifier + " not found.");
×
444
    }
445
    
446
    @DELETE
447
    @Path("authenticatedUsers/id/{id}/")
448
    public Response deleteAuthenticatedUserById(@PathParam("id") Long id) {
UNCOV
449
        AuthenticatedUser user = authSvc.findByID(id);
×
450
        if (user != null) {
×
451
            return deleteAuthenticatedUser(user);
×
452
        }
UNCOV
453
        return error(Response.Status.BAD_REQUEST, "User " + id + " not found.");
×
454
    }
455

456
    private Response deleteAuthenticatedUser(AuthenticatedUser au) {
457
        
458
        //getDeleteUserErrorMessages does all of the tests to see
459
        //if the user is 'deletable' if it returns an empty string the user 
460
        //can be safely deleted.
461
        
UNCOV
462
        String errorMessages = authSvc.getDeleteUserErrorMessages(au);
×
463
        
UNCOV
464
        if (!errorMessages.isEmpty()) {
×
465
            return badRequest(errorMessages);
×
466
        }
467
        
468
        //if the user is deletable we will delete access requests and group membership
469
        // many-to-many relationships that couldn't be cascade deleted
UNCOV
470
        authSvc.removeAuthentictedUserItems(au);
×
471
        
UNCOV
472
        authSvc.deleteAuthenticatedUser(au.getId());
×
473
        return ok("AuthenticatedUser " + au.getIdentifier() + " deleted.");
×
474
    }
475

476
    @POST
477
    @Path("authenticatedUsers/{identifier}/deactivate")
478
    public Response deactivateAuthenticatedUser(@PathParam("identifier") String identifier) {
UNCOV
479
        AuthenticatedUser user = authSvc.getAuthenticatedUser(identifier);
×
480
        if (user != null) {
×
481
            return deactivateAuthenticatedUser(user);
×
482
        }
UNCOV
483
        return error(Response.Status.BAD_REQUEST, "User " + identifier + " not found.");
×
484
    }
485

486
    @POST
487
    @Path("authenticatedUsers/id/{id}/deactivate")
488
    public Response deactivateAuthenticatedUserById(@PathParam("id") Long id) {
UNCOV
489
        AuthenticatedUser user = authSvc.findByID(id);
×
490
        if (user != null) {
×
491
            return deactivateAuthenticatedUser(user);
×
492
        }
UNCOV
493
        return error(Response.Status.BAD_REQUEST, "User " + id + " not found.");
×
494
    }
495

496
    private Response deactivateAuthenticatedUser(AuthenticatedUser userToDisable) {
UNCOV
497
        AuthenticatedUser superuser = authSvc.getAdminUser();
×
498
        if (superuser == null) {
×
499
            return error(Response.Status.INTERNAL_SERVER_ERROR, "Cannot find superuser to execute DeactivateUserCommand.");
×
500
        }
501
        try {
UNCOV
502
            execCommand(new DeactivateUserCommand(createDataverseRequest(superuser), userToDisable));
×
503
            return ok("User " + userToDisable.getIdentifier() + " deactivated.");
×
504
        } catch (WrappedResponse ex) {
×
505
            return ex.getResponse();
×
506
        }
507
    }
508

509
        @POST
510
        @Path("publishDataverseAsCreator/{id}")
511
        public Response publishDataverseAsCreator(@PathParam("id") long id) {
512
                try {
UNCOV
513
                        Dataverse dataverse = dataverseSvc.find(id);
×
514
                        if (dataverse != null) {
×
515
                                AuthenticatedUser authenticatedUser = dataverse.getCreator();
×
516
                                return ok(json(execCommand(
×
517
                                                new PublishDataverseCommand(createDataverseRequest(authenticatedUser), dataverse))));
×
518
                        } else {
UNCOV
519
                                return error(Status.BAD_REQUEST, "Could not find dataverse with id " + id);
×
520
                        }
UNCOV
521
                } catch (WrappedResponse wr) {
×
522
                        return wr.getResponse();
×
523
                }
524
        }
525

526
        @Deprecated
527
        @GET
528
        @AuthRequired
529
        @Path("authenticatedUsers")
530
        public Response listAuthenticatedUsers(@Context ContainerRequestContext crc) {
531
                try {
UNCOV
532
                        AuthenticatedUser user = getRequestAuthenticatedUserOrDie(crc);
×
533
                        if (!user.isSuperuser()) {
×
534
                                return error(Response.Status.FORBIDDEN, "Superusers only.");
×
535
                        }
UNCOV
536
                } catch (WrappedResponse ex) {
×
537
                        return error(Response.Status.FORBIDDEN, "Superusers only.");
×
538
                }
×
539
                JsonArrayBuilder userArray = Json.createArrayBuilder();
×
540
                authSvc.findAllAuthenticatedUsers().stream().forEach((user) -> {
×
541
                        userArray.add(json(user));
×
542
                });
×
543
                return ok(userArray);
×
544
        }
545

546
        @GET
547
        @AuthRequired
548
        @Path(listUsersPartialAPIPath)
549
        @Produces({ "application/json" })
550
        public Response filterAuthenticatedUsers(
551
                        @Context ContainerRequestContext crc,
552
                        @QueryParam("searchTerm") String searchTerm,
553
                        @QueryParam("selectedPage") Integer selectedPage,
554
                        @QueryParam("itemsPerPage") Integer itemsPerPage,
555
                        @QueryParam("sortKey") String sortKey
556
        ) {
557

UNCOV
558
                User authUser = getRequestUser(crc);
×
559

UNCOV
560
                if (!authUser.isSuperuser()) {
×
561
                        return error(Response.Status.FORBIDDEN,
×
562
                                        BundleUtil.getStringFromBundle("dashboard.list_users.api.auth.not_superuser"));
×
563
                }
564

UNCOV
565
                UserListMaker userListMaker = new UserListMaker(userService);
×
566

567
                // String sortKey = null;
UNCOV
568
                UserListResult userListResult = userListMaker.runUserSearch(searchTerm, itemsPerPage, selectedPage, sortKey);
×
569

UNCOV
570
                return ok(userListResult.toJSON());
×
571
        }
572

573
        /**
574
         * @todo Make this support creation of BuiltInUsers.
575
         *
576
         * @todo Add way more error checking. Only the happy path is tested by AdminIT.
577
         */
578
        @POST
579
        @Path("authenticatedUsers")
580
        public Response createAuthenicatedUser(JsonObject jsonObject) {
UNCOV
581
                logger.fine("JSON in: " + jsonObject);
×
582
                String persistentUserId = jsonObject.getString("persistentUserId");
×
583
                String identifier = jsonObject.getString("identifier");
×
584
                String proposedAuthenticatedUserIdentifier = identifier.replaceFirst("@", "");
×
585
                String firstName = jsonObject.getString("firstName");
×
586
                String lastName = jsonObject.getString("lastName");
×
587
                String emailAddress = jsonObject.getString("email");
×
588
                String position = null;
×
589
                String affiliation = null;
×
590
                UserRecordIdentifier userRecordId = new UserRecordIdentifier(jsonObject.getString("authenticationProviderId"),
×
591
                                persistentUserId);
UNCOV
592
                AuthenticatedUserDisplayInfo userDisplayInfo = new AuthenticatedUserDisplayInfo(firstName, lastName,
×
593
                                emailAddress, affiliation, position);
UNCOV
594
                boolean generateUniqueIdentifier = true;
×
595
                AuthenticatedUser authenticatedUser = authSvc.createAuthenticatedUser(userRecordId,
×
596
                                proposedAuthenticatedUserIdentifier, userDisplayInfo, true);
UNCOV
597
                return ok(json(authenticatedUser));
×
598
        }
599

600
        //TODO: Delete this endpoint after 4.9.3. Was updated with change in docs. --MAD
601
        /**
602
         * curl -X PUT -d "shib@mailinator.com"
603
         * http://localhost:8080/api/admin/authenticatedUsers/id/11/convertShibToBuiltIn
604
         *
605
         * @deprecated We have documented this API endpoint so we'll keep in around for
606
         *             a while but we should encourage everyone to switch to the
607
         *             "convertRemoteToBuiltIn" endpoint and then remove this
608
         *             Shib-specfic one.
609
         */
610
        @PUT
611
        @AuthRequired
612
        @Path("authenticatedUsers/id/{id}/convertShibToBuiltIn")
613
        @Deprecated
614
        public Response convertShibUserToBuiltin(@Context ContainerRequestContext crc, @PathParam("id") Long id, String newEmailAddress) {
615
                try {
UNCOV
616
                        AuthenticatedUser user = getRequestAuthenticatedUserOrDie(crc);
×
617
                        if (!user.isSuperuser()) {
×
618
                                return error(Response.Status.FORBIDDEN, "Superusers only.");
×
619
                        }
UNCOV
620
                } catch (WrappedResponse ex) {
×
621
                        return error(Response.Status.FORBIDDEN, "Superusers only.");
×
622
                }
×
623
                try {
UNCOV
624
                        BuiltinUser builtinUser = authSvc.convertRemoteToBuiltIn(id, newEmailAddress);
×
625
                        if (builtinUser == null) {
×
626
                                return error(Response.Status.BAD_REQUEST, "User id " + id
×
627
                                                + " could not be converted from Shibboleth to BuiltIn. An Exception was not thrown.");
628
                        }
UNCOV
629
                        AuthenticatedUser authUser = authSvc.getAuthenticatedUser(builtinUser.getUserName());
×
630
                        JsonObjectBuilder output = Json.createObjectBuilder();
×
631
                        output.add("email", authUser.getEmail());
×
632
                        output.add("username", builtinUser.getUserName());
×
633
                        return ok(output);
×
634
                } catch (Throwable ex) {
×
635
                        StringBuilder sb = new StringBuilder();
×
636
                        sb.append(ex + " ");
×
637
                        while (ex.getCause() != null) {
×
638
                                ex = ex.getCause();
×
639
                                sb.append(ex + " ");
×
640
                        }
UNCOV
641
                        String msg = "User id " + id
×
642
                                        + " could not be converted from Shibboleth to BuiltIn. Details from Exception: " + sb;
UNCOV
643
                        logger.info(msg);
×
644
                        return error(Response.Status.BAD_REQUEST, msg);
×
645
                }
646
        }
647

648
        @PUT
649
        @AuthRequired
650
        @Path("authenticatedUsers/id/{id}/convertRemoteToBuiltIn")
651
        public Response convertOAuthUserToBuiltin(@Context ContainerRequestContext crc, @PathParam("id") Long id, String newEmailAddress) {
652
                try {
UNCOV
653
                        AuthenticatedUser user = getRequestAuthenticatedUserOrDie(crc);
×
654
                        if (!user.isSuperuser()) {
×
655
                                return error(Response.Status.FORBIDDEN, "Superusers only.");
×
656
                        }
UNCOV
657
                } catch (WrappedResponse ex) {
×
658
                        return error(Response.Status.FORBIDDEN, "Superusers only.");
×
659
                }
×
660
                try {
UNCOV
661
                        BuiltinUser builtinUser = authSvc.convertRemoteToBuiltIn(id, newEmailAddress);
×
662
                        //AuthenticatedUser authUser = authService.getAuthenticatedUser(aUser.getUserName());
UNCOV
663
                        if (builtinUser == null) {
×
664
                                return error(Response.Status.BAD_REQUEST, "User id " + id
×
665
                                                + " could not be converted from remote to BuiltIn. An Exception was not thrown.");
666
                        }
UNCOV
667
                        AuthenticatedUser authUser = authSvc.getAuthenticatedUser(builtinUser.getUserName());
×
668
                        JsonObjectBuilder output = Json.createObjectBuilder();
×
669
                        output.add("email", authUser.getEmail());
×
670
                        output.add("username", builtinUser.getUserName());
×
671
                        return ok(output);
×
672
                } catch (Throwable ex) {
×
673
                        StringBuilder sb = new StringBuilder();
×
674
                        sb.append(ex + " ");
×
675
                        while (ex.getCause() != null) {
×
676
                                ex = ex.getCause();
×
677
                                sb.append(ex + " ");
×
678
                        }
UNCOV
679
                        String msg = "User id " + id + " could not be converted from remote to BuiltIn. Details from Exception: "
×
680
                                        + sb;
UNCOV
681
                        logger.info(msg);
×
682
                        return error(Response.Status.BAD_REQUEST, msg);
×
683
                }
684
        }
685

686
        /**
687
         * This is used in testing via AdminIT.java but we don't expect sysadmins to use
688
         * this.
689
         */
690
        @PUT
691
        @AuthRequired
692
        @Path("authenticatedUsers/convert/builtin2shib")
693
        public Response builtin2shib(@Context ContainerRequestContext crc, String content) {
UNCOV
694
                logger.info("entering builtin2shib...");
×
695
                try {
UNCOV
696
                        AuthenticatedUser userToRunThisMethod = getRequestAuthenticatedUserOrDie(crc);
×
697
                        if (!userToRunThisMethod.isSuperuser()) {
×
698
                                return error(Response.Status.FORBIDDEN, "Superusers only.");
×
699
                        }
UNCOV
700
                } catch (WrappedResponse ex) {
×
701
                        return error(Response.Status.FORBIDDEN, "Superusers only.");
×
702
                }
×
703
                boolean disabled = false;
×
704
                if (disabled) {
×
705
                        return error(Response.Status.BAD_REQUEST, "API endpoint disabled.");
×
706
                }
UNCOV
707
                AuthenticatedUser builtInUserToConvert = null;
×
708
                String emailToFind;
709
                String password;
UNCOV
710
                String authuserId = "0"; // could let people specify id on authuser table. probably better to let them
×
711
                                                                        // tell us their
712
                String newEmailAddressToUse;
713
                try {
UNCOV
714
                        String[] args = content.split(":");
×
715
                        emailToFind = args[0];
×
716
                        password = args[1];
×
717
                        newEmailAddressToUse = args[2];
×
718
                        // authuserId = args[666];
UNCOV
719
                } catch (ArrayIndexOutOfBoundsException ex) {
×
720
                        return error(Response.Status.BAD_REQUEST, "Problem with content <<<" + content + ">>>: " + ex.toString());
×
721
                }
×
722
                AuthenticatedUser existingAuthUserFoundByEmail = shibService.findAuthUserByEmail(emailToFind);
×
723
                String existing = "NOT FOUND";
×
724
                if (existingAuthUserFoundByEmail != null) {
×
725
                        builtInUserToConvert = existingAuthUserFoundByEmail;
×
726
                        existing = existingAuthUserFoundByEmail.getIdentifier();
×
727
                } else {
UNCOV
728
                        long longToLookup = Long.parseLong(authuserId);
×
729
                        AuthenticatedUser specifiedUserToConvert = authSvc.findByID(longToLookup);
×
730
                        if (specifiedUserToConvert != null) {
×
731
                                builtInUserToConvert = specifiedUserToConvert;
×
732
                        } else {
UNCOV
733
                                return error(Response.Status.BAD_REQUEST,
×
734
                                                "No user to convert. We couldn't find a *single* existing user account based on " + emailToFind
735
                                                                + " and no user was found using specified id " + longToLookup);
736
                        }
737
                }
UNCOV
738
                String shibProviderId = ShibAuthenticationProvider.PROVIDER_ID;
×
739
                Map<String, String> randomUser = authTestDataService.getRandomUser();
×
740
                // String eppn = UUID.randomUUID().toString().substring(0, 8);
UNCOV
741
                String eppn = randomUser.get("eppn");
×
742
                String idPEntityId = randomUser.get("idp");
×
743
                String notUsed = null;
×
744
                String separator = "|";
×
745
                UserIdentifier newUserIdentifierInLookupTable = new UserIdentifier(idPEntityId + separator + eppn, notUsed);
×
746
                String overwriteFirstName = randomUser.get("firstName");
×
747
                String overwriteLastName = randomUser.get("lastName");
×
748
                String overwriteEmail = randomUser.get("email");
×
749
                overwriteEmail = newEmailAddressToUse;
×
750
                logger.info("overwriteEmail: " + overwriteEmail);
×
751
                boolean validEmail = EMailValidator.isEmailValid(overwriteEmail);
×
752
                if (!validEmail) {
×
753
                        // See https://github.com/IQSS/dataverse/issues/2998
UNCOV
754
                        return error(Response.Status.BAD_REQUEST, "invalid email: " + overwriteEmail);
×
755
                }
756
                /**
757
                 * @todo If affiliation is not null, put it in RoleAssigneeDisplayInfo
758
                 *       constructor.
759
                 */
760
                /**
761
                 * Here we are exercising (via an API test) shibService.getAffiliation with the
762
                 * TestShib IdP and a non-production DevShibAccountType.
763
                 */
UNCOV
764
                idPEntityId = ShibUtil.testShibIdpEntityId;
×
765
                String overwriteAffiliation = shibService.getAffiliation(idPEntityId,
×
766
                                ShibServiceBean.DevShibAccountType.RANDOM);
UNCOV
767
                logger.info("overwriteAffiliation: " + overwriteAffiliation);
×
768
                /**
769
                 * @todo Find a place to put "position" in the authenticateduser table:
770
                 *       https://github.com/IQSS/dataverse/issues/1444#issuecomment-74134694
771
                 */
UNCOV
772
                String overwritePosition = "staff;student";
×
773
                AuthenticatedUserDisplayInfo displayInfo = new AuthenticatedUserDisplayInfo(overwriteFirstName,
×
774
                                overwriteLastName, overwriteEmail, overwriteAffiliation, overwritePosition);
UNCOV
775
                JsonObjectBuilder response = Json.createObjectBuilder();
×
776
                JsonArrayBuilder problems = Json.createArrayBuilder();
×
777
                if (password != null) {
×
778
                        response.add("password supplied", password);
×
779
                        boolean knowsExistingPassword = false;
×
780
                        BuiltinUser oldBuiltInUser = builtinUserService.findByUserName(builtInUserToConvert.getUserIdentifier());
×
781
                        if (oldBuiltInUser != null) {
×
782
                                if (builtInUserToConvert.isDeactivated()) {
×
783
                                        problems.add("builtin account has been deactivated");
×
784
                                        return error(Status.BAD_REQUEST, problems.build().toString());
×
785
                                }
UNCOV
786
                                String usernameOfBuiltinAccountToConvert = oldBuiltInUser.getUserName();
×
787
                                response.add("old username", usernameOfBuiltinAccountToConvert);
×
788
                                AuthenticatedUser authenticatedUser = authSvc.canLogInAsBuiltinUser(usernameOfBuiltinAccountToConvert,
×
789
                                                password);
UNCOV
790
                                if (authenticatedUser != null) {
×
791
                                        knowsExistingPassword = true;
×
792
                                        AuthenticatedUser convertedUser = authSvc.convertBuiltInToShib(builtInUserToConvert, shibProviderId,
×
793
                                                        newUserIdentifierInLookupTable);
UNCOV
794
                                        if (convertedUser != null) {
×
795
                                                /**
796
                                                 * @todo Display name is not being overwritten. Logic must be in Shib backing
797
                                                 *       bean
798
                                                 */
UNCOV
799
                                                AuthenticatedUser updatedInfoUser = authSvc.updateAuthenticatedUser(convertedUser, displayInfo);
×
800
                                                if (updatedInfoUser != null) {
×
801
                                                        response.add("display name overwritten with", updatedInfoUser.getName());
×
802
                                                } else {
UNCOV
803
                                                        problems.add("couldn't update display info");
×
804
                                                }
UNCOV
805
                                        } else {
×
806
                                                problems.add("unable to convert user");
×
807
                                        }
808
                                }
UNCOV
809
                        } else {
×
810
                                problems.add("couldn't find old username");
×
811
                        }
UNCOV
812
                        if (!knowsExistingPassword) {
×
813
                                String message = "User doesn't know password.";
×
814
                                problems.add(message);
×
815
                                /**
816
                                 * @todo Someday we should make a errorResponse method that takes JSON arrays
817
                                 *       and objects.
818
                                 */
UNCOV
819
                                return error(Status.BAD_REQUEST, problems.build().toString());
×
820
                        }
821
                        // response.add("knows existing password", knowsExistingPassword);
822
                }
823

UNCOV
824
                response.add("user to convert", builtInUserToConvert.getIdentifier());
×
825
                response.add("existing user found by email (prompt to convert)", existing);
×
826
                response.add("changing to this provider", shibProviderId);
×
827
                response.add("value to overwrite old first name", overwriteFirstName);
×
828
                response.add("value to overwrite old last name", overwriteLastName);
×
829
                response.add("value to overwrite old email address", overwriteEmail);
×
830
                if (overwriteAffiliation != null) {
×
831
                        response.add("affiliation", overwriteAffiliation);
×
832
                }
UNCOV
833
                response.add("problems", problems);
×
834
                return ok(response);
×
835
        }
836

837
        /**
838
         * This is used in testing via AdminIT.java but we don't expect sysadmins to use
839
         * this.
840
         */
841
        @PUT
842
        @AuthRequired
843
        @Path("authenticatedUsers/convert/builtin2oauth")
844
        public Response builtin2oauth(@Context ContainerRequestContext crc, String content) {
UNCOV
845
                logger.info("entering builtin2oauth...");
×
846
                try {
UNCOV
847
                        AuthenticatedUser userToRunThisMethod = getRequestAuthenticatedUserOrDie(crc);
×
848
                        if (!userToRunThisMethod.isSuperuser()) {
×
849
                                return error(Response.Status.FORBIDDEN, "Superusers only.");
×
850
                        }
UNCOV
851
                } catch (WrappedResponse ex) {
×
852
                        return error(Response.Status.FORBIDDEN, "Superusers only.");
×
853
                }
×
854
                boolean disabled = false;
×
855
                if (disabled) {
×
856
                        return error(Response.Status.BAD_REQUEST, "API endpoint disabled.");
×
857
                }
UNCOV
858
                AuthenticatedUser builtInUserToConvert = null;
×
859
                String emailToFind;
860
                String password;
UNCOV
861
                String authuserId = "0"; // could let people specify id on authuser table. probably better to let them
×
862
                                                                        // tell us their
863
                String newEmailAddressToUse;
864
                String newProviderId;
865
                String newPersistentUserIdInLookupTable;
UNCOV
866
                logger.info("content: " + content);
×
867
                try {
UNCOV
868
                        String[] args = content.split(":");
×
869
                        emailToFind = args[0];
×
870
                        password = args[1];
×
871
                        newEmailAddressToUse = args[2];
×
872
                        newProviderId = args[3];
×
873
                        newPersistentUserIdInLookupTable = args[4];
×
874
                        // authuserId = args[666];
UNCOV
875
                } catch (ArrayIndexOutOfBoundsException ex) {
×
876
                        return error(Response.Status.BAD_REQUEST, "Problem with content <<<" + content + ">>>: " + ex.toString());
×
877
                }
×
878
                AuthenticatedUser existingAuthUserFoundByEmail = shibService.findAuthUserByEmail(emailToFind);
×
879
                String existing = "NOT FOUND";
×
880
                if (existingAuthUserFoundByEmail != null) {
×
881
                        builtInUserToConvert = existingAuthUserFoundByEmail;
×
882
                        existing = existingAuthUserFoundByEmail.getIdentifier();
×
883
                } else {
UNCOV
884
                        long longToLookup = Long.parseLong(authuserId);
×
885
                        AuthenticatedUser specifiedUserToConvert = authSvc.findByID(longToLookup);
×
886
                        if (specifiedUserToConvert != null) {
×
887
                                builtInUserToConvert = specifiedUserToConvert;
×
888
                        } else {
UNCOV
889
                                return error(Response.Status.BAD_REQUEST,
×
890
                                                "No user to convert. We couldn't find a *single* existing user account based on " + emailToFind
891
                                                                + " and no user was found using specified id " + longToLookup);
892
                        }
893
                }
894
                // String shibProviderId = ShibAuthenticationProvider.PROVIDER_ID;
UNCOV
895
                Map<String, String> randomUser = authTestDataService.getRandomUser();
×
896
                // String eppn = UUID.randomUUID().toString().substring(0, 8);
UNCOV
897
                String eppn = randomUser.get("eppn");
×
898
                String idPEntityId = randomUser.get("idp");
×
899
                String notUsed = null;
×
900
                String separator = "|";
×
901
                // UserIdentifier newUserIdentifierInLookupTable = new
902
                // UserIdentifier(idPEntityId + separator + eppn, notUsed);
UNCOV
903
                UserIdentifier newUserIdentifierInLookupTable = new UserIdentifier(newPersistentUserIdInLookupTable, notUsed);
×
904
                String overwriteFirstName = randomUser.get("firstName");
×
905
                String overwriteLastName = randomUser.get("lastName");
×
906
                String overwriteEmail = randomUser.get("email");
×
907
                overwriteEmail = newEmailAddressToUse;
×
908
                logger.info("overwriteEmail: " + overwriteEmail);
×
909
                boolean validEmail = EMailValidator.isEmailValid(overwriteEmail);
×
910
                if (!validEmail) {
×
911
                        // See https://github.com/IQSS/dataverse/issues/2998
UNCOV
912
                        return error(Response.Status.BAD_REQUEST, "invalid email: " + overwriteEmail);
×
913
                }
914
                /**
915
                 * @todo If affiliation is not null, put it in RoleAssigneeDisplayInfo
916
                 *       constructor.
917
                 */
918
                /**
919
                 * Here we are exercising (via an API test) shibService.getAffiliation with the
920
                 * TestShib IdP and a non-production DevShibAccountType.
921
                 */
922
                // idPEntityId = ShibUtil.testShibIdpEntityId;
923
                // String overwriteAffiliation = shibService.getAffiliation(idPEntityId,
924
                // ShibServiceBean.DevShibAccountType.RANDOM);
UNCOV
925
                String overwriteAffiliation = null;
×
926
                logger.info("overwriteAffiliation: " + overwriteAffiliation);
×
927
                /**
928
                 * @todo Find a place to put "position" in the authenticateduser table:
929
                 *       https://github.com/IQSS/dataverse/issues/1444#issuecomment-74134694
930
                 */
UNCOV
931
                String overwritePosition = "staff;student";
×
932
                AuthenticatedUserDisplayInfo displayInfo = new AuthenticatedUserDisplayInfo(overwriteFirstName,
×
933
                                overwriteLastName, overwriteEmail, overwriteAffiliation, overwritePosition);
UNCOV
934
                JsonObjectBuilder response = Json.createObjectBuilder();
×
935
                JsonArrayBuilder problems = Json.createArrayBuilder();
×
936
                if (password != null) {
×
937
                        response.add("password supplied", password);
×
938
                        boolean knowsExistingPassword = false;
×
939
                        BuiltinUser oldBuiltInUser = builtinUserService.findByUserName(builtInUserToConvert.getUserIdentifier());
×
940
                        if (oldBuiltInUser != null) {
×
941
                                String usernameOfBuiltinAccountToConvert = oldBuiltInUser.getUserName();
×
942
                                response.add("old username", usernameOfBuiltinAccountToConvert);
×
943
                                AuthenticatedUser authenticatedUser = authSvc.canLogInAsBuiltinUser(usernameOfBuiltinAccountToConvert,
×
944
                                                password);
UNCOV
945
                                if (authenticatedUser != null) {
×
946
                                        knowsExistingPassword = true;
×
947
                                        AuthenticatedUser convertedUser = authSvc.convertBuiltInUserToRemoteUser(builtInUserToConvert,
×
948
                                                        newProviderId, newUserIdentifierInLookupTable);
UNCOV
949
                                        if (convertedUser != null) {
×
950
                                                /**
951
                                                 * @todo Display name is not being overwritten. Logic must be in Shib backing
952
                                                 *       bean
953
                                                 */
UNCOV
954
                                                AuthenticatedUser updatedInfoUser = authSvc.updateAuthenticatedUser(convertedUser, displayInfo);
×
955
                                                if (updatedInfoUser != null) {
×
956
                                                        response.add("display name overwritten with", updatedInfoUser.getName());
×
957
                                                } else {
UNCOV
958
                                                        problems.add("couldn't update display info");
×
959
                                                }
UNCOV
960
                                        } else {
×
961
                                                problems.add("unable to convert user");
×
962
                                        }
963
                                }
UNCOV
964
                        } else {
×
965
                                problems.add("couldn't find old username");
×
966
                        }
UNCOV
967
                        if (!knowsExistingPassword) {
×
968
                                String message = "User doesn't know password.";
×
969
                                problems.add(message);
×
970
                                /**
971
                                 * @todo Someday we should make a errorResponse method that takes JSON arrays
972
                                 *       and objects.
973
                                 */
UNCOV
974
                                return error(Status.BAD_REQUEST, problems.build().toString());
×
975
                        }
976
                        // response.add("knows existing password", knowsExistingPassword);
977
                }
978

UNCOV
979
                response.add("user to convert", builtInUserToConvert.getIdentifier());
×
980
                response.add("existing user found by email (prompt to convert)", existing);
×
981
                response.add("changing to this provider", newProviderId);
×
982
                response.add("value to overwrite old first name", overwriteFirstName);
×
983
                response.add("value to overwrite old last name", overwriteLastName);
×
984
                response.add("value to overwrite old email address", overwriteEmail);
×
985
                if (overwriteAffiliation != null) {
×
986
                        response.add("affiliation", overwriteAffiliation);
×
987
                }
UNCOV
988
                response.add("problems", problems);
×
989
                return ok(response);
×
990
        }
991

992

993

994

995
        @Path("roles")
996
        @POST
997
        public Response createNewBuiltinRole(RoleDTO roleDto) {
UNCOV
998
                ActionLogRecord alr = new ActionLogRecord(ActionLogRecord.ActionType.Admin, "createBuiltInRole")
×
999
                                .setInfo(roleDto.getAlias() + ":" + roleDto.getDescription());
×
1000
                try {
UNCOV
1001
                        return ok(json(rolesSvc.save(roleDto.asRole())));
×
1002
                } catch (Exception e) {
×
1003
                        alr.setActionResult(ActionLogRecord.Result.InternalError);
×
1004
                        alr.setInfo(alr.getInfo() + "// " + e.getMessage());
×
1005
                        return error(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage());
×
1006
                } finally {
UNCOV
1007
                        actionLogSvc.log(alr);
×
1008
                }
1009
        }
1010

1011
        @Path("roles")
1012
        @GET
1013
        public Response listBuiltinRoles() {
1014
                try {
UNCOV
1015
                        return ok(rolesToJson(rolesSvc.findBuiltinRoles()));
×
1016
                } catch (Exception e) {
×
1017
                        return error(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage());
×
1018
                }
1019
        }
1020

1021
    @DELETE
1022
        @AuthRequired
1023
    @Path("roles/{id}")
1024
    public Response deleteRole(@Context ContainerRequestContext crc, @PathParam("id") String id) {
1025

UNCOV
1026
        return response(req -> {
×
1027
            DataverseRole doomed = findRoleOrDie(id);
×
1028
            execCommand(new DeleteRoleCommand(req, doomed));
×
1029
            return ok("role " + doomed.getName() + " deleted.");
×
1030
        }, getRequestUser(crc));
×
1031
    }
1032

1033
    @Path("superuser/{identifier}")
1034
    @Deprecated
1035
    @POST
1036
    public Response toggleSuperuser(@PathParam("identifier") String identifier) {
UNCOV
1037
        ActionLogRecord alr = new ActionLogRecord(ActionLogRecord.ActionType.Admin, "toggleSuperuser")
×
1038
                .setInfo(identifier);
×
1039
        try {
UNCOV
1040
            final AuthenticatedUser user = authSvc.getAuthenticatedUser(identifier);
×
1041
            return setSuperuserStatus(user, !user.isSuperuser());
×
1042
        } catch (Exception e) {
×
1043
            alr.setActionResult(ActionLogRecord.Result.InternalError);
×
1044
            alr.setInfo(alr.getInfo() + "// " + e.getMessage());
×
1045
            return error(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage());
×
1046
        } finally {
UNCOV
1047
            actionLogSvc.log(alr);
×
1048
        }
1049
    }
1050

1051
    private Response setSuperuserStatus(AuthenticatedUser user, Boolean isSuperuser) {
UNCOV
1052
        if (user.isDeactivated()) {
×
1053
            return error(Status.BAD_REQUEST, "You cannot make a deactivated user a superuser.");
×
1054
        }
UNCOV
1055
        user.setSuperuser(isSuperuser);
×
1056
        return ok("User " + user.getIdentifier() + " " + (user.isSuperuser() ? "set" : "removed")
×
1057
                + " as a superuser.");
1058
    }
1059

1060
    @Path("superuser/{identifier}")
1061
    @PUT
1062
    // Using string instead of boolean so user doesn't need to add a Content-type header in their request
1063
    public Response setSuperuserStatus(@PathParam("identifier") String identifier, String isSuperuser) {
UNCOV
1064
        ActionLogRecord alr = new ActionLogRecord(ActionLogRecord.ActionType.Admin, "setSuperuserStatus")
×
1065
                .setInfo(identifier + ":" + isSuperuser);
×
1066
        try {
UNCOV
1067
            return setSuperuserStatus(authSvc.getAuthenticatedUser(identifier), StringUtil.isTrue(isSuperuser));
×
1068
        } catch (Exception e) {
×
1069
            alr.setActionResult(ActionLogRecord.Result.InternalError);
×
1070
            alr.setInfo(alr.getInfo() + "// " + e.getMessage());
×
1071
            return error(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage());
×
1072
        } finally {
UNCOV
1073
            actionLogSvc.log(alr);
×
1074
        }
1075
    }
1076

1077
    @GET
1078
    @Path("validate/datasets")
1079
    @Produces({"application/json"})
1080
    public Response validateAllDatasets(@QueryParam("variables") boolean includeVariables) {
1081
        
1082
        // Streaming output: the API will start producing 
1083
        // the output right away, as it goes through the list 
1084
        // of the datasets; there's potentially a lot of content 
1085
        // to validate, so we don't want to wait for the process 
1086
        // to finish. Or to wait to encounter the first invalid 
1087
        // object - so we'll be reporting both the success and failure
1088
        // outcomes for all the validated datasets, to give the user
1089
        // an indication of the progress. 
1090
        // This is the first streaming API that produces json that 
1091
        // we have; there may be better ways to stream json - but 
1092
        // what I have put together below works. -- L.A. 
UNCOV
1093
        StreamingOutput stream = new StreamingOutput() {
×
1094

1095
            @Override
1096
            public void write(OutputStream os) throws IOException,
1097
                    WebApplicationException {
UNCOV
1098
                os.write("{\"datasets\": [\n".getBytes());
×
1099
                
UNCOV
1100
                boolean wroteObject = false;
×
1101
                for (Long datasetId : datasetService.findAllLocalDatasetIds()) {
×
1102
                    // Potentially, there's a godzillion datasets in this Dataverse. 
1103
                    // This is why we go through the list of ids here, and instantiate 
1104
                    // only one dataset at a time. 
UNCOV
1105
                    boolean success = false;
×
1106
                    boolean constraintViolationDetected = false;
×
1107
                     
UNCOV
1108
                    JsonObjectBuilder output = Json.createObjectBuilder();
×
1109
                    output.add("datasetId", datasetId);
×
1110

1111
                    
1112
                    try {
UNCOV
1113
                        datasetService.instantiateDatasetInNewTransaction(datasetId, includeVariables);
×
1114
                        success = true;
×
1115
                    } catch (Exception ex) {
×
1116
                        Throwable cause = ex;
×
1117
                        while (cause != null) {
×
1118
                            if (cause instanceof ConstraintViolationException) {
×
1119
                                ConstraintViolationException constraintViolationException = (ConstraintViolationException) cause;
×
1120
                                for (ConstraintViolation<?> constraintViolation : constraintViolationException
×
1121
                                        .getConstraintViolations()) {
×
1122
                                    String databaseRow = constraintViolation.getLeafBean().toString();
×
1123
                                    String field = constraintViolation.getPropertyPath().toString();
×
1124
                                    String invalidValue = null;
×
1125
                                    if (constraintViolation.getInvalidValue() != null) {
×
1126
                                        invalidValue = constraintViolation.getInvalidValue().toString();
×
1127
                                    }
UNCOV
1128
                                    output.add("status", "invalid");
×
1129
                                    output.add("entityClassDatabaseTableRowId", databaseRow);
×
1130
                                    output.add("field", field);
×
1131
                                    output.add("invalidValue", invalidValue == null ? "NULL" : invalidValue);
×
1132
                                    
UNCOV
1133
                                    constraintViolationDetected = true; 
×
1134
                                    
UNCOV
1135
                                    break; 
×
1136
                                    
1137
                                }
1138
                            }
UNCOV
1139
                            cause = cause.getCause();
×
1140
                        }
UNCOV
1141
                    }
×
1142
                    
1143
                    
UNCOV
1144
                    if (success) {
×
1145
                        output.add("status", "valid");
×
1146
                    } else if (!constraintViolationDetected) {
×
1147
                        output.add("status", "unknown");
×
1148
                    }
1149
                    
1150
                    // write it out:
1151
                    
UNCOV
1152
                    if (wroteObject) {
×
1153
                        os.write(",\n".getBytes());
×
1154
                    }
1155

UNCOV
1156
                    os.write(output.build().toString().getBytes("UTF8"));
×
1157
                    
UNCOV
1158
                    if (!wroteObject) {
×
1159
                        wroteObject = true;
×
1160
                    }
UNCOV
1161
                }
×
1162
                
1163
                
UNCOV
1164
                os.write("\n]\n}\n".getBytes());
×
1165
            }
×
1166
            
1167
        };
UNCOV
1168
        return Response.ok(stream).build();
×
1169
    }
1170
        
1171
    @Path("validate/dataset/{id}")
1172
    @GET
1173
    public Response validateDataset(@PathParam("id") String id, @QueryParam("variables") boolean includeVariables) {
1174
        Dataset dataset;
1175
        try {
UNCOV
1176
            dataset = findDatasetOrDie(id);
×
1177
        } catch (Exception ex) {
×
1178
            return error(Response.Status.NOT_FOUND, "No Such Dataset");
×
1179
        }
×
1180

UNCOV
1181
        Long dbId = dataset.getId();
×
1182

UNCOV
1183
        String msg = "unknown";
×
1184
        try {
UNCOV
1185
            datasetService.instantiateDatasetInNewTransaction(dbId, includeVariables);
×
1186
            msg = "valid";
×
1187
        } catch (Exception ex) {
×
1188
            Throwable cause = ex;
×
1189
            while (cause != null) {
×
1190
                if (cause instanceof ConstraintViolationException) {
×
1191
                    ConstraintViolationException constraintViolationException = (ConstraintViolationException) cause;
×
1192
                    for (ConstraintViolation<?> constraintViolation : constraintViolationException
×
1193
                            .getConstraintViolations()) {
×
1194
                        String databaseRow = constraintViolation.getLeafBean().toString();
×
1195
                        String field = constraintViolation.getPropertyPath().toString();
×
1196
                        String invalidValue = null; 
×
1197
                        if (constraintViolation.getInvalidValue() != null) {
×
1198
                            invalidValue = constraintViolation.getInvalidValue().toString();
×
1199
                        }
UNCOV
1200
                        JsonObjectBuilder violation = Json.createObjectBuilder();
×
1201
                        violation.add("entityClassDatabaseTableRowId", databaseRow);
×
1202
                        violation.add("field", field);
×
1203
                        violation.add("invalidValue", invalidValue == null ? "NULL" : invalidValue);
×
1204
                        return ok(violation);
×
1205
                    }
1206
                }
UNCOV
1207
                cause = cause.getCause();
×
1208
            }
UNCOV
1209
        }
×
1210
        return ok(msg);
×
1211
    }
1212
    
1213
    // This API does the same thing as /validateDataFileHashValue/{fileId}, 
1214
    // but for all the files in the dataset, with streaming output.
1215
    @GET
1216
    @Path("validate/dataset/files/{id}")
1217
    @Produces({"application/json"})
1218
    public Response validateDatasetDatafiles(@PathParam("id") String id) {
1219
        
1220
        // Streaming output: the API will start producing 
1221
        // the output right away, as it goes through the list 
1222
        // of the datafiles in the dataset.
1223
        // The streaming mechanism is modeled after validate/datasets API.
UNCOV
1224
        StreamingOutput stream = new StreamingOutput() {
×
1225

1226
            @Override
1227
            public void write(OutputStream os) throws IOException,
1228
                    WebApplicationException {
1229
                Dataset dataset;
1230
        
1231
                try {
UNCOV
1232
                    dataset = findDatasetOrDie(id);
×
1233
                } catch (Exception ex) {
×
1234
                    throw new IOException(ex.getMessage());
×
1235
                }
×
1236
                
UNCOV
1237
                os.write("{\"dataFiles\": [\n".getBytes());
×
1238
                
UNCOV
1239
                boolean wroteObject = false;
×
1240
                for (DataFile dataFile : dataset.getFiles()) {
×
1241
                    // Potentially, there's a godzillion datasets in this Dataverse. 
1242
                    // This is why we go through the list of ids here, and instantiate 
1243
                    // only one dataset at a time. 
UNCOV
1244
                    boolean success = false;
×
1245
                    boolean constraintViolationDetected = false;
×
1246
                     
UNCOV
1247
                    JsonObjectBuilder output = Json.createObjectBuilder();
×
1248
                    output.add("datafileId", dataFile.getId());
×
1249
                    output.add("storageIdentifier", dataFile.getStorageIdentifier());
×
1250

1251
                    
1252
                    try {
UNCOV
1253
                        FileUtil.validateDataFileChecksum(dataFile);
×
1254
                        success = true;
×
1255
                    } catch (IOException ex) {
×
1256
                        output.add("status", "invalid");
×
1257
                        output.add("errorMessage", ex.getMessage());
×
1258
                    }
×
1259
                    
UNCOV
1260
                    if (success) {
×
1261
                        output.add("status", "valid");
×
1262
                    } 
1263
                    
1264
                    // write it out:
1265
                    
UNCOV
1266
                    if (wroteObject) {
×
1267
                        os.write(",\n".getBytes());
×
1268
                    }
1269

UNCOV
1270
                    os.write(output.build().toString().getBytes("UTF8"));
×
1271
                    
UNCOV
1272
                    if (!wroteObject) {
×
1273
                        wroteObject = true;
×
1274
                    }
UNCOV
1275
                }
×
1276
                
UNCOV
1277
                os.write("\n]\n}\n".getBytes());
×
1278
            }
×
1279
            
1280
        };
UNCOV
1281
        return Response.ok(stream).build();
×
1282
    }
1283

1284
        @Path("assignments/assignees/{raIdtf: .*}")
1285
        @GET
1286
        public Response getAssignmentsFor(@PathParam("raIdtf") String raIdtf) {
1287

UNCOV
1288
                JsonArrayBuilder arr = Json.createArrayBuilder();
×
1289
                roleAssigneeSvc.getAssignmentsFor(raIdtf).forEach(a -> arr.add(json(a)));
×
1290

UNCOV
1291
                return ok(arr);
×
1292
        }
1293

1294
        /**
1295
         * This method is used in integration tests.
1296
         *
1297
         * @param userId
1298
         *            The database id of an AuthenticatedUser.
1299
         * @return The confirm email token.
1300
         */
1301
        @Path("confirmEmail/{userId}")
1302
        @GET
1303
        public Response getConfirmEmailToken(@PathParam("userId") long userId) {
UNCOV
1304
                AuthenticatedUser user = authSvc.findByID(userId);
×
1305
                if (user != null) {
×
1306
                        ConfirmEmailData confirmEmailData = confirmEmailSvc.findSingleConfirmEmailDataByUser(user);
×
1307
                        if (confirmEmailData != null) {
×
1308
                                return ok(Json.createObjectBuilder().add("token", confirmEmailData.getToken()));
×
1309
                        }
1310
                }
UNCOV
1311
                return error(Status.BAD_REQUEST, "Could not find confirm email token for user " + userId);
×
1312
        }
1313

1314
        /**
1315
         * This method is used in integration tests.
1316
         *
1317
         * @param userId
1318
         *            The database id of an AuthenticatedUser.
1319
         */
1320
        @Path("confirmEmail/{userId}")
1321
        @POST
1322
        public Response startConfirmEmailProcess(@PathParam("userId") long userId) {
UNCOV
1323
                AuthenticatedUser user = authSvc.findByID(userId);
×
1324
                if (user != null) {
×
1325
                        try {
UNCOV
1326
                                ConfirmEmailInitResponse confirmEmailInitResponse = confirmEmailSvc.beginConfirm(user);
×
1327
                                ConfirmEmailData confirmEmailData = confirmEmailInitResponse.getConfirmEmailData();
×
1328
                                return ok(Json.createObjectBuilder().add("tokenCreated", confirmEmailData.getCreated().toString())
×
1329
                                                .add("identifier", user.getUserIdentifier()));
×
1330
                        } catch (ConfirmEmailException ex) {
×
1331
                                return error(Status.BAD_REQUEST,
×
1332
                                                "Could not start confirm email process for user " + userId + ": " + ex.getLocalizedMessage());
×
1333
                        }
1334
                }
UNCOV
1335
                return error(Status.BAD_REQUEST, "Could not find user based on " + userId);
×
1336
        }
1337

1338
        /**
1339
         * This method is used by an integration test in UsersIT.java to exercise bug
1340
         * https://github.com/IQSS/dataverse/issues/3287 . Not for use by users!
1341
         */
1342
        @Path("convertUserFromBcryptToSha1")
1343
        @POST
1344
        public Response convertUserFromBcryptToSha1(String json) {
UNCOV
1345
                JsonReader jsonReader = Json.createReader(new StringReader(json));
×
1346
                JsonObject object = jsonReader.readObject();
×
1347
                jsonReader.close();
×
1348
                BuiltinUser builtinUser = builtinUserService.find(new Long(object.getInt("builtinUserId")));
×
1349
                builtinUser.updateEncryptedPassword("4G7xxL9z11/JKN4jHPn4g9iIQck=", 0); // password is "sha-1Pass", 0 means
×
1350
                                                                                                                                                                // SHA-1
UNCOV
1351
                BuiltinUser savedUser = builtinUserService.save(builtinUser);
×
1352
                return ok("foo: " + savedUser);
×
1353

1354
        }
1355

1356
    @Path("permissions/{dvo}")
1357
    @AuthRequired
1358
    @GET
1359
    public Response findPermissonsOn(@Context final ContainerRequestContext crc, @PathParam("dvo") final String dvo) {
1360
        try {
UNCOV
1361
            final DvObject dvObj = findDvo(dvo);
×
1362
            final User aUser = getRequestUser(crc);
×
1363
            final JsonObjectBuilder bld = Json.createObjectBuilder();
×
1364
            bld.add("user", aUser.getIdentifier());
×
1365
            bld.add("permissions", json(permissionSvc.permissionsFor(createDataverseRequest(aUser), dvObj)));
×
1366
            return ok(bld);
×
1367
        } catch (WrappedResponse r) {
×
1368
            return r.getResponse();
×
1369
        } catch (Exception e) {
×
1370
            logger.log(Level.SEVERE, "Error while testing permissions", e);
×
1371
            return error(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage());
×
1372
        }
1373
    }
1374

1375
        @Path("assignee/{idtf}")
1376
        @GET
1377
        public Response findRoleAssignee(@PathParam("idtf") String idtf) {
UNCOV
1378
                RoleAssignee ra = roleAssigneeSvc.getRoleAssignee(idtf);
×
1379
                return (ra == null) ? notFound("Role Assignee '" + idtf + "' not found.") : ok(json(ra.getDisplayInfo()));
×
1380
        }
1381

1382
        @Path("datasets/integrity/{datasetVersionId}/fixmissingunf")
1383
        @POST
1384
        public Response fixUnf(@PathParam("datasetVersionId") String datasetVersionId,
1385
                        @QueryParam("forceRecalculate") boolean forceRecalculate) {
UNCOV
1386
                JsonObjectBuilder info = datasetVersionSvc.fixMissingUnf(datasetVersionId, forceRecalculate);
×
1387
                return ok(info);
×
1388
        }
1389

1390
        @Path("datafiles/integrity/fixmissingoriginaltypes")
1391
        @GET
1392
        public Response fixMissingOriginalTypes() {
UNCOV
1393
                JsonObjectBuilder info = Json.createObjectBuilder();
×
1394

UNCOV
1395
                List<Long> affectedFileIds = fileService.selectFilesWithMissingOriginalTypes();
×
1396

UNCOV
1397
                if (affectedFileIds.isEmpty()) {
×
1398
                        info.add("message",
×
1399
                                        "All the tabular files in the database already have the original types set correctly; exiting.");
1400
                } else {
UNCOV
1401
                        for (Long fileid : affectedFileIds) {
×
1402
                                logger.fine("found file id: " + fileid);
×
1403
                        }
×
1404
                        info.add("message", "Found " + affectedFileIds.size()
×
1405
                                        + " tabular files with missing original types. Kicking off an async job that will repair the files in the background.");
1406
                }
1407

UNCOV
1408
                ingestService.fixMissingOriginalTypes(affectedFileIds);
×
1409

UNCOV
1410
                return ok(info);
×
1411
        }
1412
        
1413
    @Path("datafiles/integrity/fixmissingoriginalsizes")
1414
    @GET
1415
    public Response fixMissingOriginalSizes(@QueryParam("limit") Integer limit) {
UNCOV
1416
        JsonObjectBuilder info = Json.createObjectBuilder();
×
1417

UNCOV
1418
        List<Long> affectedFileIds = fileService.selectFilesWithMissingOriginalSizes();
×
1419

UNCOV
1420
        if (affectedFileIds.isEmpty()) {
×
1421
            info.add("message",
×
1422
                    "All the tabular files in the database already have the original sizes set correctly; exiting.");
1423
        } else {
1424
            
UNCOV
1425
            int howmany = affectedFileIds.size();
×
1426
            String message = "Found " + howmany + " tabular files with missing original sizes. "; 
×
1427
            
UNCOV
1428
            if (limit == null || howmany <= limit) {
×
1429
                message = message.concat(" Kicking off an async job that will repair the files in the background.");
×
1430
            } else {
UNCOV
1431
                affectedFileIds.subList(limit, howmany-1).clear();
×
1432
                message = message.concat(" Kicking off an async job that will repair the " + limit + " files in the background.");                        
×
1433
            }
UNCOV
1434
            info.add("message", message);
×
1435
        }
1436

UNCOV
1437
        ingestService.fixMissingOriginalSizes(affectedFileIds);
×
1438
        return ok(info);
×
1439
    }
1440

1441
        /**
1442
         * This method is used in API tests, called from UtilIt.java.
1443
         */
1444
        @GET
1445
        @Path("datasets/thumbnailMetadata/{id}")
1446
        public Response getDatasetThumbnailMetadata(@PathParam("id") Long idSupplied) {
UNCOV
1447
                Dataset dataset = datasetSvc.find(idSupplied);
×
1448
                if (dataset == null) {
×
1449
                        return error(Response.Status.NOT_FOUND, "Could not find dataset based on id supplied: " + idSupplied + ".");
×
1450
                }
UNCOV
1451
                JsonObjectBuilder data = Json.createObjectBuilder();
×
1452
                DatasetThumbnail datasetThumbnail = dataset.getDatasetThumbnail(ImageThumbConverter.DEFAULT_CARDIMAGE_SIZE);
×
1453
                data.add("isUseGenericThumbnail", dataset.isUseGenericThumbnail());
×
1454
                data.add("datasetLogoPresent", DatasetUtil.isDatasetLogoPresent(dataset, ImageThumbConverter.DEFAULT_CARDIMAGE_SIZE));
×
1455
                if (datasetThumbnail != null) {
×
1456
                        data.add("datasetThumbnailBase64image", datasetThumbnail.getBase64image());
×
1457
                        DataFile dataFile = datasetThumbnail.getDataFile();
×
1458
                        if (dataFile != null) {
×
1459
                                /**
1460
                                 * @todo Change this from a String to a long.
1461
                                 */
UNCOV
1462
                                data.add("dataFileId", dataFile.getId().toString());
×
1463
                        }
1464
                }
UNCOV
1465
                return ok(data);
×
1466
        }
1467

1468
        /**
1469
         * validatePassword
1470
         * <p>
1471
         * Validate a password with an API call
1472
         *
1473
         * @param password
1474
         *            The password
1475
         * @return A response with the validation result.
1476
         */
1477
        @Path("validatePassword")
1478
        @POST
1479
        public Response validatePassword(String password) {
1480

UNCOV
1481
                final List<String> errors = passwordValidatorService.validate(password, new Date(), false);
×
1482
                final JsonArrayBuilder errorArray = Json.createArrayBuilder();
×
1483
                errors.forEach(errorArray::add);
×
1484
                return ok(Json.createObjectBuilder().add("password", password).add("errors", errorArray));
×
1485
        }
1486

1487
        @GET
1488
        @Path("/isOrcid")
1489
        public Response isOrcidEnabled() {
UNCOV
1490
                return authSvc.isOrcidEnabled() ? ok("Orcid is enabled") : ok("no orcid for you.");
×
1491
        }
1492

1493
    @POST
1494
        @AuthRequired
1495
    @Path("{id}/reregisterHDLToPID")
1496
    public Response reregisterHdlToPID(@Context ContainerRequestContext crc, @PathParam("id") String id) {
UNCOV
1497
        logger.info("Starting to reregister  " + id + " Dataset Id. (from hdl to doi)" + new Date());
×
1498
        try {
1499

1500
            
UNCOV
1501
            User u = getRequestUser(crc);
×
1502
            if (!u.isSuperuser()) {
×
1503
                logger.info("Bad Request Unauthor " );
×
1504
                return error(Status.UNAUTHORIZED, BundleUtil.getStringFromBundle("admin.api.auth.mustBeSuperUser"));
×
1505
            }
1506

UNCOV
1507
            DataverseRequest r = createDataverseRequest(u);
×
1508
            Dataset ds = findDatasetOrDie(id);
×
1509
            
UNCOV
1510
            if (HandlePidProvider.HDL_PROTOCOL.equals(dvObjectService.getEffectivePidGenerator(ds).getProtocol())) {
×
1511
                logger.info("Bad Request protocol set to handle  " );
×
1512
                return error(Status.BAD_REQUEST, BundleUtil.getStringFromBundle("admin.api.migrateHDL.failure.must.be.set.for.doi"));
×
1513
            }
UNCOV
1514
            if (ds.getIdentifier() != null && !ds.getIdentifier().isEmpty() && ds.getProtocol().equals(HandlePidProvider.HDL_PROTOCOL)) {
×
1515
                execCommand(new RegisterDvObjectCommand(r, ds, true));
×
1516
            } else {
UNCOV
1517
                return error(Status.BAD_REQUEST, BundleUtil.getStringFromBundle("admin.api.migrateHDL.failure.must.be.hdl.dataset"));
×
1518
            }
1519

UNCOV
1520
        } catch (WrappedResponse r) {
×
1521
            logger.info("Failed to migrate Dataset Handle id: " + id);
×
1522
            return badRequest(BundleUtil.getStringFromBundle("admin.api.migrateHDL.failure", Arrays.asList(id)));
×
1523
        } catch (Exception e) {
×
1524
            logger.info("Failed to migrate Dataset Handle id: " + id + " Unexpected Exception " + e.getMessage());
×
1525
            List<String> args = Arrays.asList(id,e.getMessage());
×
1526
            return badRequest(BundleUtil.getStringFromBundle("admin.api.migrateHDL.failureWithException", args));
×
1527
        }
×
1528
        
UNCOV
1529
        return ok(BundleUtil.getStringFromBundle("admin.api.migrateHDL.success"));
×
1530
    }
1531

1532
    @GET
1533
    @AuthRequired
1534
    @Path("{id}/registerDataFile")
1535
    public Response registerDataFile(@Context ContainerRequestContext crc, @PathParam("id") String id) {
UNCOV
1536
        logger.info("Starting to register  " + id + " file id. " + new Date());
×
1537

1538
        try {
UNCOV
1539
            User u = getRequestUser(crc);
×
1540
            DataverseRequest r = createDataverseRequest(u);
×
1541
            DataFile df = findDataFileOrDie(id);
×
1542
            if(!systemConfig.isFilePIDsEnabledForCollection(df.getOwner().getOwner())) {
×
1543
                return forbidden("PIDs are not enabled for this file's collection.");
×
1544
            }
UNCOV
1545
            if (df.getIdentifier() == null || df.getIdentifier().isEmpty()) {
×
1546
                execCommand(new RegisterDvObjectCommand(r, df));
×
1547
            } else {
UNCOV
1548
                return ok("File was already registered. ");
×
1549
            }
1550

UNCOV
1551
        } catch (WrappedResponse r) {
×
1552
            logger.info("Failed to register file id: " + id);
×
1553
        } catch (Exception e) {
×
1554
            logger.info("Failed to register file id: " + id + " Unexpecgted Exception " + e.getMessage());
×
1555
        }
×
1556
        return ok("Datafile registration complete. File registered successfully.");
×
1557
    }
1558

1559
    @GET
1560
    @AuthRequired
1561
    @Path("/registerDataFileAll")
1562
    public Response registerDataFileAll(@Context ContainerRequestContext crc) {
UNCOV
1563
        Integer count = fileService.findAll().size();
×
1564
        Integer successes = 0;
×
1565
        Integer alreadyRegistered = 0;
×
1566
        Integer released = 0;
×
1567
        Integer draft = 0;
×
1568
        Integer skipped = 0;
×
1569
        logger.info("Starting to register: analyzing " + count + " files. " + new Date());
×
1570
        logger.info("Only unregistered, published files will be registered.");
×
1571
        User u = null;
×
1572
        try {
UNCOV
1573
            u = getRequestAuthenticatedUserOrDie(crc);
×
1574
        } catch (WrappedResponse e1) {
×
1575
            return error(Status.UNAUTHORIZED, "api key required");
×
1576
        }
×
1577
        DataverseRequest r = createDataverseRequest(u);
×
1578
        for (DataFile df : fileService.findAll()) {
×
1579
            try {
UNCOV
1580
                if ((df.getIdentifier() == null || df.getIdentifier().isEmpty())) {
×
1581
                    if(!systemConfig.isFilePIDsEnabledForCollection(df.getOwner().getOwner())) {
×
1582
                        skipped++;
×
1583
                        if (skipped % 100 == 0) {
×
1584
                            logger.info(skipped + " of  " + count + " files not in collections that allow file PIDs. " + new Date());
×
1585
                        }
UNCOV
1586
                    } else if (df.isReleased()) {
×
1587
                        released++;
×
1588
                        execCommand(new RegisterDvObjectCommand(r, df));
×
1589
                        successes++;
×
1590
                        if (successes % 100 == 0) {
×
1591
                            logger.info(successes + " of  " + count + " files registered successfully. " + new Date());
×
1592
                        }
1593
                        try {
UNCOV
1594
                            Thread.sleep(1000);
×
1595
                        } catch (InterruptedException ie) {
×
1596
                            logger.warning("Interrupted Exception when attempting to execute Thread.sleep()!");
×
1597
                        }
×
1598
                    } else {
UNCOV
1599
                        draft++;
×
1600
                        if (draft % 100 == 0) {
×
1601
                          logger.info(draft + " of  " + count + " files not yet published");
×
1602
                        }
1603
                    }
1604
                } else {
UNCOV
1605
                    alreadyRegistered++;
×
1606
                    if(alreadyRegistered % 100 == 0) {
×
1607
                      logger.info(alreadyRegistered + " of  " + count + " files are already registered. " + new Date());
×
1608
                    }
1609
                }
UNCOV
1610
            } catch (WrappedResponse ex) {
×
1611
                logger.info("Failed to register file id: " + df.getId());
×
1612
                Logger.getLogger(Datasets.class.getName()).log(Level.SEVERE, null, ex);
×
1613
            } catch (Exception e) {
×
1614
                logger.info("Unexpected Exception: " + e.getMessage());
×
1615
            }
×
1616
            
1617

UNCOV
1618
        }
×
1619
        logger.info("Final Results:");
×
1620
        logger.info(alreadyRegistered + " of  " + count + " files were already registered. " + new Date());
×
1621
        logger.info(draft + " of  " + count + " files are not yet published. " + new Date());
×
1622
        logger.info(released + " of  " + count + " unregistered, published files to register. " + new Date());
×
1623
        logger.info(successes + " of  " + released + " unregistered, published files registered successfully. "
×
1624
                + new Date());
UNCOV
1625
        logger.info(skipped + " of  " + count + " files not in collections that allow file PIDs. " + new Date());
×
1626

UNCOV
1627
        return ok("Datafile registration complete." + successes + " of  " + released
×
1628
                + " unregistered, published files registered successfully.");
1629
    }
1630
    
1631
    @GET
1632
    @AuthRequired
1633
    @Path("/registerDataFiles/{alias}")
1634
    public Response registerDataFilesInCollection(@Context ContainerRequestContext crc, @PathParam("alias") String alias, @QueryParam("sleep") Integer sleepInterval) {
1635
        Dataverse collection;
1636
        try {
UNCOV
1637
            collection = findDataverseOrDie(alias);
×
1638
        } catch (WrappedResponse r) {
×
1639
            return r.getResponse();
×
1640
        }
×
1641
        
UNCOV
1642
        AuthenticatedUser superuser = authSvc.getAdminUser();
×
1643
        if (superuser == null) {
×
1644
            return error(Response.Status.INTERNAL_SERVER_ERROR, "Cannot find the superuser to execute /admin/registerDataFiles.");
×
1645
        }
1646
        
UNCOV
1647
        if (!systemConfig.isFilePIDsEnabledForCollection(collection)) {
×
1648
            return ok("Registration of file-level pid is disabled in collection "+alias+"; nothing to do");
×
1649
        }
1650
        
UNCOV
1651
        List<DataFile> dataFiles = fileService.findByDirectCollectionOwner(collection.getId());
×
1652
        Integer count = dataFiles.size();
×
1653
        Integer countSuccesses = 0;
×
1654
        Integer countAlreadyRegistered = 0;
×
1655
        Integer countReleased = 0;
×
1656
        Integer countDrafts = 0;
×
1657
        
UNCOV
1658
        if (sleepInterval == null) {
×
1659
            sleepInterval = 1; 
×
1660
        } else if (sleepInterval.intValue() < 1) {
×
1661
            return error(Response.Status.BAD_REQUEST, "Invalid sleep interval: "+sleepInterval);
×
1662
        }
1663
        
UNCOV
1664
        logger.info("Starting to register: analyzing " + count + " files. " + new Date());
×
1665
        logger.info("Only unregistered, published files will be registered.");
×
1666
        
1667
        
1668
        
UNCOV
1669
        for (DataFile df : dataFiles) {
×
1670
            try {
UNCOV
1671
                if ((df.getIdentifier() == null || df.getIdentifier().isEmpty())) {
×
1672
                    if (df.isReleased()) {
×
1673
                        countReleased++;
×
1674
                        DataverseRequest r = createDataverseRequest(superuser);
×
1675
                        execCommand(new RegisterDvObjectCommand(r, df));
×
1676
                        countSuccesses++;
×
1677
                        if (countSuccesses % 100 == 0) {
×
1678
                            logger.info(countSuccesses + " out of " + count + " files registered successfully. " + new Date());
×
1679
                        }
1680
                        try {
UNCOV
1681
                            Thread.sleep(sleepInterval * 1000);
×
1682
                        } catch (InterruptedException ie) {
×
1683
                            logger.warning("Interrupted Exception when attempting to execute Thread.sleep()!");
×
1684
                        }
×
1685
                    } else {
×
1686
                        countDrafts++;
×
1687
                        logger.fine(countDrafts + " out of " + count + " files not yet published");
×
1688
                    }
1689
                } else {
UNCOV
1690
                    countAlreadyRegistered++;
×
1691
                    logger.fine(countAlreadyRegistered + " out of " + count + " files are already registered. " + new Date());
×
1692
                }
UNCOV
1693
            } catch (WrappedResponse ex) {
×
1694
                countReleased++;
×
1695
                logger.info("Failed to register file id: " + df.getId());
×
1696
                Logger.getLogger(Datasets.class.getName()).log(Level.SEVERE, null, ex);
×
1697
            } catch (Exception e) {
×
1698
                logger.info("Unexpected Exception: " + e.getMessage());
×
1699
            }
×
1700
        }
×
1701
        
UNCOV
1702
        logger.info(countAlreadyRegistered + " out of " + count + " files were already registered. " + new Date());
×
1703
        logger.info(countDrafts + " out of " + count + " files are not yet published. " + new Date());
×
1704
        logger.info(countReleased + " out of " + count + " unregistered, published files to register. " + new Date());
×
1705
        logger.info(countSuccesses + " out of " + countReleased + " unregistered, published files registered successfully. "
×
1706
                + new Date());
1707

UNCOV
1708
        return ok("Datafile registration complete. " + countSuccesses + " out of " + countReleased
×
1709
                + " unregistered, published files registered successfully.");
1710
    }
1711

1712
    @GET
1713
    @AuthRequired
1714
    @Path("/updateHashValues/{alg}")
1715
    public Response updateHashValues(@Context ContainerRequestContext crc, @PathParam("alg") String alg, @QueryParam("num") int num) {
UNCOV
1716
        Integer count = fileService.findAll().size();
×
1717
        Integer successes = 0;
×
1718
        Integer alreadyUpdated = 0;
×
1719
        Integer rehashed = 0;
×
1720
        Integer harvested = 0;
×
1721

UNCOV
1722
        if (num <= 0)
×
1723
            num = Integer.MAX_VALUE;
×
1724
        DataFile.ChecksumType cType = null;
×
1725
        try {
UNCOV
1726
            cType = DataFile.ChecksumType.fromString(alg);
×
1727
        } catch (IllegalArgumentException iae) {
×
1728
            return error(Status.BAD_REQUEST, "Unknown algorithm");
×
1729
        }
×
1730
        logger.info("Starting to rehash: analyzing " + count + " files. " + new Date());
×
1731
        logger.info("Hashes not created with " + alg + " will be verified, and, if valid, replaced with a hash using "
×
1732
                + alg);
1733
        try {
UNCOV
1734
            User u = getRequestAuthenticatedUserOrDie(crc);
×
1735
            if (!u.isSuperuser())
×
1736
                return error(Status.UNAUTHORIZED, "must be superuser");
×
1737
        } catch (WrappedResponse e1) {
×
1738
            return error(Status.UNAUTHORIZED, "api key required");
×
1739
        }
×
1740

UNCOV
1741
        for (DataFile df : fileService.findAll()) {
×
1742
            if (rehashed.intValue() >= num)
×
1743
                break;
×
1744
            InputStream in = null;
×
1745
            InputStream in2 = null;
×
1746
            try {
UNCOV
1747
                if (df.isHarvested()) {
×
1748
                    harvested++;
×
1749
                } else {
UNCOV
1750
                    if (!df.getChecksumType().equals(cType)) {
×
1751

UNCOV
1752
                        rehashed++;
×
1753
                        logger.fine(rehashed + ": Datafile: " + df.getFileMetadata().getLabel() + ", "
×
1754
                                + df.getIdentifier());
×
1755
                        // verify hash and calc new one to replace it
UNCOV
1756
                        StorageIO<DataFile> storage = df.getStorageIO();
×
1757
                        storage.open(DataAccessOption.READ_ACCESS);
×
1758
                        if (!df.isTabularData()) {
×
1759
                            in = storage.getInputStream();
×
1760
                        } else {
1761
                            // if this is a tabular file, read the preserved original "auxiliary file"
1762
                            // instead:
UNCOV
1763
                            in = storage.getAuxFileAsInputStream(FileUtil.SAVED_ORIGINAL_FILENAME_EXTENSION);
×
1764
                        }
UNCOV
1765
                        if (in == null)
×
1766
                            logger.warning("Cannot retrieve file.");
×
1767
                        String currentChecksum = FileUtil.calculateChecksum(in, df.getChecksumType());
×
1768
                        if (currentChecksum.equals(df.getChecksumValue())) {
×
1769
                            logger.fine("Current checksum for datafile: " + df.getFileMetadata().getLabel() + ", "
×
1770
                                    + df.getIdentifier() + " is valid");
×
1771
                            // Need to reset so we don't get the same stream (StorageIO class inputstreams
1772
                            // are normally only used once)
UNCOV
1773
                            storage.setInputStream(null);
×
1774
                            storage.open(DataAccessOption.READ_ACCESS);
×
1775
                            if (!df.isTabularData()) {
×
1776
                                in2 = storage.getInputStream();
×
1777
                            } else {
1778
                                // if this is a tabular file, read the preserved original "auxiliary file"
1779
                                // instead:
UNCOV
1780
                                in2 = storage.getAuxFileAsInputStream(FileUtil.SAVED_ORIGINAL_FILENAME_EXTENSION);
×
1781
                            }
UNCOV
1782
                            if (in2 == null)
×
1783
                                logger.warning("Cannot retrieve file to calculate new checksum.");
×
1784
                            String newChecksum = FileUtil.calculateChecksum(in2, cType);
×
1785

UNCOV
1786
                            df.setChecksumType(cType);
×
1787
                            df.setChecksumValue(newChecksum);
×
1788
                            successes++;
×
1789
                            if (successes % 100 == 0) {
×
1790
                                logger.info(
×
1791
                                        successes + " of  " + count + " files rehashed successfully. " + new Date());
1792
                            }
UNCOV
1793
                        } else {
×
1794
                            logger.warning("Problem: Current checksum for datafile: " + df.getFileMetadata().getLabel()
×
1795
                                    + ", " + df.getIdentifier() + " is INVALID");
×
1796
                        }
UNCOV
1797
                    } else {
×
1798
                        alreadyUpdated++;
×
1799
                        if (alreadyUpdated % 100 == 0) {
×
1800
                            logger.info(alreadyUpdated + " of  " + count
×
1801
                                    + " files are already have hashes with the new algorithm. " + new Date());
1802
                        }
1803
                    }
1804
                }
UNCOV
1805
            } catch (Exception e) {
×
1806
                logger.warning("Unexpected Exception: " + e.getMessage());
×
1807

1808
            } finally {
UNCOV
1809
                IOUtils.closeQuietly(in);
×
1810
                IOUtils.closeQuietly(in2);
×
1811
            }
UNCOV
1812
        }
×
1813
        logger.info("Final Results:");
×
1814
        logger.info(harvested + " harvested files skipped.");
×
1815
        logger.info(
×
1816
                alreadyUpdated + " of  " + count + " files already had hashes with the new algorithm. " + new Date());
UNCOV
1817
        logger.info(rehashed + " of  " + count + " files to rehash. " + new Date());
×
1818
        logger.info(
×
1819
                successes + " of  " + rehashed + " files successfully rehashed with the new algorithm. " + new Date());
1820

UNCOV
1821
        return ok("Datafile rehashing complete." + successes + " of  " + rehashed + " files successfully rehashed.");
×
1822
    }
1823
        
1824
    @POST
1825
        @AuthRequired
1826
    @Path("/computeDataFileHashValue/{fileId}/algorithm/{alg}")
1827
    public Response computeDataFileHashValue(@Context ContainerRequestContext crc, @PathParam("fileId") String fileId, @PathParam("alg") String alg) {
1828

1829
        try {
UNCOV
1830
            User u = getRequestAuthenticatedUserOrDie(crc);
×
1831
            if (!u.isSuperuser()) {
×
1832
                return error(Status.UNAUTHORIZED, "must be superuser");
×
1833
            }
UNCOV
1834
        } catch (WrappedResponse e1) {
×
1835
            return error(Status.UNAUTHORIZED, "api key required");
×
1836
        }
×
1837

UNCOV
1838
        DataFile fileToUpdate = null;
×
1839
        try {
UNCOV
1840
            fileToUpdate = findDataFileOrDie(fileId);
×
1841
        } catch (WrappedResponse r) {
×
1842
            logger.info("Could not find file with the id: " + fileId);
×
1843
            return error(Status.BAD_REQUEST, "Could not find file with the id: " + fileId);
×
1844
        }
×
1845

UNCOV
1846
        if (fileToUpdate.isHarvested()) {
×
1847
            return error(Status.BAD_REQUEST, "File with the id: " + fileId + " is harvested.");
×
1848
        }
1849

UNCOV
1850
        DataFile.ChecksumType cType = null;
×
1851
        try {
UNCOV
1852
            cType = DataFile.ChecksumType.fromString(alg);
×
1853
        } catch (IllegalArgumentException iae) {
×
1854
            return error(Status.BAD_REQUEST, "Unknown algorithm: " + alg);
×
1855
        }
×
1856

UNCOV
1857
        String newChecksum = "";
×
1858

UNCOV
1859
        InputStream in = null;
×
1860
        try {
1861

UNCOV
1862
            StorageIO<DataFile> storage = fileToUpdate.getStorageIO();
×
1863
            storage.open(DataAccessOption.READ_ACCESS);
×
1864
            if (!fileToUpdate.isTabularData()) {
×
1865
                in = storage.getInputStream();
×
1866
            } else {
UNCOV
1867
                in = storage.getAuxFileAsInputStream(FileUtil.SAVED_ORIGINAL_FILENAME_EXTENSION);
×
1868
            }
UNCOV
1869
            if (in == null) {
×
1870
                return error(Status.NOT_FOUND, "Could not retrieve file with the id: " + fileId);
×
1871
            }
UNCOV
1872
            newChecksum = FileUtil.calculateChecksum(in, cType);
×
1873
            fileToUpdate.setChecksumType(cType);
×
1874
            fileToUpdate.setChecksumValue(newChecksum);
×
1875

UNCOV
1876
        } catch (Exception e) {
×
1877
            logger.warning("Unexpected Exception: " + e.getMessage());
×
1878

1879
        } finally {
UNCOV
1880
            IOUtils.closeQuietly(in);
×
1881
        }
1882

UNCOV
1883
        return ok("Datafile rehashing complete. " + fileId + "  successfully rehashed. New hash value is: " + newChecksum);
×
1884
    }
1885
    
1886
    @POST
1887
        @AuthRequired
1888
    @Path("/validateDataFileHashValue/{fileId}")
1889
    public Response validateDataFileHashValue(@Context ContainerRequestContext crc, @PathParam("fileId") String fileId) {
1890

1891
        try {
UNCOV
1892
            User u = getRequestAuthenticatedUserOrDie(crc);
×
1893
            if (!u.isSuperuser()) {
×
1894
                return error(Status.UNAUTHORIZED, "must be superuser");
×
1895
            }
UNCOV
1896
        } catch (WrappedResponse e1) {
×
1897
            return error(Status.UNAUTHORIZED, "api key required");
×
1898
        }
×
1899

UNCOV
1900
        DataFile fileToValidate = null;
×
1901
        try {
UNCOV
1902
            fileToValidate = findDataFileOrDie(fileId);
×
1903
        } catch (WrappedResponse r) {
×
1904
            logger.info("Could not find file with the id: " + fileId);
×
1905
            return error(Status.BAD_REQUEST, "Could not find file with the id: " + fileId);
×
1906
        }
×
1907

UNCOV
1908
        if (fileToValidate.isHarvested()) {
×
1909
            return error(Status.BAD_REQUEST, "File with the id: " + fileId + " is harvested.");
×
1910
        }
1911

UNCOV
1912
        DataFile.ChecksumType cType = null;
×
1913
        try {
UNCOV
1914
            String checkSumTypeFromDataFile = fileToValidate.getChecksumType().toString();
×
1915
            cType = DataFile.ChecksumType.fromString(checkSumTypeFromDataFile);
×
1916
        } catch (IllegalArgumentException iae) {
×
1917
            return error(Status.BAD_REQUEST, "Unknown algorithm");
×
1918
        }
×
1919

UNCOV
1920
        String currentChecksum = fileToValidate.getChecksumValue();
×
1921
        String calculatedChecksum = "";
×
1922
        InputStream in = null;
×
1923
        try {
1924

UNCOV
1925
            StorageIO<DataFile> storage = fileToValidate.getStorageIO();
×
1926
            storage.open(DataAccessOption.READ_ACCESS);
×
1927
            if (!fileToValidate.isTabularData()) {
×
1928
                in = storage.getInputStream();
×
1929
            } else {
UNCOV
1930
                in = storage.getAuxFileAsInputStream(FileUtil.SAVED_ORIGINAL_FILENAME_EXTENSION);
×
1931
            }
UNCOV
1932
            if (in == null) {
×
1933
                return error(Status.NOT_FOUND, "Could not retrieve file with the id: " + fileId);
×
1934
            }
UNCOV
1935
            calculatedChecksum = FileUtil.calculateChecksum(in, cType);
×
1936

UNCOV
1937
        } catch (Exception e) {
×
1938
            logger.warning("Unexpected Exception: " + e.getMessage());
×
1939
            return error(Status.BAD_REQUEST, "Checksum Validation Unexpected Exception: " + e.getMessage());
×
1940
        } finally {
UNCOV
1941
            IOUtils.closeQuietly(in);
×
1942

1943
        }
1944

UNCOV
1945
        if (currentChecksum.equals(calculatedChecksum)) {
×
1946
            return ok("Datafile validation complete for " + fileId + ". The hash value is: " + calculatedChecksum);
×
1947
        } else {
UNCOV
1948
            return error(Status.EXPECTATION_FAILED, "Datafile validation failed for " + fileId + ". The saved hash value is: " + currentChecksum + " while the recalculated hash value for the stored file is: " + calculatedChecksum);
×
1949
        }
1950

1951
    }
1952

1953
    @POST
1954
        @AuthRequired
1955
    @Path("/submitDatasetVersionToArchive/{id}/{version}")
1956
    public Response submitDatasetVersionToArchive(@Context ContainerRequestContext crc, @PathParam("id") String dsid,
1957
            @PathParam("version") String versionNumber) {
1958

1959
        try {
UNCOV
1960
            AuthenticatedUser au = getRequestAuthenticatedUserOrDie(crc);
×
1961

UNCOV
1962
            Dataset ds = findDatasetOrDie(dsid);
×
1963

UNCOV
1964
            DatasetVersion dv = datasetversionService.findByFriendlyVersionNumber(ds.getId(), versionNumber);
×
1965
            if(dv==null) {
×
1966
                return error(Status.BAD_REQUEST, "Requested version not found.");
×
1967
            }
UNCOV
1968
            if (dv.getArchivalCopyLocation() == null) {
×
1969
                String className = settingsService.getValueForKey(SettingsServiceBean.Key.ArchiverClassName);
×
1970
                // Note - the user is being sent via the createDataverseRequest(au) call to the
1971
                // back-end command where it is used to get the API Token which is
1972
                // then used to retrieve files (e.g. via S3 direct downloads) to create the Bag
UNCOV
1973
                AbstractSubmitToArchiveCommand cmd = ArchiverUtil.createSubmitToArchiveCommand(className,
×
1974
                        createDataverseRequest(au), dv);
×
1975
                // createSubmitToArchiveCommand() tries to find and instantiate an non-abstract
1976
                // implementation of AbstractSubmitToArchiveCommand based on the provided
1977
                // className. If a class with that name isn't found (or can't be instatiated), it
1978
                // will return null
UNCOV
1979
                if (cmd != null) {
×
1980
                    if(ArchiverUtil.onlySingleVersionArchiving(cmd.getClass(), settingsService)) {
×
1981
                        for (DatasetVersion version : ds.getVersions()) {
×
1982
                            if ((dv != version) && version.getArchivalCopyLocation() != null) {
×
1983
                                return error(Status.CONFLICT, "Dataset already archived.");
×
1984
                            }
UNCOV
1985
                        } 
×
1986
                    }
UNCOV
1987
                    new Thread(new Runnable() {
×
1988
                        public void run() {
1989
                            try {
UNCOV
1990
                                DatasetVersion dv = commandEngine.submit(cmd);
×
1991
                                if (!dv.getArchivalCopyLocationStatus().equals(DatasetVersion.ARCHIVAL_STATUS_FAILURE)) {
×
1992
                                    logger.info(
×
1993
                                            "DatasetVersion id=" + ds.getGlobalId().toString() + " v" + versionNumber
×
1994
                                                    + " submitted to Archive, status: " + dv.getArchivalCopyLocationStatus());
×
1995
                                } else {
UNCOV
1996
                                    logger.severe("Error submitting version due to conflict/error at Archive");
×
1997
                                }
UNCOV
1998
                            } catch (CommandException ex) {
×
1999
                                logger.log(Level.SEVERE, "Unexpected Exception calling  submit archive command", ex);
×
2000
                            }
×
2001
                        }
×
2002
                    }).start();
×
2003
                    return ok("Archive submission using " + cmd.getClass().getCanonicalName()
×
2004
                            + " started. Processing can take significant time for large datasets and requires that the user have permission to publish the dataset. View log and/or check archive for results.");
2005
                } else {
UNCOV
2006
                    logger.log(Level.SEVERE, "Could not find Archiver class: " + className);
×
2007
                    return error(Status.INTERNAL_SERVER_ERROR, "Could not find Archiver class: " + className);
×
2008
                }
2009
            } else {
UNCOV
2010
                return error(Status.BAD_REQUEST, "Version was already submitted for archiving.");
×
2011
            }
UNCOV
2012
        } catch (WrappedResponse e1) {
×
2013
            return e1.getResponse();
×
2014
        }
2015
    }
2016

2017
    
2018
    /**
2019
     * Iteratively archives all unarchived dataset versions
2020
     * @param
2021
     * listonly - don't archive, just list unarchived versions
2022
     * limit - max number to process
2023
     * lastestonly - only archive the latest versions
2024
     * @return
2025
     */
2026
    @POST
2027
        @AuthRequired
2028
    @Path("/archiveAllUnarchivedDatasetVersions")
2029
    public Response archiveAllUnarchivedDatasetVersions(@Context ContainerRequestContext crc, @QueryParam("listonly") boolean listonly, @QueryParam("limit") Integer limit, @QueryParam("latestonly") boolean latestonly) {
2030

2031
        try {
UNCOV
2032
            AuthenticatedUser au = getRequestAuthenticatedUserOrDie(crc);
×
2033

UNCOV
2034
            List<DatasetVersion> dsl = datasetversionService.getUnarchivedDatasetVersions();
×
2035
            if (dsl != null) {
×
2036
                if (listonly) {
×
2037
                    JsonArrayBuilder jab = Json.createArrayBuilder();
×
2038
                    logger.fine("Unarchived versions found: ");
×
2039
                    int current = 0;
×
2040
                    for (DatasetVersion dv : dsl) {
×
2041
                        if (limit != null && current >= limit) {
×
2042
                            break;
×
2043
                        }
UNCOV
2044
                        if (!latestonly || dv.equals(dv.getDataset().getLatestVersionForCopy())) {
×
2045
                            jab.add(dv.getDataset().getGlobalId().toString() + ", v" + dv.getFriendlyVersionNumber());
×
2046
                            logger.fine("    " + dv.getDataset().getGlobalId().toString() + ", v" + dv.getFriendlyVersionNumber());
×
2047
                            current++;
×
2048
                        }
UNCOV
2049
                    }
×
2050
                    return ok(jab); 
×
2051
                }
UNCOV
2052
                String className = settingsService.getValueForKey(SettingsServiceBean.Key.ArchiverClassName);
×
2053
                // Note - the user is being sent via the createDataverseRequest(au) call to the
2054
                // back-end command where it is used to get the API Token which is
2055
                // then used to retrieve files (e.g. via S3 direct downloads) to create the Bag
UNCOV
2056
                final DataverseRequest request = createDataverseRequest(au);
×
2057
                // createSubmitToArchiveCommand() tries to find and instantiate an non-abstract
2058
                // implementation of AbstractSubmitToArchiveCommand based on the provided
2059
                // className. If a class with that name isn't found (or can't be instatiated, it
2060
                // will return null
UNCOV
2061
                AbstractSubmitToArchiveCommand cmd = ArchiverUtil.createSubmitToArchiveCommand(className, request, dsl.get(0));
×
2062
                if (cmd != null) {
×
2063
                    //Found an archiver to use
UNCOV
2064
                    new Thread(new Runnable() {
×
2065
                        public void run() {
UNCOV
2066
                            int total = dsl.size();
×
2067
                            int successes = 0;
×
2068
                            int failures = 0;
×
2069
                            for (DatasetVersion dv : dsl) {
×
2070
                                if (limit != null && (successes + failures) >= limit) {
×
2071
                                    break;
×
2072
                                }
UNCOV
2073
                                if (!latestonly || dv.equals(dv.getDataset().getLatestVersionForCopy())) {
×
2074
                                    try {
UNCOV
2075
                                        AbstractSubmitToArchiveCommand cmd = ArchiverUtil.createSubmitToArchiveCommand(className, request, dv);
×
2076

UNCOV
2077
                                        dv = commandEngine.submit(cmd);
×
2078
                                        if (!dv.getArchivalCopyLocationStatus().equals(DatasetVersion.ARCHIVAL_STATUS_FAILURE)) {
×
2079
                                            successes++;
×
2080
                                            logger.info("DatasetVersion id=" + dv.getDataset().getGlobalId().toString() + " v" + dv.getFriendlyVersionNumber() + " submitted to Archive, status: "
×
2081
                                                    + dv.getArchivalCopyLocationStatus());
×
2082
                                        } else {
UNCOV
2083
                                            failures++;
×
2084
                                            logger.severe("Error submitting version due to conflict/error at Archive for " + dv.getDataset().getGlobalId().toString() + " v" + dv.getFriendlyVersionNumber());
×
2085
                                        }
UNCOV
2086
                                    } catch (CommandException ex) {
×
2087
                                        failures++;
×
2088
                                        logger.log(Level.SEVERE, "Unexpected Exception calling  submit archive command", ex);
×
2089
                                    }
×
2090
                                }
UNCOV
2091
                                logger.fine(successes + failures + " of " + total + " archive submissions complete");
×
2092
                            }
×
2093
                            logger.info("Archiving complete: " + successes + " Successes, " + failures + " Failures. See prior log messages for details.");
×
2094
                        }
×
2095
                    }).start();
×
2096
                    return ok("Starting to archive all unarchived published dataset versions using " + cmd.getClass().getCanonicalName() + ". Processing can take significant time for large datasets/ large numbers of dataset versions  and requires that the user have permission to publish the dataset(s). View log and/or check archive for results.");
×
2097
                } else {
UNCOV
2098
                    logger.log(Level.SEVERE, "Could not find Archiver class: " + className);
×
2099
                    return error(Status.INTERNAL_SERVER_ERROR, "Could not find Archiver class: " + className);
×
2100
                }
2101
            } else {
UNCOV
2102
                return error(Status.BAD_REQUEST, "No unarchived published dataset versions found");
×
2103
            }
UNCOV
2104
        } catch (WrappedResponse e1) {
×
2105
            return e1.getResponse();
×
2106
        }
2107
    }
2108
    
2109
    @DELETE
2110
    @Path("/clearMetricsCache")
2111
    public Response clearMetricsCache() {
UNCOV
2112
        em.createNativeQuery("DELETE FROM metric").executeUpdate();
×
2113
        return ok("all metric caches cleared.");
×
2114
    }
2115

2116
    @DELETE
2117
    @Path("/clearMetricsCache/{name}")
2118
    public Response clearMetricsCacheByName(@PathParam("name") String name) {
UNCOV
2119
        Query deleteQuery = em.createNativeQuery("DELETE FROM metric where name = ?");
×
2120
        deleteQuery.setParameter(1, name);
×
2121
        deleteQuery.executeUpdate();
×
2122
        return ok("metric cache " + name + " cleared.");
×
2123
    }
2124

2125
    @GET
2126
        @AuthRequired
2127
    @Path("/dataverse/{alias}/addRoleAssignmentsToChildren")
2128
    public Response addRoleAssignementsToChildren(@Context ContainerRequestContext crc, @PathParam("alias") String alias) throws WrappedResponse {
UNCOV
2129
        Dataverse owner = dataverseSvc.findByAlias(alias);
×
2130
        if (owner == null) {
×
2131
            return error(Response.Status.NOT_FOUND, "Could not find dataverse based on alias supplied: " + alias + ".");
×
2132
        }
2133
        try {
UNCOV
2134
            AuthenticatedUser user = getRequestAuthenticatedUserOrDie(crc);
×
2135
            if (!user.isSuperuser()) {
×
2136
                return error(Response.Status.FORBIDDEN, "Superusers only.");
×
2137
            }
UNCOV
2138
        } catch (WrappedResponse wr) {
×
2139
            return wr.getResponse();
×
2140
        }
×
2141
        boolean inheritAllRoles = false;
×
2142
        String rolesString = settingsSvc.getValueForKey(SettingsServiceBean.Key.InheritParentRoleAssignments, "");
×
2143
        if (rolesString.length() > 0) {
×
2144
            ArrayList<String> rolesToInherit = new ArrayList<String>(Arrays.asList(rolesString.split("\\s*,\\s*")));
×
2145
            if (!rolesToInherit.isEmpty()) {
×
2146
                if (rolesToInherit.contains("*")) {
×
2147
                    inheritAllRoles = true;
×
2148
                }
UNCOV
2149
                return ok(dataverseSvc.addRoleAssignmentsToChildren(owner, rolesToInherit, inheritAllRoles));
×
2150
            }
2151
        }
UNCOV
2152
        return error(Response.Status.BAD_REQUEST,
×
2153
                "InheritParentRoleAssignments does not list any roles on this instance");
2154
    }
2155
    
2156
    @GET
2157
        @AuthRequired
2158
    @Path("/dataverse/{alias}/storageDriver")
2159
    public Response getStorageDriver(@Context ContainerRequestContext crc, @PathParam("alias") String alias) throws WrappedResponse {
UNCOV
2160
            Dataverse dataverse = dataverseSvc.findByAlias(alias);
×
2161
            if (dataverse == null) {
×
2162
                    return error(Response.Status.NOT_FOUND, "Could not find dataverse based on alias supplied: " + alias + ".");
×
2163
            }
2164
            try {
UNCOV
2165
                    AuthenticatedUser user = getRequestAuthenticatedUserOrDie(crc);
×
2166
                    if (!user.isSuperuser()) {
×
2167
                            return error(Response.Status.FORBIDDEN, "Superusers only.");
×
2168
                    }
UNCOV
2169
            } catch (WrappedResponse wr) {
×
2170
                    return wr.getResponse();
×
2171
            }
×
2172
            //Note that this returns what's set directly on this dataverse. If null/DataAccess.UNDEFINED_STORAGE_DRIVER_IDENTIFIER, the user would have to recurse the chain of parents to find the effective storageDriver
UNCOV
2173
            return ok(dataverse.getStorageDriverId());
×
2174
    }
2175
    
2176
    @PUT
2177
        @AuthRequired
2178
    @Path("/dataverse/{alias}/storageDriver")
2179
    public Response setStorageDriver(@Context ContainerRequestContext crc, @PathParam("alias") String alias, String label) throws WrappedResponse {
UNCOV
2180
            Dataverse dataverse = dataverseSvc.findByAlias(alias);
×
2181
            if (dataverse == null) {
×
2182
                    return error(Response.Status.NOT_FOUND, "Could not find dataverse based on alias supplied: " + alias + ".");
×
2183
            }
2184
            try {
UNCOV
2185
                    AuthenticatedUser user = getRequestAuthenticatedUserOrDie(crc);
×
2186
                    if (!user.isSuperuser()) {
×
2187
                            return error(Response.Status.FORBIDDEN, "Superusers only.");
×
2188
                    }
UNCOV
2189
            } catch (WrappedResponse wr) {
×
2190
                    return wr.getResponse();
×
2191
            }
×
2192
            for (Entry<String, String> store: DataAccess.getStorageDriverLabels().entrySet()) {
×
2193
                    if(store.getKey().equals(label)) {
×
2194
                            dataverse.setStorageDriverId(store.getValue());
×
2195
                            return ok("Storage set to: " + store.getKey() + "/" + store.getValue());
×
2196
                    }
UNCOV
2197
            }
×
2198
            return error(Response.Status.BAD_REQUEST,
×
2199
                            "No Storage Driver found for : " + label);
2200
    }
2201

2202
    @DELETE
2203
        @AuthRequired
2204
    @Path("/dataverse/{alias}/storageDriver")
2205
    public Response resetStorageDriver(@Context ContainerRequestContext crc, @PathParam("alias") String alias) throws WrappedResponse {
UNCOV
2206
            Dataverse dataverse = dataverseSvc.findByAlias(alias);
×
2207
            if (dataverse == null) {
×
2208
                    return error(Response.Status.NOT_FOUND, "Could not find dataverse based on alias supplied: " + alias + ".");
×
2209
            }
2210
            try {
UNCOV
2211
                    AuthenticatedUser user = getRequestAuthenticatedUserOrDie(crc);
×
2212
                    if (!user.isSuperuser()) {
×
2213
                            return error(Response.Status.FORBIDDEN, "Superusers only.");
×
2214
                    }
UNCOV
2215
            } catch (WrappedResponse wr) {
×
2216
                    return wr.getResponse();
×
2217
            }
×
2218
            dataverse.setStorageDriverId("");
×
2219
            return ok("Storage reset to default: " + DataAccess.DEFAULT_STORAGE_DRIVER_IDENTIFIER);
×
2220
    }
2221
    
2222
    @GET
2223
        @AuthRequired
2224
    @Path("/dataverse/storageDrivers")
2225
    public Response listStorageDrivers(@Context ContainerRequestContext crc) throws WrappedResponse {
2226
            try {
UNCOV
2227
                    AuthenticatedUser user = getRequestAuthenticatedUserOrDie(crc);
×
2228
                    if (!user.isSuperuser()) {
×
2229
                            return error(Response.Status.FORBIDDEN, "Superusers only.");
×
2230
                    }
UNCOV
2231
            } catch (WrappedResponse wr) {
×
2232
                    return wr.getResponse();
×
2233
            }
×
2234
            JsonObjectBuilder bld = jsonObjectBuilder();
×
2235
            DataAccess.getStorageDriverLabels().entrySet().forEach(s -> bld.add(s.getKey(), s.getValue()));
×
2236
                return ok(bld);
×
2237
    }
2238
    
2239
    @GET
2240
        @AuthRequired
2241
    @Path("/dataverse/{alias}/curationLabelSet")
2242
    public Response getCurationLabelSet(@Context ContainerRequestContext crc, @PathParam("alias") String alias) throws WrappedResponse {
UNCOV
2243
        Dataverse dataverse = dataverseSvc.findByAlias(alias);
×
2244
        if (dataverse == null) {
×
2245
            return error(Response.Status.NOT_FOUND, "Could not find dataverse based on alias supplied: " + alias + ".");
×
2246
        }
2247
        try {
UNCOV
2248
            AuthenticatedUser user = getRequestAuthenticatedUserOrDie(crc);
×
2249
            if (!user.isSuperuser()) {
×
2250
                return error(Response.Status.FORBIDDEN, "Superusers only.");
×
2251
            }
UNCOV
2252
        } catch (WrappedResponse wr) {
×
2253
            return wr.getResponse();
×
2254
        }
×
2255
        // Note that this returns what's set directly on this dataverse. If
2256
        // null/SystemConfig.DEFAULTCURATIONLABELSET, the user would have to recurse the
2257
        // chain of parents to find the effective curationLabelSet
UNCOV
2258
        return ok(dataverse.getCurationLabelSetName());
×
2259
    }
2260

2261
    @PUT
2262
        @AuthRequired
2263
    @Path("/dataverse/{alias}/curationLabelSet")
2264
    public Response setCurationLabelSet(@Context ContainerRequestContext crc, @PathParam("alias") String alias, @QueryParam("name") String name) throws WrappedResponse {
UNCOV
2265
        Dataverse dataverse = dataverseSvc.findByAlias(alias);
×
2266
        if (dataverse == null) {
×
2267
            return error(Response.Status.NOT_FOUND, "Could not find dataverse based on alias supplied: " + alias + ".");
×
2268
        }
2269
        try {
UNCOV
2270
            AuthenticatedUser user = getRequestAuthenticatedUserOrDie(crc);
×
2271
            if (!user.isSuperuser()) {
×
2272
                return error(Response.Status.FORBIDDEN, "Superusers only.");
×
2273
            }
UNCOV
2274
        } catch (WrappedResponse wr) {
×
2275
            return wr.getResponse();
×
2276
        }
×
2277
        if (SystemConfig.CURATIONLABELSDISABLED.equals(name) || SystemConfig.DEFAULTCURATIONLABELSET.equals(name)) {
×
2278
            dataverse.setCurationLabelSetName(name);
×
2279
            return ok("Curation Label Set Name set to: " + name);
×
2280
        } else {
UNCOV
2281
            for (String setName : systemConfig.getCurationLabels().keySet()) {
×
2282
                if (setName.equals(name)) {
×
2283
                    dataverse.setCurationLabelSetName(name);
×
2284
                    return ok("Curation Label Set Name set to: " + setName);
×
2285
                }
UNCOV
2286
            }
×
2287
        }
UNCOV
2288
        return error(Response.Status.BAD_REQUEST,
×
2289
                "No Curation Label Set found for : " + name);
2290
    }
2291

2292
    @DELETE
2293
        @AuthRequired
2294
    @Path("/dataverse/{alias}/curationLabelSet")
2295
    public Response resetCurationLabelSet(@Context ContainerRequestContext crc, @PathParam("alias") String alias) throws WrappedResponse {
UNCOV
2296
        Dataverse dataverse = dataverseSvc.findByAlias(alias);
×
2297
        if (dataverse == null) {
×
2298
            return error(Response.Status.NOT_FOUND, "Could not find dataverse based on alias supplied: " + alias + ".");
×
2299
        }
2300
        try {
UNCOV
2301
            AuthenticatedUser user = getRequestAuthenticatedUserOrDie(crc);
×
2302
            if (!user.isSuperuser()) {
×
2303
                return error(Response.Status.FORBIDDEN, "Superusers only.");
×
2304
            }
UNCOV
2305
        } catch (WrappedResponse wr) {
×
2306
            return wr.getResponse();
×
2307
        }
×
2308
        dataverse.setCurationLabelSetName(SystemConfig.DEFAULTCURATIONLABELSET);
×
2309
        return ok("Curation Label Set reset to default: " + SystemConfig.DEFAULTCURATIONLABELSET);
×
2310
    }
2311

2312
    @GET
2313
        @AuthRequired
2314
    @Path("/dataverse/curationLabelSets")
2315
    public Response listCurationLabelSets(@Context ContainerRequestContext crc) throws WrappedResponse {
2316
        try {
UNCOV
2317
            AuthenticatedUser user = getRequestAuthenticatedUserOrDie(crc);
×
2318
            if (!user.isSuperuser()) {
×
2319
                return error(Response.Status.FORBIDDEN, "Superusers only.");
×
2320
            }
UNCOV
2321
        } catch (WrappedResponse wr) {
×
2322
            return wr.getResponse();
×
2323
        }
×
2324
        JsonObjectBuilder bld = Json.createObjectBuilder();
×
2325

UNCOV
2326
        systemConfig.getCurationLabels().entrySet().forEach(s -> {
×
2327
            JsonArrayBuilder labels = Json.createArrayBuilder();
×
2328
            Arrays.asList(s.getValue()).forEach(l -> labels.add(l));
×
2329
            bld.add(s.getKey(), labels);
×
2330
        });
×
2331
        return ok(bld);
×
2332
    }
2333
    
2334
    @POST
2335
    @Path("/bannerMessage")
2336
    public Response addBannerMessage(JsonObject jsonObject) throws WrappedResponse {
2337

UNCOV
2338
        BannerMessage toAdd = new BannerMessage();
×
2339
        try {
2340

UNCOV
2341
            String dismissible = jsonObject.getString("dismissibleByUser");
×
2342

UNCOV
2343
            boolean dismissibleByUser = false;
×
2344
            if (dismissible.equals("true")) {
×
2345
                dismissibleByUser = true;
×
2346
            }
UNCOV
2347
            toAdd.setDismissibleByUser(dismissibleByUser);
×
2348
            toAdd.setBannerMessageTexts(new ArrayList());
×
2349
            toAdd.setActive(true);
×
2350
            JsonArray jsonArray = jsonObject.getJsonArray("messageTexts");
×
2351
            for (int i = 0; i < jsonArray.size(); i++) {
×
2352
                JsonObject obj = (JsonObject) jsonArray.get(i);
×
2353
                String message = obj.getString("message");
×
2354
                String lang = obj.getString("lang");
×
2355
                BannerMessageText messageText = new BannerMessageText();
×
2356
                messageText.setMessage(message);
×
2357
                messageText.setLang(lang);
×
2358
                messageText.setBannerMessage(toAdd);
×
2359
                toAdd.getBannerMessageTexts().add(messageText);
×
2360
            }
UNCOV
2361
            bannerMessageService.save(toAdd);
×
2362

UNCOV
2363
            JsonObjectBuilder jsonObjectBuilder = Json.createObjectBuilder()
×
2364
                .add("message", "Banner Message added successfully.")
×
2365
                .add("id", toAdd.getId());
×
2366

UNCOV
2367
            return ok(jsonObjectBuilder);
×
2368

UNCOV
2369
        } catch (Exception e) {
×
2370
            logger.warning("Unexpected Exception: " + e.getMessage());
×
2371
            return error(Status.BAD_REQUEST, "Add Banner Message unexpected exception: invalid or missing JSON object.");
×
2372
        }
2373

2374
    }
2375
    
2376
    @DELETE
2377
    @Path("/bannerMessage/{id}")
2378
    public Response deleteBannerMessage(@PathParam("id") Long id) throws WrappedResponse {
2379
 
UNCOV
2380
        BannerMessage message = em.find(BannerMessage.class, id);
×
2381
        if (message == null){
×
2382
            return error(Response.Status.NOT_FOUND, "Message id = "  + id + " not found.");
×
2383
        }
UNCOV
2384
        bannerMessageService.deleteBannerMessage(id);
×
2385
        
UNCOV
2386
        return ok("Message id =  " + id + " deleted.");
×
2387

2388
    }
2389
    
2390
    @PUT
2391
    @Path("/bannerMessage/{id}/deactivate")
2392
    public Response deactivateBannerMessage(@PathParam("id") Long id) throws WrappedResponse {
UNCOV
2393
        BannerMessage message = em.find(BannerMessage.class, id);
×
2394
        if (message == null){
×
2395
            return error(Response.Status.NOT_FOUND, "Message id = "  + id + " not found.");
×
2396
        }
UNCOV
2397
        bannerMessageService.deactivateBannerMessage(id);
×
2398
        
UNCOV
2399
        return ok("Message id =  " + id + " deactivated.");
×
2400

2401
    }
2402
    
2403
    @GET
2404
    @Path("/bannerMessage")
2405
    public Response getBannerMessages(@PathParam("id") Long id) throws WrappedResponse {
2406

UNCOV
2407
        List<BannerMessage> messagesList = bannerMessageService.findAllBannerMessages();
×
2408

UNCOV
2409
        for (BannerMessage message : messagesList) {
×
2410
            if ("".equals(message.getDisplayValue())) {
×
2411
               return error(Response.Status.INTERNAL_SERVER_ERROR, "No banner messages found for this locale.");
×
2412
            }
UNCOV
2413
        }
×
2414

UNCOV
2415
        JsonArrayBuilder messages = messagesList.stream()
×
2416
        .map(m -> jsonObjectBuilder().add("id", m.getId()).add("displayValue", m.getDisplayValue()))
×
2417
        .collect(toJsonArray());
×
2418
        
UNCOV
2419
        return ok(messages);
×
2420
    }
2421
    
2422
    @POST
2423
        @AuthRequired
2424
    @Consumes("application/json")
2425
    @Path("/requestSignedUrl")
2426
    public Response getSignedUrl(@Context ContainerRequestContext crc, JsonObject urlInfo) {
UNCOV
2427
        AuthenticatedUser superuser = null;
×
2428
        try {
UNCOV
2429
            superuser = getRequestAuthenticatedUserOrDie(crc);
×
2430
        } catch (WrappedResponse wr) {
×
2431
            return wr.getResponse();
×
2432
        }
×
2433
        if (superuser == null || !superuser.isSuperuser()) {
×
2434
            return error(Response.Status.FORBIDDEN, "Requesting signed URLs is restricted to superusers.");
×
2435
        }
2436
        
UNCOV
2437
        String userId = urlInfo.getString("user");
×
2438
        String key=null;
×
2439
        if (userId != null) {
×
2440
            AuthenticatedUser user = authSvc.getAuthenticatedUser(userId);
×
2441
            // If a user param was sent, we sign the URL for them, otherwise on behalf of
2442
            // the superuser who made this api call
UNCOV
2443
            if (user != null) {
×
2444
                ApiToken apiToken = authSvc.findApiTokenByUser(user);
×
2445
                if (apiToken != null && !apiToken.isExpired() && !apiToken.isDisabled()) {
×
2446
                    key = apiToken.getTokenString();
×
2447
                }
UNCOV
2448
            } else {
×
2449
                userId = superuser.getUserIdentifier();
×
2450
                // We ~know this exists - the superuser just used it and it was unexpired/not
2451
                // disabled. (ToDo - if we want this to work with workflow tokens (or as a
2452
                // signed URL), we should do more checking as for the user above))
UNCOV
2453
                key = authSvc.findApiTokenByUser(superuser).getTokenString();
×
2454
            }
UNCOV
2455
            if (key == null) {
×
2456
                return error(Response.Status.CONFLICT, "Do not have a valid user with apiToken");
×
2457
            }
UNCOV
2458
            key = JvmSettings.API_SIGNING_SECRET.lookupOptional().orElse("") + key;
×
2459
        }
2460
        
UNCOV
2461
        String baseUrl = urlInfo.getString("url");
×
2462
        int timeout = urlInfo.getInt(URLTokenUtil.TIMEOUT, 10);
×
2463
        String method = urlInfo.getString(URLTokenUtil.HTTP_METHOD, "GET");
×
2464
        
UNCOV
2465
        String signedUrl = UrlSignerUtil.signUrl(baseUrl, timeout, userId, method, key); 
×
2466
        
UNCOV
2467
        return ok(Json.createObjectBuilder().add(URLTokenUtil.SIGNED_URL, signedUrl));
×
2468
    }
2469
 
2470
    @DELETE
2471
    @Path("/clearThumbnailFailureFlag")
2472
    public Response clearThumbnailFailureFlag() {
UNCOV
2473
        em.createNativeQuery("UPDATE dvobject SET previewimagefail = FALSE").executeUpdate();
×
2474
        return ok("Thumbnail Failure Flags cleared.");
×
2475
    }
2476
    
2477
    @DELETE
2478
    @Path("/clearThumbnailFailureFlag/{id}")
2479
    public Response clearThumbnailFailureFlagByDatafile(@PathParam("id") String fileId) {
2480
        try {
UNCOV
2481
            DataFile df = findDataFileOrDie(fileId);
×
2482
            Query deleteQuery = em.createNativeQuery("UPDATE dvobject SET previewimagefail = FALSE where id = ?");
×
2483
            deleteQuery.setParameter(1, df.getId());
×
2484
            deleteQuery.executeUpdate();
×
2485
            return ok("Thumbnail Failure Flag cleared for file id=: " + df.getId() + ".");
×
2486
        } catch (WrappedResponse r) {
×
2487
            logger.info("Could not find file with the id: " + fileId);
×
2488
            return error(Status.BAD_REQUEST, "Could not find file with the id: " + fileId);
×
2489
        }
2490
    }
2491

2492
    /**
2493
     * For testing only. Download a file from /tmp.
2494
     */
2495
    @GET
2496
    @AuthRequired
2497
    @Path("/downloadTmpFile")
2498
    public Response downloadTmpFile(@Context ContainerRequestContext crc, @QueryParam("fullyQualifiedPathToFile") String fullyQualifiedPathToFile) {
2499
        try {
UNCOV
2500
            AuthenticatedUser user = getRequestAuthenticatedUserOrDie(crc);
×
2501
            if (!user.isSuperuser()) {
×
2502
                return error(Response.Status.FORBIDDEN, "Superusers only.");
×
2503
            }
UNCOV
2504
        } catch (WrappedResponse wr) {
×
2505
            return wr.getResponse();
×
2506
        }
×
2507
        java.nio.file.Path normalizedPath = Paths.get(fullyQualifiedPathToFile).normalize();
×
2508
        if (!normalizedPath.toString().startsWith("/tmp")) {
×
2509
            return error(Status.BAD_REQUEST, "Path must begin with '/tmp' but after normalization was '" + normalizedPath +"'.");
×
2510
        }
2511
        try {
UNCOV
2512
            return ok(new FileInputStream(fullyQualifiedPathToFile));
×
2513
        } catch (IOException ex) {
×
2514
            return error(Status.BAD_REQUEST, ex.toString());
×
2515
        }
2516
    }
2517

2518
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc