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

IQSS / dataverse / #23072

04 Sep 2024 02:25PM CUT coverage: 20.759% (-0.001%) from 20.76%
#23072

push

github

web-flow
Fix addDataverse expected request body structure (#10802)

* Fixed: MetadataBlockServiceBean to check for not excluded fields in input levels

* Changed: using queries for obtaining dataset field types based on displaying conditions

* Refactor: json printer method for MetadataBlock

* Added: IT test case for list metadata blocks testing field with include=false and displayOnCreate=true property

* Fixed: removed condition in MetadataBlockServiceBean

* Added: release notes for #10741

* Fixed: displayOnCreate query logic

* Fixed: excluding conditionally required fields when display-on-create is true

* Fixed: query predicate for required-in-dataverse field condition

* Fixed: addDataverse API facetIds field json structure

* Added: docs #10800

0 of 3 new or added lines in 1 file covered. (0.0%)

1 existing line in 1 file now uncovered.

17527 of 84429 relevant lines covered (20.76%)

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.nio.charset.StandardCharsets;
69
import java.util.Map;
70
import java.util.Map.Entry;
71
import java.util.logging.Level;
72
import java.util.logging.Logger;
73
import jakarta.ejb.EJB;
74
import jakarta.ejb.Stateless;
75
import jakarta.json.JsonObject;
76
import jakarta.json.JsonReader;
77
import jakarta.validation.ConstraintViolation;
78
import jakarta.validation.ConstraintViolationException;
79
import jakarta.ws.rs.Produces;
80
import jakarta.ws.rs.core.Response.Status;
81

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

297
            return ok(container);
×
298

299
        
300
    }
301

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

559
                User authUser = getRequestUser(crc);
×
560

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

566
                UserListMaker userListMaker = new UserListMaker(userService);
×
567

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

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

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

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

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

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

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

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

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

993

994

995

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

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

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

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

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

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

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

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

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

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

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

1182
        Long dbId = dataset.getId();
×
1183

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

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

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

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

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

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

1292
                return ok(arr);
×
1293
        }
1294

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

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

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

1355
        }
1356

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

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

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

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

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

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

1409
                ingestService.fixMissingOriginalTypes(affectedFileIds);
×
1410

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1858
        String newChecksum = "";
×
1859

1860
        InputStream in = null;
×
1861
        try {
1862

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

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

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

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

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

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

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

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

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

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

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

1944
        }
1945

1946
        if (currentChecksum.equals(calculatedChecksum)) {
×
1947
            return ok("Datafile validation complete for " + fileId + ". The hash value is: " + calculatedChecksum);
×
1948
        } else {
1949
            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);
×
1950
        }
1951

1952
    }
1953

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

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

1963
            Dataset ds = findDatasetOrDie(dsid);
×
1964

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

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

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

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

2078
                                        dv = commandEngine.submit(cmd);
×
2079
                                        if (!dv.getArchivalCopyLocationStatus().equals(DatasetVersion.ARCHIVAL_STATUS_FAILURE)) {
×
2080
                                            successes++;
×
2081
                                            logger.info("DatasetVersion id=" + dv.getDataset().getGlobalId().toString() + " v" + dv.getFriendlyVersionNumber() + " submitted to Archive, status: "
×
2082
                                                    + dv.getArchivalCopyLocationStatus());
×
2083
                                        } else {
2084
                                            failures++;
×
2085
                                            logger.severe("Error submitting version due to conflict/error at Archive for " + dv.getDataset().getGlobalId().toString() + " v" + dv.getFriendlyVersionNumber());
×
2086
                                        }
2087
                                    } catch (CommandException ex) {
×
2088
                                        failures++;
×
2089
                                        logger.log(Level.SEVERE, "Unexpected Exception calling  submit archive command", ex);
×
2090
                                    }
×
2091
                                }
2092
                                logger.fine(successes + failures + " of " + total + " archive submissions complete");
×
2093
                            }
×
2094
                            logger.info("Archiving complete: " + successes + " Successes, " + failures + " Failures. See prior log messages for details.");
×
2095
                        }
×
2096
                    }).start();
×
2097
                    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.");
×
2098
                } else {
2099
                    logger.log(Level.SEVERE, "Could not find Archiver class: " + className);
×
2100
                    return error(Status.INTERNAL_SERVER_ERROR, "Could not find Archiver class: " + className);
×
2101
                }
2102
            } else {
2103
                return error(Status.BAD_REQUEST, "No unarchived published dataset versions found");
×
2104
            }
2105
        } catch (WrappedResponse e1) {
×
2106
            return e1.getResponse();
×
2107
        }
2108
    }
2109
    
2110
    @DELETE
2111
    @Path("/clearMetricsCache")
2112
    public Response clearMetricsCache() {
2113
        em.createNativeQuery("DELETE FROM metric").executeUpdate();
×
2114
        return ok("all metric caches cleared.");
×
2115
    }
2116

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

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

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

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

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

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

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

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

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

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

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

2368
            return ok(jsonObjectBuilder);
×
2369

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

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

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

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

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

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

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

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

2519
}
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