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

CeON / dataverse / 1371

28 Jun 2024 05:54AM UTC coverage: 25.453% (-0.05%) from 25.503%
1371

push

jenkins

web-flow
Closes #2474: Store the entity id in saml session once user logs in with the identity provider (#2486)

49 of 54 new or added lines in 4 files covered. (90.74%)

860 existing lines in 14 files now uncovered.

17805 of 69953 relevant lines covered (25.45%)

0.25 hits per line

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

94.23
/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/users/SamlSessionRegistry.java
1
package edu.harvard.iq.dataverse.users;
2

3
import edu.harvard.iq.dataverse.DataverseSession;
4
import edu.harvard.iq.dataverse.authorization.providers.saml.SamlUserData;
5
import edu.harvard.iq.dataverse.persistence.user.AuthenticatedUser;
6
import io.vavr.control.Option;
7

8
import javax.ejb.Singleton;
9
import java.util.ArrayList;
10
import java.util.Collections;
11
import java.util.HashMap;
12
import java.util.List;
13
import java.util.Map;
14
import java.util.UUID;
15
import java.util.concurrent.ConcurrentHashMap;
16

17
@Singleton
18
public class SamlSessionRegistry {
1✔
19
    private final Map<String, SamlUserSession> samlUsers = new ConcurrentHashMap<>();
1✔
20

21
    // -------------------- LOGIC --------------------
22

23
    public void register(DataverseSession dvSession, SamlUserData userData) {
24
        samlUsers.compute(userData.getCompositeId(), (compositeKey, existingSession) -> {
1✔
25
            if (existingSession == null) {
1✔
26
                return new SamlUserSession(userData.getIdpEntityId(), dvSession);
1✔
27
            } else {
28
                existingSession.addDataverseSession(dvSession);
1✔
29
                return existingSession;
1✔
30
            }
31
        });
32
    }
1✔
33

34
    public List<DataverseSession> unregister(DataverseSession session) {
35
        cleanUp();
1✔
36
        if (session == null || !session.getUser().isAuthenticated()) {
1✔
UNCOV
37
            return Collections.emptyList();
×
38
        }
39

40
        return resolvePersistentUserId((AuthenticatedUser) session.getUser())
1✔
41
                .map(persistentUserId -> removeDataverseSession(persistentUserId, session))
1✔
42
                .getOrElse(Collections.emptyList());
1✔
43
    }
44

45
    public List<DataverseSession> unregister(AuthenticatedUser authenticatedUser) {
46
        cleanUp();
1✔
47
        return resolvePersistentUserId(authenticatedUser)
1✔
48
                .map(samlUsers::remove)
1✔
49
                .map(SamlUserSession::getDataverseSessions)
1✔
50
                .getOrElse(Collections.emptyList());
1✔
51
    }
52

53
    public Option<String> findEntityId(AuthenticatedUser authenticatedUser) {
54
        return resolvePersistentUserId(authenticatedUser)
1✔
55
                .flatMap(persistentUserId -> Option.of(samlUsers.get(persistentUserId)))
1✔
56
                .map(SamlUserSession::getEntityId);
1✔
57
    }
58

59
    // -------------------- PRIVATE --------------------
60

61
    /**
62
     * Removes sessions that were actually invalidated, but somehow not unregistered.
63
     * In case of huge amount of users should be invoked independently (eg. by @Schedule).
64
     */
65
    private void cleanUp() {
66
        samlUsers.entrySet()
1✔
67
                .removeIf(e -> {
1✔
68
                    e.getValue().cleanUpDataverseSessions();
1✔
69
                    return e.getValue().hasNoDataverseSessions();
1✔
70
                });
71
    }
1✔
72

73
    private Option<String> resolvePersistentUserId(AuthenticatedUser authenticatedUser) {
74
        if (authenticatedUser == null || authenticatedUser.getAuthenticatedUserLookup() == null) {
1✔
NEW
75
            return Option.none();
×
76
        }
77

78
        return Option.of(authenticatedUser.getAuthenticatedUserLookup().getPersistentUserId());
1✔
79
    }
80

81
    private List<DataverseSession> removeDataverseSession(String persistentUserId, DataverseSession session) {
82
        List<DataverseSession> removed = new ArrayList<>();
1✔
83

84
        samlUsers.compute(persistentUserId, (k, samlSession) -> {
1✔
85
            if (samlSession == null) {
1✔
NEW
86
                return null;
×
87
            }
88

89
            samlSession.removeDataverseSession(session.getSessionId()).forEach(removed::add);
1✔
90
            if (samlSession.hasNoDataverseSessions()) {
1✔
91
                return null;
1✔
92
            }
93

94
            return samlSession;
1✔
95
        });
96

97
        return removed;
1✔
98
    }
99

100
    // -------------------- INNER CLASSES --------------------
101

102
    private static class SamlUserSession {
103
        private final String entityId;
104
        private final Map<UUID, DataverseSession> dataverseSessions = new HashMap<>();
1✔
105

106
        SamlUserSession(String entityId, DataverseSession session) {
1✔
107
            this.entityId = entityId;
1✔
108
            dataverseSessions.put(session.getSessionId(), session);
1✔
109
        }
1✔
110

111
        String getEntityId() {
112
            return entityId;
1✔
113
        }
114

115
        List<DataverseSession> getDataverseSessions() {
116
            return new ArrayList<>(dataverseSessions.values());
1✔
117
        }
118

119
        boolean hasNoDataverseSessions() {
120
            return dataverseSessions.isEmpty();
1✔
121
        }
122

123
        void addDataverseSession(DataverseSession session) {
124
            dataverseSessions.put(session.getSessionId(), session);
1✔
125
        }
1✔
126

127
        Option<DataverseSession> removeDataverseSession(UUID sessionId) {
128
            return Option.of(dataverseSessions.remove(sessionId));
1✔
129
        }
130

131
        void cleanUpDataverseSessions() {
132
            dataverseSessions.values().removeIf(session -> !session.getUser().isAuthenticated());
1✔
133
        }
1✔
134
    }
135
}
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

© 2026 Coveralls, Inc