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

IQSS / dataverse / #23660

05 Nov 2024 05:11PM CUT coverage: 21.206% (-0.02%) from 21.224%
#23660

Pull #11001

github

GPortas
Changed: using JPA criteria instead of code looping for DatasetType query
Pull Request #11001: allow links between dataset types and metadata blocks

1 of 99 new or added lines in 7 files covered. (1.01%)

3 existing lines in 3 files now uncovered.

18316 of 86371 relevant lines covered (21.21%)

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.FeatureFlags;
104
import edu.harvard.iq.dataverse.settings.SettingsServiceBean;
105
import edu.harvard.iq.dataverse.userdata.UserListMaker;
106
import edu.harvard.iq.dataverse.userdata.UserListResult;
107
import edu.harvard.iq.dataverse.util.ArchiverUtil;
108
import edu.harvard.iq.dataverse.util.BundleUtil;
109
import edu.harvard.iq.dataverse.util.FileUtil;
110
import edu.harvard.iq.dataverse.util.SystemConfig;
111
import edu.harvard.iq.dataverse.util.URLTokenUtil;
112
import edu.harvard.iq.dataverse.util.UrlSignerUtil;
113

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

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

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

142
        private static final Logger logger = Logger.getLogger(Admin.class.getName());
×
143

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

183
    // Make the session available
184
    @Inject
185
    DataverseSession session;
186

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

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

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

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

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

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

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

225
                return ok("Setting " + name + " deleted.");
×
226
        }
227

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

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

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

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

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

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

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

299
            return ok(container);
×
300

301
        
302
    }
303

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

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

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

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

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

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

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

370
                row.setEnabled(enable);
×
371
                em.merge(row);
×
372

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

561
                User authUser = getRequestUser(crc);
×
562

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

568
                UserListMaker userListMaker = new UserListMaker(userService);
×
569

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

573
                return ok(userListResult.toJSON());
×
574
        }
575

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

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

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

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

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

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

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

995

996

997

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

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

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

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

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

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

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

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

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

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

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

1184
        Long dbId = dataset.getId();
×
1185

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

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

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

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

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

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

1294
                return ok(arr);
×
1295
        }
1296

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

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

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

1357
        }
1358

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

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

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

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

1398
                List<Long> affectedFileIds = fileService.selectFilesWithMissingOriginalTypes();
×
1399

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

1411
                ingestService.fixMissingOriginalTypes(affectedFileIds);
×
1412

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

1421
        List<Long> affectedFileIds = fileService.selectFilesWithMissingOriginalSizes();
×
1422

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

1440
        ingestService.fixMissingOriginalSizes(affectedFileIds);
×
1441
        return ok(info);
×
1442
    }
1443

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1860
        String newChecksum = "";
×
1861

1862
        InputStream in = null;
×
1863
        try {
1864

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

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

1882
        } finally {
1883
            IOUtils.closeQuietly(in);
×
1884
        }
1885

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

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

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

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

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

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

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

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

1946
        }
1947

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

1954
    }
1955

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

1962
        try {
1963
            AuthenticatedUser au = getRequestAuthenticatedUserOrDie(crc);
×
1964

1965
            Dataset ds = findDatasetOrDie(dsid);
×
1966

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

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

2034
        try {
2035
            AuthenticatedUser au = getRequestAuthenticatedUserOrDie(crc);
×
2036

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

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

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

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

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

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

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

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

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

2341
        BannerMessage toAdd = new BannerMessage();
×
2342
        try {
2343

2344
            String dismissible = jsonObject.getString("dismissibleByUser");
×
2345

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

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

2370
            return ok(jsonObjectBuilder);
×
2371

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

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

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

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

2410
        List<BannerMessage> messagesList = bannerMessageService.findAllBannerMessages();
×
2411

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

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

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

2521
    @GET
2522
    @Path("/featureFlags")
2523
    public Response getFeatureFlags() {
2524
        Map<String, String> map = new TreeMap<>();
×
2525
        for (FeatureFlags flag : FeatureFlags.values()) {
×
2526
            map.put(flag.name(), flag.enabled() ? "enabled" : "disabled");
×
2527
        }
2528
        return ok(Json.createObjectBuilder(map));
×
2529
    }
2530

2531
    @GET
2532
    @Path("/featureFlags/{flag}")
2533
    public Response getFeatureFlag(@PathParam("flag") String flagIn) {
2534
        try {
2535
            FeatureFlags flag = FeatureFlags.valueOf(flagIn);
×
2536
            JsonObjectBuilder job = Json.createObjectBuilder();
×
2537
            job.add("enabled", flag.enabled());
×
2538
            return ok(job);
×
2539
        } catch (IllegalArgumentException ex) {
×
2540
            return error(Status.NOT_FOUND, "Feature flag not found. Try listing all feature flags.");
×
2541
        }
2542
    }
2543

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