Skip to content

Commit 982b9db

Browse files
authored
Merge pull request #3931 from IQSS/3246-user-login
#3246: added last login/creation time/last API use for authenticateduser
2 parents ba61257 + 5aaeb2e commit 982b9db

18 files changed

Lines changed: 557 additions & 804 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
--Uncomment to preserve "Dataverse" at end of each dataverse name.
2-
--UPDATE dataverse SET name = name || ' Dataverse';
2+
--UPDATE dataverse SET name = name || ' Dataverse';
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- Adding new columns for "createdtime", "lastlogintime", and "lastapiusetime"
2+
-- Default "createdtime" to 1/1/2000
3+
-- Dropping "modificationtime" as it is inconsistent between user auths and best replaced by the new columns.
4+
ALTER TABLE authenticateduser ADD COLUMN createdtime TIMESTAMP NOT NULL DEFAULT '01-01-2000 00:00:00';
5+
ALTER TABLE authenticateduser ADD COLUMN lastlogintime TIMESTAMP DEFAULT NULL;
6+
ALTER TABLE authenticateduser ADD COLUMN lastapiusetime TIMESTAMP DEFAULT NULL;
7+
ALTER TABLE authenticateduser DROP COLUMN modificationtime;

src/main/java/edu/harvard/iq/dataverse/UserServiceBean.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
44
import edu.harvard.iq.dataverse.search.IndexServiceBean;
5+
import java.sql.Timestamp;
6+
import java.util.Date;
57
import java.util.logging.Logger;
68
import javax.ejb.EJB;
79
import javax.ejb.Stateless;
@@ -24,15 +26,33 @@ public AuthenticatedUser find(Object pk) {
2426
return (AuthenticatedUser) em.find(AuthenticatedUser.class, pk);
2527
}
2628

27-
public AuthenticatedUser save( AuthenticatedUser user ) {
28-
if ( user.getId() == null ) {
29+
30+
public AuthenticatedUser save(AuthenticatedUser user) {
31+
if (user.getId() == null) {
2932
em.persist(this);
3033
} else {
34+
if (user.getCreatedTime() == null) {
35+
user.setCreatedTime(new Timestamp(new Date().getTime())); // default new creation time
36+
user.setLastLoginTime(user.getCreatedTime()); // sets initial lastLoginTime to creation time
37+
logger.info("Creation time null! Setting user creation time to now");
38+
}
3139
user = em.merge(user);
3240
}
3341
em.flush();
3442

3543
return user;
3644
}
3745

46+
public AuthenticatedUser updateLastLogin(AuthenticatedUser user) {
47+
//assumes that AuthenticatedUser user already exists
48+
user.setLastLoginTime(new Timestamp(new Date().getTime()));
49+
50+
return save(user);
51+
}
52+
53+
public AuthenticatedUser updateLastApiUseTime(AuthenticatedUser user) {
54+
//assumes that AuthenticatedUser user already exists
55+
user.setLastApiUseTime(new Timestamp(new Date().getTime()));
56+
return save(user);
57+
}
3858
}

src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -328,10 +328,14 @@ protected AuthenticatedUser findAuthenticatedUserOrDie() throws WrappedResponse
328328
return findAuthenticatedUserOrDie(getRequestApiKey());
329329
}
330330

331+
331332
private AuthenticatedUser findAuthenticatedUserOrDie( String key ) throws WrappedResponse {
332-
AuthenticatedUser u = authSvc.lookupUser(key);
333-
if ( u != null ) {
334-
return u;
333+
AuthenticatedUser authUser = authSvc.lookupUser(key);
334+
if ( authUser != null ) {
335+
System.out.println("Updating lastApiUseTime for authenticated user via abstractapibean");
336+
authUser = userSvc.updateLastApiUseTime(authUser);
337+
338+
return authUser;
335339
}
336340
throw new WrappedResponse( badApiKey(key) );
337341
}

src/main/java/edu/harvard/iq/dataverse/api/datadeposit/SwordAuth.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ public AuthenticatedUser auth(AuthCredentials authCredentials) throws SwordAuthE
4242
logger.fine(msg);
4343
throw new SwordAuthException(msg);
4444
} else {
45+
46+
authenticatedUserFromToken = userSvc.updateLastApiUseTime(authenticatedUserFromToken);
4547
return authenticatedUserFromToken;
4648
}
4749
}

src/main/java/edu/harvard/iq/dataverse/authorization/AuthenticationServiceBean.java

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package edu.harvard.iq.dataverse.authorization;
22

33
import edu.harvard.iq.dataverse.UserNotificationServiceBean;
4+
import edu.harvard.iq.dataverse.UserServiceBean;
45
import edu.harvard.iq.dataverse.search.IndexServiceBean;
56
import edu.harvard.iq.dataverse.actionlogging.ActionLogRecord;
67
import edu.harvard.iq.dataverse.actionlogging.ActionLogServiceBean;
@@ -95,6 +96,9 @@ public class AuthenticationServiceBean {
9596
@EJB
9697
PasswordResetServiceBean passwordResetServiceBean;
9798

99+
@EJB
100+
UserServiceBean userService;
101+
98102
@PersistenceContext(unitName = "VDCNet-ejbPU")
99103
private EntityManager em;
100104

@@ -321,13 +325,17 @@ public AuthenticatedUser authenticate( String authenticationProviderId, Authenti
321325
// yay! see if we already have this user.
322326
AuthenticatedUser user = lookupUser(authenticationProviderId, resp.getUserId());
323327

328+
if (user != null){
329+
user = userService.updateLastLogin(user);
330+
}
331+
324332
/**
325333
* @todo Why does a method called "authenticate" have the potential
326334
* to call "createAuthenticatedUser"? Isn't the creation of a user a
327335
* different action than authenticating?
328336
*
329337
* @todo Wouldn't this be more readable with if/else rather than
330-
* ternary?
338+
* ternary? (please)
331339
*/
332340
return ( user == null ) ?
333341
AuthenticationServiceBean.this.createAuthenticatedUser(
@@ -433,14 +441,12 @@ public AuthenticatedUser lookupUser( String apiToken ) {
433441
}
434442

435443
public AuthenticatedUser save( AuthenticatedUser user ) {
436-
user.setModificationTime(getCurrentTimestamp());
437444
em.persist(user);
438445
em.flush();
439446
return user;
440447
}
441448

442449
public AuthenticatedUser update( AuthenticatedUser user ) {
443-
user.setModificationTime(getCurrentTimestamp());
444450
return em.merge(user);
445451
}
446452

@@ -493,12 +499,16 @@ public boolean updateProvider( AuthenticatedUser authenticatedUser, String authe
493499
* @throws EJBException which may wrap an ConstraintViolationException if the proposed user does not pass bean validation.
494500
*/
495501
public AuthenticatedUser createAuthenticatedUser(UserRecordIdentifier userRecordId,
496-
String proposedAuthenticatedUserIdentifier,
497-
AuthenticatedUserDisplayInfo userDisplayInfo,
498-
boolean generateUniqueIdentifier) {
502+
String proposedAuthenticatedUserIdentifier,
503+
AuthenticatedUserDisplayInfo userDisplayInfo,
504+
boolean generateUniqueIdentifier) {
499505
AuthenticatedUser authenticatedUser = new AuthenticatedUser();
500-
authenticatedUser.applyDisplayInfo(userDisplayInfo);
506+
// set account creation time & initial login time (same timestamp)
507+
authenticatedUser.setCreatedTime(new Timestamp(new Date().getTime()));
508+
authenticatedUser.setLastLoginTime(authenticatedUser.getCreatedTime());
501509

510+
authenticatedUser.applyDisplayInfo(userDisplayInfo);
511+
502512
// we have no desire for leading or trailing whitespace in identifiers
503513
if (proposedAuthenticatedUserIdentifier != null) {
504514
proposedAuthenticatedUserIdentifier = proposedAuthenticatedUserIdentifier.trim();
@@ -535,7 +545,7 @@ public AuthenticatedUser createAuthenticatedUser(UserRecordIdentifier userRecord
535545
* better to do something like "startConfirmEmailProcessForNewUser". */
536546
confirmEmailService.createToken(authenticatedUser);
537547
}
538-
548+
539549
actionLogSvc.log( new ActionLogRecord(ActionLogRecord.ActionType.Auth, "createUser")
540550
.setInfo(authenticatedUser.getIdentifier()));
541551

src/main/java/edu/harvard/iq/dataverse/authorization/providers/builtin/DataverseUserPage.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import edu.harvard.iq.dataverse.UserNotification;
1818
import static edu.harvard.iq.dataverse.UserNotification.Type.CREATEDV;
1919
import edu.harvard.iq.dataverse.UserNotificationServiceBean;
20+
import edu.harvard.iq.dataverse.UserServiceBean;
2021
import edu.harvard.iq.dataverse.authorization.AuthUtil;
2122
import edu.harvard.iq.dataverse.authorization.AuthenticatedUserDisplayInfo;
2223
import edu.harvard.iq.dataverse.authorization.AuthenticationProvider;
@@ -82,6 +83,8 @@ public enum EditMode {
8283
@EJB
8384
UserNotificationServiceBean userNotificationService;
8485
@EJB
86+
UserServiceBean userService;
87+
@EJB
8588
DatasetServiceBean datasetService;
8689
@EJB
8790
DataFileServiceBean fileService;
@@ -307,14 +310,17 @@ public String save() {
307310
new UserRecordIdentifier(BuiltinAuthenticationProvider.PROVIDER_ID, builtinUser.getUserName()),
308311
builtinUser.getUserName(), builtinUser.getDisplayInfo(), false);
309312
if ( au == null ) {
310-
// username exists
313+
// Username already exists, show an error message
311314
getUsernameField().setValid(false);
312315
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, BundleUtil.getStringFromBundle("user.username.taken"), null);
313316
FacesContext context = FacesContext.getCurrentInstance();
314317
context.addMessage(getUsernameField().getClientId(context), message);
315318
return null;
316319
}
317320

321+
// The Authenticated User was just created via the UI, add an initial login timestamp
322+
au = userService.updateLastLogin(au);
323+
318324
// Authenticated user registered. Save the new bulitin, and log in.
319325
builtinUserService.save(builtinUser);
320326
session.setUser(au);

src/main/java/edu/harvard/iq/dataverse/authorization/users/AuthenticatedUser.java

Lines changed: 58 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import static edu.harvard.iq.dataverse.util.StringUtil.nonEmpty;
99
import java.io.Serializable;
1010
import java.sql.Timestamp;
11+
import java.util.Date;
1112
import java.util.List;
1213
import java.util.Objects;
1314
import javax.persistence.CascadeType;
@@ -73,19 +74,26 @@ public class AuthenticatedUser implements User, Serializable {
7374
private String email;
7475
private String affiliation;
7576
private String position;
77+
7678
@NotBlank(message = "Please enter your last name.")
7779
private String lastName;
80+
7881
@NotBlank(message = "Please enter your first name.")
7982
private String firstName;
83+
8084
@Column(nullable = true)
8185
private Timestamp emailConfirmed;
82-
private boolean superuser;
86+
//TODO: add the word time after next 3 columns
87+
@Column(nullable=false)
88+
private Timestamp createdTime;
89+
90+
@Column(nullable=true)
91+
private Timestamp lastLoginTime; // last user login timestamp
8392

84-
/**
85-
* @todo Remove? Check for accuracy? For Solr JOINs we used to care about
86-
* the modification times of users but now we don't index users at all.
87-
*/
88-
private Timestamp modificationTime;
93+
@Column(nullable=true)
94+
private Timestamp lastApiUseTime; // last API use with user's token
95+
96+
private boolean superuser;
8997

9098
/**
9199
* @todo Consider storing a hash of *all* potentially interesting Shibboleth
@@ -213,10 +221,6 @@ public void setSuperuser(boolean superuser) {
213221
this.superuser = superuser;
214222
}
215223

216-
public void setModificationTime(Timestamp modificationTime) {
217-
this.modificationTime = modificationTime;
218-
}
219-
220224
@OneToOne(mappedBy = "authenticatedUser")
221225
private AuthenticatedUserLookup authenticatedUserLookup;
222226

@@ -262,4 +266,47 @@ public String getSortByString() {
262266
return this.getLastName() + " " + this.getFirstName() + " " + this.getUserIdentifier();
263267
}
264268

265-
}
269+
/**
270+
*
271+
* @param lastLoginTime
272+
*/
273+
public void setLastLoginTime(Timestamp lastLoginTime){
274+
275+
this.lastLoginTime = lastLoginTime;
276+
}
277+
278+
/**
279+
* @param lastLoginTime
280+
*/
281+
public Timestamp getLastLoginTime(){
282+
return this.lastLoginTime;
283+
}
284+
285+
286+
public void setCreatedTime(Timestamp createdTime){
287+
this.createdTime = createdTime;
288+
}
289+
290+
public Timestamp getCreatedTime(){
291+
return this.createdTime;
292+
}
293+
294+
295+
/**
296+
*
297+
* @param lastApiUseTime
298+
*/
299+
public void setLastApiUseTime(Timestamp lastApiUseTime){
300+
this.lastApiUseTime = lastApiUseTime;
301+
}
302+
303+
/**
304+
*
305+
* @param lastApiUseTime
306+
*/
307+
public Timestamp getLastApiUseTime(){
308+
309+
return this.lastApiUseTime;
310+
}
311+
312+
}

src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ public static JsonObjectBuilder jsonForAuthUser(AuthenticatedUser authenticatedU
119119
.add("position", authenticatedUser.getPosition())
120120
.add("persistentUserId", authenticatedUser.getAuthenticatedUserLookup().getPersistentUserId())
121121
.add("emailLastConfirmed", authenticatedUser.getEmailConfirmed())
122+
.add("createdTime", authenticatedUser.getCreatedTime())
123+
.add("lastLoginTime", authenticatedUser.getLastLoginTime())
124+
.add("lastApiUseTime", authenticatedUser.getLastApiUseTime())
122125
.add("authenticationProviderId", authenticatedUser.getAuthenticatedUserLookup().getAuthenticationProviderId());
123126
}
124127

src/main/webapp/file_replace/dataset_file_list.xhtml

Lines changed: 0 additions & 39 deletions
This file was deleted.

0 commit comments

Comments
 (0)