From 2ee969466ed26c32641d56104209364d93aad386 Mon Sep 17 00:00:00 2001 From: chasegawa Date: Mon, 22 May 2023 13:54:25 -0700 Subject: [PATCH 01/12] SHIBUI-2568 Adding tracking of logins for use in telemetry reporting --- .../ui/security/model/UserLoginRecord.java | 34 ++++++++++++ .../repository/UserLoginRecordRepository.java | 10 ++++ .../ui/security/service/UserService.java | 25 +++++++++ .../springsecurity/AdminUserService.java | 1 + .../admin/ui/AbstractBaseDataJpaTest.groovy | 4 ++ .../security/service/UserServiceTests.groovy | 38 ++++++++++++++ .../unicon/shibui/pac4j/AddNewUserFilter.java | 2 + .../shibui/pac4j/AddNewUserFilterTests.groovy | 52 +++++++++++++++++-- 8 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/UserLoginRecord.java create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/UserLoginRecordRepository.java diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/UserLoginRecord.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/UserLoginRecord.java new file mode 100644 index 000000000..75e5fa728 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/UserLoginRecord.java @@ -0,0 +1,34 @@ +package edu.internet2.tier.shibboleth.admin.ui.security.model; + +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import java.util.Date; + +@Data +@Entity +@Table(uniqueConstraints = { @UniqueConstraint(columnNames = { "username", "login" }) }) +public class UserLoginRecord { + @Id + @GeneratedValue + private Long id; + + private String username; + + private String login; + + private Date loginDate; + + public UserLoginRecord() { + } + + public UserLoginRecord(String username, Date loginDate, String formattedDate) { + this.username = username; + this.login = formattedDate; + this.loginDate = loginDate; + } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/UserLoginRecordRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/UserLoginRecordRepository.java new file mode 100644 index 000000000..b5819bcd5 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/UserLoginRecordRepository.java @@ -0,0 +1,10 @@ +package edu.internet2.tier.shibboleth.admin.ui.security.repository; + +import edu.internet2.tier.shibboleth.admin.ui.security.model.UserLoginRecord; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface UserLoginRecordRepository extends JpaRepository { + Optional findByUsernameAndLogin(String username, String formattedDate); +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java index 429dfa6c2..82429ab26 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java @@ -10,8 +10,10 @@ import edu.internet2.tier.shibboleth.admin.ui.security.model.Ownership; import edu.internet2.tier.shibboleth.admin.ui.security.model.Role; import edu.internet2.tier.shibboleth.admin.ui.security.model.User; +import edu.internet2.tier.shibboleth.admin.ui.security.model.UserLoginRecord; import edu.internet2.tier.shibboleth.admin.ui.security.repository.OwnershipRepository; import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository; +import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserLoginRecordRepository; import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository; import lombok.NoArgsConstructor; import org.apache.commons.lang.StringUtils; @@ -21,7 +23,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -34,6 +38,8 @@ @Service @NoArgsConstructor public class UserService { + private final static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd-MM-yyyy"); + @Autowired private IGroupService groupService; @@ -43,6 +49,9 @@ public class UserService { @Autowired private RoleRepository roleRepository; + @Autowired + private UserLoginRecordRepository userLoginRecordRepository; + @Autowired private UserRepository userRepository; @@ -139,6 +148,8 @@ public Set getUserRoles(String username) { HashSet result = new HashSet<>(); user.ifPresent(value -> value.getRoles().forEach(role -> result.add(role.getName()))); return result; + + } // @TODO - probably delegate this out to something plugable at some point @@ -236,4 +247,18 @@ public void updateUserRole(User user) { public boolean currentUserCanEnable() { return getCurrentUser().getRole().equals("ROLE_ENABLE"); } + + /** + * Ensure there exists a login record for this username and today's date + * @param username + */ + public void updateLoginRecord(String username) { + Date current = new Date(); + String formattedDate = DATE_FORMAT.format(current); + + if (userLoginRecordRepository.findByUsernameAndLogin(username,formattedDate).isEmpty()) { + UserLoginRecord ulr = new UserLoginRecord(username, current, formattedDate); + userLoginRecordRepository.saveAndFlush(ulr); + } + } } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java index 34e6ffd06..c7da5f668 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java @@ -44,6 +44,7 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx throw new UsernameNotFoundException(String.format("No roles are defined for user [%s]", username)); } + userService.updateLoginRecord(username); return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities); } } \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/AbstractBaseDataJpaTest.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/AbstractBaseDataJpaTest.groovy index fda2f6b8c..8db64d5f8 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/AbstractBaseDataJpaTest.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/AbstractBaseDataJpaTest.groovy @@ -10,6 +10,7 @@ import edu.internet2.tier.shibboleth.admin.ui.security.repository.ApproversRepos import edu.internet2.tier.shibboleth.admin.ui.security.repository.GroupsRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.OwnershipRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository +import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserLoginRecordRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository import edu.internet2.tier.shibboleth.admin.ui.security.service.GroupServiceForTesting import edu.internet2.tier.shibboleth.admin.ui.security.service.IGroupService @@ -63,6 +64,9 @@ abstract class AbstractBaseDataJpaTest extends Specification implements ResetsDa @Autowired RoleRepository roleRepository + @Autowired + UserLoginRecordRepository userLoginRecordRepository + @Autowired UserRepository userRepository diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/UserServiceTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/UserServiceTests.groovy index 73c56aa91..df99bf0b3 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/UserServiceTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/UserServiceTests.groovy @@ -5,12 +5,16 @@ import edu.internet2.tier.shibboleth.admin.ui.security.model.Group import edu.internet2.tier.shibboleth.admin.ui.security.model.Ownership import edu.internet2.tier.shibboleth.admin.ui.security.model.Role import edu.internet2.tier.shibboleth.admin.ui.security.model.User +import edu.internet2.tier.shibboleth.admin.ui.security.model.UserLoginRecord + +import java.text.SimpleDateFormat class UserServiceTests extends AbstractBaseDataJpaTest { Role userRole def setup() { userRole = roleRepository.findByName("ROLE_USER").get() + userLoginRecordRepository.deleteAll() } protected createAdminUser() { @@ -163,4 +167,38 @@ class UserServiceTests extends AbstractBaseDataJpaTest { then: gbUpdated.ownedItems.size() == 1 } + + def "when user login - ensure record is created"() { + given: + SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd-MM-yyyy") + + Date current = new Date(); + String formattedDate = DATE_FORMAT.format(current) + UserLoginRecord ulr = new UserLoginRecord("username", current, formattedDate) + + expect: + userLoginRecordRepository.findByUsernameAndLogin("username", formattedDate).isEmpty() + userLoginRecordRepository.count() == 0 + + when: + userService.updateLoginRecord("username") + + then: + userLoginRecordRepository.findByUsernameAndLogin("username", formattedDate).isPresent() + userLoginRecordRepository.count() == 1 + + when: 'try adding again should not change result' + userService.updateLoginRecord("username") + + then: + userLoginRecordRepository.findByUsernameAndLogin("username", formattedDate).isPresent() + userLoginRecordRepository.count() == 1 + + when: + userService.updateLoginRecord("username2") + + then: + userLoginRecordRepository.findByUsernameAndLogin("username2", formattedDate).isPresent() + userLoginRecordRepository.count() == 2 + } } \ No newline at end of file diff --git a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/AddNewUserFilter.java b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/AddNewUserFilter.java index b837fb081..d9365ac06 100644 --- a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/AddNewUserFilter.java +++ b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/AddNewUserFilter.java @@ -135,6 +135,8 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha if (user.getRole().equals(ROLE_NONE)) { ((HttpServletResponse) response).sendRedirect("/unsecured/error.html"); } else { + // User exists or has been created and has a role so we can continue on. + userService.updateLoginRecord(username); chain.doFilter(request, response); // else, user is in the system already, carry on } } diff --git a/pac4j-module/src/test/groovy/net/unicon/shibui/pac4j/AddNewUserFilterTests.groovy b/pac4j-module/src/test/groovy/net/unicon/shibui/pac4j/AddNewUserFilterTests.groovy index d58ecbd4c..2097ccaa1 100644 --- a/pac4j-module/src/test/groovy/net/unicon/shibui/pac4j/AddNewUserFilterTests.groovy +++ b/pac4j-module/src/test/groovy/net/unicon/shibui/pac4j/AddNewUserFilterTests.groovy @@ -1,16 +1,16 @@ package net.unicon.shibui.pac4j +import edu.internet2.tier.shibboleth.admin.ui.security.model.Group import edu.internet2.tier.shibboleth.admin.ui.security.model.Role import edu.internet2.tier.shibboleth.admin.ui.security.model.User import edu.internet2.tier.shibboleth.admin.ui.security.repository.OwnershipRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository +import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserLoginRecordRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository import edu.internet2.tier.shibboleth.admin.ui.security.service.GroupServiceForTesting +import edu.internet2.tier.shibboleth.admin.ui.security.service.IRolesService import edu.internet2.tier.shibboleth.admin.ui.security.service.RolesServiceImpl import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService -import jakarta.servlet.FilterChain -import jakarta.servlet.http.HttpServletRequest -import jakarta.servlet.http.HttpServletResponse import org.pac4j.core.matching.matcher.PathMatcher import org.pac4j.saml.profile.SAML2Profile import org.springframework.beans.factory.annotation.Autowired @@ -27,6 +27,11 @@ import org.springframework.transaction.annotation.Transactional import spock.lang.Specification import spock.lang.Subject +import javax.servlet.FilterChain +import javax.servlet.http.HttpServletRequest +import javax.servlet.http.HttpServletResponse +import java.text.SimpleDateFormat + @DataJpaTest @ContextConfiguration(classes=[Pac4JTestingConfig]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) @@ -52,6 +57,9 @@ class AddNewUserFilterTests extends Specification { @Autowired Pac4jConfigurationProperties pac4jConfigurationProperties + @Autowired + UserLoginRecordRepository userLoginRecordRepository + @Autowired UserRepository userRepository @@ -99,6 +107,44 @@ class AddNewUserFilterTests extends Specification { roles.each { roleRepository.save(it) } + + Group gb = new Group() + gb.setResourceId("testingGroupBBB") + gb.setName("Group BBB") + gb.setValidationRegex("^(?:https?:\\/\\/)?(?:[^.]+\\.)?shib\\.org(\\/.*)?\$") + gb = groupService.createGroup(gb) + + Optional userRole = roleRepository.findByName("ROLE_USER") + User user = new User(username: "someUser", roles:[userRole.get()], password: "foo") + user.setGroup(gb) + userService.save(user) + } + + def "user logged in"() { + given: + ['Username': 'someUser', + 'FirstName': 'Some', + 'LastName': 'User', + 'Email': 'someUser@institution.edu'].each { key, value -> + saml2Profile.getAttribute(profileMapping."get${key}"()) >> [value] + } + saml2Profile.getUsername() >> "someUser" + + SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd-MM-yyyy") + + Date current = new Date(); + String formattedDate = DATE_FORMAT.format(current) + + expect: + userLoginRecordRepository.findByUsernameAndLogin("someUser", formattedDate).isEmpty() + userLoginRecordRepository.count() == 0 + + when: + addNewUserFilter.doFilter(request, response, chain) + + then: + userLoginRecordRepository.findByUsernameAndLogin("someUser", formattedDate).isPresent() + userLoginRecordRepository.count() == 1 } def "new user created"() { From 6914a0851c86d3ca7944ff04b4207827aab9aa8b Mon Sep 17 00:00:00 2001 From: chasegawa Date: Tue, 23 May 2023 13:32:38 -0700 Subject: [PATCH 02/12] SHIBUI-2568 Adding tracking of logins for use in telemetry reporting --- .../ui/security/controller/UsersController.java | 7 +++++++ .../repository/UserLoginRecordRepository.java | 6 ++++++ .../admin/ui/security/service/UserService.java | 7 +++++++ .../ui/security/service/UserServiceTests.groovy | 13 +++++++++++++ 4 files changed, 33 insertions(+) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/controller/UsersController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/controller/UsersController.java index a2faab3d0..f20ce7889 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/controller/UsersController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/controller/UsersController.java @@ -90,6 +90,13 @@ public User getCurrentUser(Principal principal) { return userService.getCurrentUser(); } + @PreAuthorize("hasRole('ADMIN')") + @Transactional(readOnly = true) + @GetMapping("/loginCount") + public int getDailyLoginCount() { + return userService.getDailyLoginCount(); + } + @PreAuthorize("hasRole('ADMIN')") @Transactional(readOnly = true) @GetMapping("/{username}") diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/UserLoginRecordRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/UserLoginRecordRepository.java index b5819bcd5..22a97fabb 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/UserLoginRecordRepository.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/UserLoginRecordRepository.java @@ -2,9 +2,15 @@ import edu.internet2.tier.shibboleth.admin.ui.security.model.UserLoginRecord; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import java.util.Date; import java.util.Optional; public interface UserLoginRecordRepository extends JpaRepository { Optional findByUsernameAndLogin(String username, String formattedDate); + + @Query(value = "SELECT count(*) FROM UserLoginRecord ulr WHERE ulr.loginDate >= :sinceDate") + int countLoginsSince(@Param("sinceDate") Date sinceDate); } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java index 82429ab26..e2a45b12f 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java @@ -17,6 +17,7 @@ import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository; import lombok.NoArgsConstructor; import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.time.DateUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -24,6 +25,7 @@ import org.springframework.transaction.annotation.Transactional; import java.text.SimpleDateFormat; +import java.time.LocalDate; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; @@ -261,4 +263,9 @@ public void updateLoginRecord(String username) { userLoginRecordRepository.saveAndFlush(ulr); } } + + public int getDailyLoginCount() { + Date since = DateUtils.addDays(new Date(), -1); + return userLoginRecordRepository.countLoginsSince(since); + } } \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/UserServiceTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/UserServiceTests.groovy index df99bf0b3..5b7b9feb0 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/UserServiceTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/UserServiceTests.groovy @@ -6,6 +6,7 @@ import edu.internet2.tier.shibboleth.admin.ui.security.model.Ownership import edu.internet2.tier.shibboleth.admin.ui.security.model.Role import edu.internet2.tier.shibboleth.admin.ui.security.model.User import edu.internet2.tier.shibboleth.admin.ui.security.model.UserLoginRecord +import org.apache.commons.lang.time.DateUtils import java.text.SimpleDateFormat @@ -193,6 +194,7 @@ class UserServiceTests extends AbstractBaseDataJpaTest { then: userLoginRecordRepository.findByUsernameAndLogin("username", formattedDate).isPresent() userLoginRecordRepository.count() == 1 + userService.getDailyLoginCount() == 1 when: userService.updateLoginRecord("username2") @@ -200,5 +202,16 @@ class UserServiceTests extends AbstractBaseDataJpaTest { then: userLoginRecordRepository.findByUsernameAndLogin("username2", formattedDate).isPresent() userLoginRecordRepository.count() == 2 + userService.getDailyLoginCount() == 2 + + when: 'older logins in db, should be same counts' + Date older = DateUtils.addDays(new Date(), -3) + String formattedDate2 = DATE_FORMAT.format(older) + UserLoginRecord ulr2 = new UserLoginRecord("username", older, formattedDate2) + userLoginRecordRepository.save(ulr2) + + then: + userLoginRecordRepository.count() == 3 + userService.getDailyLoginCount() == 2 } } \ No newline at end of file From a9c8bcacc6c1bffffa487bb90e4749ae2a64a1b2 Mon Sep 17 00:00:00 2001 From: chasegawa Date: Thu, 25 May 2023 10:14:53 -0700 Subject: [PATCH 03/12] SHIBUI-2571 Intermediate work commit - feature incomplete --- backend/src/main/app-resources/default.yml | 14 ++++ .../admin/ui/beacon/BeaconDetail.java | 21 ++++++ .../admin/ui/beacon/ShibuiDetail.java | 21 ++++++ .../CoreShibUiConfiguration.java | 20 ++++++ .../BeaconConfigurationStartupProcessing.java | 68 +++++++++++++++++++ .../ui/controller/RootUiViewController.java | 7 ++ .../admin/ui/domain/BeaconConfiguration.java | 24 +++++++ .../BeaconConfigurationRepository.java | 8 +++ .../ui/scheduled/BeaconReportingTask.java | 25 +++++++ .../ui/service/BeaconDataServiceImpl.java | 33 +++++++++ .../admin/ui/service/IBeaconDataService.java | 7 ++ .../src/main/resources/application.properties | 6 +- .../ui/configuration/TestConfiguration.groovy | 7 ++ ...ConfigurationStartupProcessingTests.groovy | 39 +++++++++++ .../EntitiesControllerIntegrationTests.groovy | 3 - 15 files changed, 298 insertions(+), 5 deletions(-) create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/BeaconDetail.java create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/ShibuiDetail.java create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/postprocessors/BeaconConfigurationStartupProcessing.java create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/BeaconConfiguration.java create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/BeaconConfigurationRepository.java create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/scheduled/BeaconReportingTask.java create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/BeaconDataServiceImpl.java create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/IBeaconDataService.java create mode 100644 backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/postprocessors/BeaconConfigurationStartupProcessingTests.groovy diff --git a/backend/src/main/app-resources/default.yml b/backend/src/main/app-resources/default.yml index 949a8e4b5..5ba247176 100644 --- a/backend/src/main/app-resources/default.yml +++ b/backend/src/main/app-resources/default.yml @@ -19,6 +19,20 @@ ## The first time the scheduler executes on this node, write out the entity descriptors to file [true|false] (default is true) # entityDescriptor: # writeOnStartup: true +### BEACON SETTINGS +## Set enabled to false to disable sending beacon data +## Only set the installationID if you wish to define the ID used by the beacon. RECOMMENDED: ignore - a default random value will be used +## Only change the urls if instructed or if you wish to redirect for testing (if multiple, separate list with commas +## Only change the productName for testing purposes +## Set the cron time to send the beacon data at a specific time - if unset, the system will send at a random time between 12AM and 4AM once per day +# beacon: +# enabled: ture +# installationID: [user-defined value] +# urls: http://collector.testbed.tier.internet2.edu:5001 +# productName: ShibUI +# send: +# cron: 0 4 * * * * + # pac4j-enabled: true # pac4j: # keystorePath: "/etc/shibui/samlKeystore.jks" diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/BeaconDetail.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/BeaconDetail.java new file mode 100644 index 000000000..24c9ccbc4 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/BeaconDetail.java @@ -0,0 +1,21 @@ +package edu.internet2.tier.shibboleth.admin.ui.beacon; + +import lombok.Data; + +@Data +public class BeaconDetail { + private String msgType = "TIERBEACON"; + private String msgName = "TIER"; + private String msgVersion = "1.0"; + + private String tbProduct; + private String tbProductVersion; + private String tbTIERRelease; + private String tbMaintainer = "Unicon"; + + private ShibuiDetail shibui; +} + +// "tbProduct": "ShibUI (shibui.beacon.productName)", +// "tbProductVersion": "1.17.4 (can we get this from the app?)", +// "tbTIERRelease": "(if the TAP container: 1.17.4; if the Unicon package, INTERNAL_1.17.4)", \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/ShibuiDetail.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/ShibuiDetail.java new file mode 100644 index 000000000..fdf0abf6b --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/ShibuiDetail.java @@ -0,0 +1,21 @@ +package edu.internet2.tier.shibboleth.admin.ui.beacon; + +import lombok.Data; + +@Data +public class ShibuiDetail { + + + // "shibui": { + // "authMechanisms": [ + // "pac4j-saml" + // ], + // "numberOfMetadataSources": 25, + // "numberOfMetadataProviders": 3, + // "numberOfFilters": 0, + // "dailyLogins": 12, + // "numberOfGroups": 15, + // "numberOfRoles": 5, + // "installationID": "4813c762-4a90-40a2-9c01-81bf67647da4" + // } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java index d1c1e9a0b..f059b5fc6 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java @@ -1,7 +1,9 @@ package edu.internet2.tier.shibboleth.admin.ui.configuration; import com.fasterxml.jackson.databind.Module; +import edu.internet2.tier.shibboleth.admin.ui.domain.BeaconConfiguration; import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects; +import edu.internet2.tier.shibboleth.admin.ui.repository.BeaconConfigurationRepository; import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository; import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository; import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolversPositionOrderContainerRepository; @@ -19,6 +21,7 @@ import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository; import edu.internet2.tier.shibboleth.admin.ui.security.service.IGroupService; import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService; +import edu.internet2.tier.shibboleth.admin.ui.service.BeaconDataServiceImpl; import edu.internet2.tier.shibboleth.admin.ui.service.DefaultMetadataResolversPositionOrderContainerService; import edu.internet2.tier.shibboleth.admin.ui.service.DirectoryService; import edu.internet2.tier.shibboleth.admin.ui.service.DirectoryServiceImpl; @@ -29,6 +32,7 @@ import edu.internet2.tier.shibboleth.admin.ui.service.FileCheckingFileWritingService; import edu.internet2.tier.shibboleth.admin.ui.service.FileWritingService; import edu.internet2.tier.shibboleth.admin.ui.service.FilterTargetService; +import edu.internet2.tier.shibboleth.admin.ui.service.IBeaconDataService; import edu.internet2.tier.shibboleth.admin.ui.service.JPADynamicRegistrationServiceImpl; import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityServiceImpl; import edu.internet2.tier.shibboleth.admin.ui.service.JPAFilterTargetServiceImpl; @@ -42,10 +46,12 @@ import jakarta.servlet.http.HttpServletRequest; import net.javacrumbs.shedlock.core.LockProvider; import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider; +import org.apache.commons.lang3.StringUtils; import org.apache.lucene.analysis.Analyzer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.actuate.info.InfoEndpoint; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -64,6 +70,7 @@ import org.springframework.web.util.UrlPathHelper; import javax.sql.DataSource; +import java.util.Optional; @SpringBootConfiguration @Import(SearchConfiguration.class) @@ -254,4 +261,17 @@ public DynamicRegistrationService dynamicRegistrationService(DynamicRegistration public LockProvider lockProvider(DataSource dataSource) { return new JdbcTemplateLockProvider(JdbcTemplateLockProvider.Configuration.builder().withJdbcTemplate(new JdbcTemplate(dataSource)).usingDbTime().build()); } + + @Bean + public String getBeaconCronValue(BeaconConfigurationRepository repo) + { + Optional bc = repo.findById(1); + return bc.isPresent() ? bc.get().getSendCron() : "0 3 * * * *"; + } + + @Bean + public IBeaconDataService getBeaconDataService(@Value("${shibui.beacon.productName:ShibUi}") String productName, InfoEndpoint info, @Value("#{environment.TIERVERSION}") String tierVersion) { + BeaconDataServiceImpl result = new BeaconDataServiceImpl(productName, info); + return result; + } } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/postprocessors/BeaconConfigurationStartupProcessing.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/postprocessors/BeaconConfigurationStartupProcessing.java new file mode 100644 index 000000000..ee7e1a1e9 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/postprocessors/BeaconConfigurationStartupProcessing.java @@ -0,0 +1,68 @@ +package edu.internet2.tier.shibboleth.admin.ui.configuration.postprocessors; + +import edu.internet2.tier.shibboleth.admin.ui.domain.BeaconConfiguration; +import edu.internet2.tier.shibboleth.admin.ui.repository.BeaconConfigurationRepository; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +import javax.persistence.EntityExistsException; +import javax.transaction.Transactional; +import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; + +/** + * This approach for running logic waits until after the Spring context has been initialized. + * We will always use an ID of 1 for the beaconConfiguration, so if the data doesn't exist, we can try to write the data. If the + * data exists, we don't need to do anything. + * + * Because multiple instances could be starting at the same time, we must account for a save "losing" to another entity - that's ok + * and if we get an exception because the entity with the id already exists, we can just bail out. + */ +@Component +public class BeaconConfigurationStartupProcessing { + @Autowired + BeaconConfigurationRepository beaconRepo; + + @EventListener + @Transactional + public void onApplicationEvent(ContextRefreshedEvent event) { + // If there is nothing in the db, create the entry to use going forward + if (beaconRepo.count() == 0) { + String appId = event.getApplicationContext().getEnvironment().getProperty("shibui.beacon.installationID"); + BeaconConfiguration bc = new BeaconConfiguration(); + bc.setInstallationId(StringUtils.isBlank(appId) ? UUID.randomUUID().toString() : appId); + + String cronDef = event.getApplicationContext().getEnvironment().getProperty("shibui.beacon.send.cron"); + + // If not set, set a random time between 12AM-4AM + if (StringUtils.isBlank(cronDef)) { + int randomMin = ThreadLocalRandom.current().nextInt(0, 60); + int randomHour = ThreadLocalRandom.current().nextInt(0, 5); + cronDef = "" + randomMin + " " + randomHour + " * * * *"; + } + + bc.setSendCron(cronDef); + + try { + beaconRepo.save(bc); + } + catch (EntityExistsException ignore) { + // Race between startup instances - as long as one of them won... + } + } + // if the entry exists, check that the cron timing hasn't changed + else { + String cronDef = event.getApplicationContext().getEnvironment().getProperty("shibui.beacon.send.cron"); + if (StringUtils.isNotBlank(cronDef)) { + BeaconConfiguration bc = beaconRepo.getReferenceById(1); + if (StringUtils.isNotBlank(cronDef) && !cronDef.equals(bc.getSendCron())) { + bc.setSendCron(cronDef); + beaconRepo.save(bc); + } + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/RootUiViewController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/RootUiViewController.java index 69fd53158..5883bc349 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/RootUiViewController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/RootUiViewController.java @@ -3,6 +3,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.info.InfoEndpoint; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -18,6 +19,7 @@ @Controller public class RootUiViewController { + @Autowired HealthEndpoint healthEndpoint; @Autowired InfoEndpoint infoEndpoint; @@ -46,4 +48,9 @@ public void indexHtml(HttpServletRequest request, HttpServletResponse response) writer.write(content.getBytes()); } } + + @GetMapping(value = "/health") + public ResponseEntity getHealth() { + return ResponseEntity.ok(healthEndpoint.health()); + } } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/BeaconConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/BeaconConfiguration.java new file mode 100644 index 000000000..93d76fdc3 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/BeaconConfiguration.java @@ -0,0 +1,24 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain; + +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Data +@Entity +public class BeaconConfiguration { + @Id + private int id = 1; + + private String installationId; + + private String sendCron; + + /** + * enforce that id will only ever be 1 + */ + public void setId(int ignored) { + this.id = 1; + } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/BeaconConfigurationRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/BeaconConfigurationRepository.java new file mode 100644 index 000000000..346a6566e --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/BeaconConfigurationRepository.java @@ -0,0 +1,8 @@ +package edu.internet2.tier.shibboleth.admin.ui.repository; + +import edu.internet2.tier.shibboleth.admin.ui.domain.BeaconConfiguration; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface BeaconConfigurationRepository extends JpaRepository { + +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/scheduled/BeaconReportingTask.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/scheduled/BeaconReportingTask.java new file mode 100644 index 000000000..c87d7017c --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/scheduled/BeaconReportingTask.java @@ -0,0 +1,25 @@ +package edu.internet2.tier.shibboleth.admin.ui.scheduled; + +import edu.internet2.tier.shibboleth.admin.ui.service.IBeaconDataService; +import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock; +import net.javacrumbs.shedlock.spring.annotation.SchedulerLock; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.transaction.annotation.Transactional; + +@Configuration +@ConditionalOnProperty(name = "shibui.beacon.enabled", matchIfMissing=true) +@EnableSchedulerLock(defaultLockAtMostFor = "${shibui.maxTask.lockTime:30m}") +public class BeaconReportingTask { + @Autowired + IBeaconDataService dataService; + + @Scheduled(cron="#{@getBeaconCronValue}") + @SchedulerLock(name = "generateEntityDescriptorFiles") + @Transactional(readOnly = true) + public void generateEntityDescriptorFiles() { + + } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/BeaconDataServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/BeaconDataServiceImpl.java new file mode 100644 index 000000000..b9f97ea9a --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/BeaconDataServiceImpl.java @@ -0,0 +1,33 @@ +package edu.internet2.tier.shibboleth.admin.ui.service; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import edu.internet2.tier.shibboleth.admin.ui.beacon.BeaconDetail; +import lombok.SneakyThrows; +import org.springframework.boot.actuate.info.InfoEndpoint; + +import java.util.Map; + +public class BeaconDataServiceImpl implements IBeaconDataService { + private ObjectMapper mapper; + private String productName; + private String version; + + public BeaconDataServiceImpl(String productName, InfoEndpoint info) { + mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // skip any null values + + this.productName = productName; + version = info.info().get("build") == null ? "unknown" : ((Map)info.info().get("build")).get("version").toString(); + } + + @Override + @SneakyThrows + public String getBeaconData() throws JsonProcessingException { + BeaconDetail detail = new BeaconDetail(); + detail.setTbProduct(productName); + detail.setTbProductVersion(version); + return mapper.writeValueAsString(detail); + } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/IBeaconDataService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/IBeaconDataService.java new file mode 100644 index 000000000..4509b0584 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/IBeaconDataService.java @@ -0,0 +1,7 @@ +package edu.internet2.tier.shibboleth.admin.ui.service; + +import com.fasterxml.jackson.core.JsonProcessingException; + +public interface IBeaconDataService { + String getBeaconData() throws JsonProcessingException; +} \ No newline at end of file diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 1fd9b2b28..ac76ebb36 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -121,7 +121,9 @@ shibui.pac4j-enabled=false #This property must be set to true in order to enable posting stats to beacon endpoint. Furthermore, appropriate #environment variables must be set for beacon publisher to be used (the ones that are set when running shib-ui in #docker container -shibui.beacon-enabled=true +shibui.beacon.enabled=true +shibui.beacon.productName=ShibUi +shibui.beacon.installationID=UNICON-SHIBUI-TESTING ### Swagger/Springdoc patterns springdoc.use-management-port=true @@ -129,7 +131,7 @@ springdoc.swagger-ui.tagsSorter: alpha springdoc.writer-with-order-by-keys: true springdoc.pathsToMatch=/entities, /api/** # This property enables the openapi and swagger-ui endpoints to be exposed beneath the actuator base path. -management.endpoints.web.exposure.include=openapi, swagger-ui, info +management.endpoints.web.exposure.include=openapi, swagger-ui, info, health management.server.port=9090 management.endpoints.web.cors.allowed-origins=* management.endpoints.web.cors.allowed-headers=* diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy index 345b0e2b7..a04043631 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy @@ -24,6 +24,8 @@ import org.opensaml.saml.metadata.resolver.impl.ResourceBackedMetadataResolver import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.actuate.info.InfoContributor +import org.springframework.boot.actuate.info.InfoEndpoint import org.springframework.boot.SpringBootConfiguration import org.springframework.boot.web.client.RestTemplateBuilder import org.springframework.context.annotation.Bean @@ -134,4 +136,9 @@ class TestConfiguration { return it } } + + @Bean + public InfoEndpoint getInfoEndpoint() { + return new InfoEndpoint(new ArrayList()); + } } \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/postprocessors/BeaconConfigurationStartupProcessingTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/postprocessors/BeaconConfigurationStartupProcessingTests.groovy new file mode 100644 index 000000000..ed14ab8a2 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/postprocessors/BeaconConfigurationStartupProcessingTests.groovy @@ -0,0 +1,39 @@ +package edu.internet2.tier.shibboleth.admin.ui.configuration.postprocessors + +import edu.internet2.tier.shibboleth.admin.ui.AbstractBaseDataJpaTest +import edu.internet2.tier.shibboleth.admin.ui.domain.BeaconConfiguration +import edu.internet2.tier.shibboleth.admin.ui.repository.BeaconConfigurationRepository +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.context.ApplicationContext +import org.springframework.context.event.ContextRefreshedEvent +import org.springframework.core.env.Environment + +import static org.mockito.Mockito.mock +import static org.mockito.Mockito.when + +class BeaconConfigurationStartupProcessingTests extends AbstractBaseDataJpaTest { + @Autowired + BeaconConfigurationRepository repo + + def 'test'() { + when: + def event = mock(ContextRefreshedEvent) + def appContext = mock(ApplicationContext) + def env = mock(Environment) + when(event.getApplicationContext()).thenReturn(appContext) + when(appContext.getEnvironment()).thenReturn(env) + when(env.getProperty("shibui.beacon.installationID")).thenReturn(null) + when(env.getProperty("shibui.beacon.send.cron")).thenReturn(null) + + BeaconConfigurationStartupProcessing testObject = new BeaconConfigurationStartupProcessing().with { + it.beaconRepo = repo + it + } + testObject.onApplicationEvent(event) + + then: + BeaconConfiguration bc = repo.getReferenceById(1) + bc.getInstallationId() != null + bc.getSendCron() != null + } +} \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntitiesControllerIntegrationTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntitiesControllerIntegrationTests.groovy index 3fb344313..c9bacb09a 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntitiesControllerIntegrationTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntitiesControllerIntegrationTests.groovy @@ -11,7 +11,6 @@ import edu.internet2.tier.shibboleth.admin.ui.security.model.Group import edu.internet2.tier.shibboleth.admin.ui.security.model.Ownership import edu.internet2.tier.shibboleth.admin.ui.security.model.Role import edu.internet2.tier.shibboleth.admin.ui.security.model.User -import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorVersionService import edu.internet2.tier.shibboleth.admin.ui.service.EntityService import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityDescriptorServiceImpl import edu.internet2.tier.shibboleth.admin.ui.util.RandomGenerator @@ -62,8 +61,6 @@ class EntitiesControllerIntegrationTests extends AbstractBaseDataJpaTest { @Subject def controller - EntityDescriptorVersionService versionService = Mock() - @Transactional def setup() { openSamlObjects.init() From ed2d2065a449db1af99131652a97af51dc95b806 Mon Sep 17 00:00:00 2001 From: chasegawa Date: Thu, 25 May 2023 16:15:43 -0700 Subject: [PATCH 04/12] SHIBUI-2571 Intermediate work commit - feature incomplete --- .../admin/ui/beacon/BeaconDetail.java | 8 +-- .../admin/ui/beacon/ShibuiDetail.java | 26 ++++---- .../CoreShibUiConfiguration.java | 7 ++- .../admin/ui/controller/BeaconController.java | 25 ++++++++ .../admin/ui/domain/BeaconConfiguration.java | 11 ++++ .../EntityDescriptorRepository.java | 2 + .../admin/ui/repository/FilterRepository.java | 6 +- .../MetadataResolverRepository.java | 6 +- .../ui/service/BeaconDataServiceImpl.java | 59 ++++++++++++++++--- delete.json | 16 +++++ 10 files changed, 136 insertions(+), 30 deletions(-) create mode 100644 backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/BeaconController.java create mode 100644 delete.json diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/BeaconDetail.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/BeaconDetail.java index 24c9ccbc4..ac690b1cb 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/BeaconDetail.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/BeaconDetail.java @@ -1,8 +1,10 @@ package edu.internet2.tier.shibboleth.admin.ui.beacon; import lombok.Data; +import lombok.experimental.Accessors; @Data +@Accessors(chain = true) public class BeaconDetail { private String msgType = "TIERBEACON"; private String msgName = "TIER"; @@ -14,8 +16,4 @@ public class BeaconDetail { private String tbMaintainer = "Unicon"; private ShibuiDetail shibui; -} - -// "tbProduct": "ShibUI (shibui.beacon.productName)", -// "tbProductVersion": "1.17.4 (can we get this from the app?)", -// "tbTIERRelease": "(if the TAP container: 1.17.4; if the Unicon package, INTERNAL_1.17.4)", \ No newline at end of file +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/ShibuiDetail.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/ShibuiDetail.java index fdf0abf6b..ec89e5f00 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/ShibuiDetail.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/ShibuiDetail.java @@ -1,21 +1,19 @@ package edu.internet2.tier.shibboleth.admin.ui.beacon; +import lombok.Builder; import lombok.Data; +import java.util.List; + @Data +@Builder public class ShibuiDetail { - - - // "shibui": { - // "authMechanisms": [ - // "pac4j-saml" - // ], - // "numberOfMetadataSources": 25, - // "numberOfMetadataProviders": 3, - // "numberOfFilters": 0, - // "dailyLogins": 12, - // "numberOfGroups": 15, - // "numberOfRoles": 5, - // "installationID": "4813c762-4a90-40a2-9c01-81bf67647da4" - // } + private List authMechanisms; + private int numberOfMetadataSources; + private int numberOfMetadataProviders; + private int numberOfFilters; + private int dailyLogins; + private int numberOfGroups; + private int numberOfRoles; + private String installationID; } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java index f059b5fc6..d6d137e50 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java @@ -5,6 +5,7 @@ import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects; import edu.internet2.tier.shibboleth.admin.ui.repository.BeaconConfigurationRepository; import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository; +import edu.internet2.tier.shibboleth.admin.ui.repository.FilterRepository; import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository; import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolversPositionOrderContainerRepository; import edu.internet2.tier.shibboleth.admin.ui.scheduled.EntityDescriptorFilesScheduledTasks; @@ -270,8 +271,10 @@ public String getBeaconCronValue(BeaconConfigurationRepository repo) } @Bean - public IBeaconDataService getBeaconDataService(@Value("${shibui.beacon.productName:ShibUi}") String productName, InfoEndpoint info, @Value("#{environment.TIERVERSION}") String tierVersion) { - BeaconDataServiceImpl result = new BeaconDataServiceImpl(productName, info); + public IBeaconDataService getBeaconDataService(@Value("${shibui.beacon.productName:ShibUi}") String productName, InfoEndpoint info, @Value("#{environment.TIERVERSION}") String tierVersion, + EntityDescriptorRepository entityDescriptorRepository, MetadataResolverRepository metadataResolverRepository, FilterRepository filterRepository, + GroupsRepository groupsRepository, RoleRepository roleRepository, BeaconConfigurationRepository beaconConfigurationRepository, UserService userService) { + BeaconDataServiceImpl result = new BeaconDataServiceImpl(productName, info, tierVersion, entityDescriptorRepository, metadataResolverRepository, filterRepository, groupsRepository, roleRepository, beaconConfigurationRepository, userService); return result; } } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/BeaconController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/BeaconController.java new file mode 100644 index 000000000..f5ece2032 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/BeaconController.java @@ -0,0 +1,25 @@ +package edu.internet2.tier.shibboleth.admin.ui.controller; + +import com.fasterxml.jackson.core.JsonProcessingException; +import edu.internet2.tier.shibboleth.admin.ui.service.IBeaconDataService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Profile({"dev", "very-dangerous"}) +@RestController +@RequestMapping(value = "/api/beacon") +public class BeaconController { + + @Autowired + private IBeaconDataService service; + + + @GetMapping(value = "/detail") + public ResponseEntity getDetail() throws JsonProcessingException { + return ResponseEntity.ok(service.getBeaconData()); + } +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/BeaconConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/BeaconConfiguration.java index 93d76fdc3..c3b263c77 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/BeaconConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/BeaconConfiguration.java @@ -1,9 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; import lombok.Data; +import lombok.Setter; import javax.persistence.Entity; import javax.persistence.Id; +import javax.persistence.Transient; @Data @Entity @@ -15,6 +17,15 @@ public class BeaconConfiguration { private String sendCron; + // Comma separated list of the auth mechanisms used. + @Setter + @Transient + private static String authMechanisms = "unset"; + + public static String getAuthMechanisms() { + return authMechanisms; + } + /** * enforce that id will only ever be 1 */ diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepository.java index 7fb999568..44ca96afe 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepository.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/EntityDescriptorRepository.java @@ -60,4 +60,6 @@ public interface EntityDescriptorRepository extends JpaRepository getEntityDescriptorsNeedingApproval(@Param("groupIds") List groupIds); + @Query("SELECT COUNT(ed) FROM EntityDescriptor ed WHERE ed.serviceEnabled = true") + int getActiveEntityCount(); } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepository.java index 8c2273d77..e36d55d3c 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepository.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/FilterRepository.java @@ -1,8 +1,12 @@ package edu.internet2.tier.shibboleth.admin.ui.repository; import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; public interface FilterRepository extends CrudRepository { MetadataFilter findByResourceId(String resourceId); -} + + @Query("SELECT COUNT(f) FROM MetadataFilter f WHERE f.filterEnabled = true") + int getActiveFilterCount(); +} \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepository.java index 07835ea9a..333fb2c11 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepository.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolverRepository.java @@ -1,6 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.repository; import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; /** @@ -11,4 +12,7 @@ public interface MetadataResolverRepository extends CrudRepository Date: Fri, 26 May 2023 12:35:15 -0700 Subject: [PATCH 05/12] SHIBUI-2571 Beacon gathering and sending --- .../CoreShibUiConfiguration.java | 14 +++++++++ .../admin/ui/controller/BeaconController.java | 11 ++++++- .../ui/scheduled/BeaconReportingTask.java | 30 +++++++++++++++++-- .../src/main/resources/application.properties | 1 + 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java index d6d137e50..7d001b90f 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java @@ -71,6 +71,11 @@ import org.springframework.web.util.UrlPathHelper; import javax.sql.DataSource; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.Optional; @SpringBootConfiguration @@ -277,4 +282,13 @@ public IBeaconDataService getBeaconDataService(@Value("${shibui.beacon.productNa BeaconDataServiceImpl result = new BeaconDataServiceImpl(productName, info, tierVersion, entityDescriptorRepository, metadataResolverRepository, filterRepository, groupsRepository, roleRepository, beaconConfigurationRepository, userService); return result; } + + @Bean + public List beaconEndpointUrl(@Value("${shibui.beacon.url}") String urls) throws MalformedURLException { + List result = new ArrayList<>(); + for (String url : Arrays.asList(urls.split(","))) { + result.add(new URL(url)); + } + return result; + } } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/BeaconController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/BeaconController.java index f5ece2032..e41f17f39 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/BeaconController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/BeaconController.java @@ -1,11 +1,13 @@ package edu.internet2.tier.shibboleth.admin.ui.controller; import com.fasterxml.jackson.core.JsonProcessingException; +import edu.internet2.tier.shibboleth.admin.ui.scheduled.BeaconReportingTask; import edu.internet2.tier.shibboleth.admin.ui.service.IBeaconDataService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Profile; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -13,13 +15,20 @@ @RestController @RequestMapping(value = "/api/beacon") public class BeaconController { + @Autowired + BeaconReportingTask beaconReporter; @Autowired private IBeaconDataService service; - @GetMapping(value = "/detail") public ResponseEntity getDetail() throws JsonProcessingException { return ResponseEntity.ok(service.getBeaconData()); } + + @PostMapping("/send") + public ResponseEntity forceSendBeaconData() { + beaconReporter.sendBeaconData(); + return ResponseEntity.ok("Manual push of beacon data completed"); + } } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/scheduled/BeaconReportingTask.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/scheduled/BeaconReportingTask.java index c87d7017c..5c25c8603 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/scheduled/BeaconReportingTask.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/scheduled/BeaconReportingTask.java @@ -4,11 +4,18 @@ import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock; import net.javacrumbs.shedlock.spring.annotation.SchedulerLock; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.transaction.annotation.Transactional; +import java.io.IOException; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.List; + @Configuration @ConditionalOnProperty(name = "shibui.beacon.enabled", matchIfMissing=true) @EnableSchedulerLock(defaultLockAtMostFor = "${shibui.maxTask.lockTime:30m}") @@ -16,10 +23,29 @@ public class BeaconReportingTask { @Autowired IBeaconDataService dataService; + @Autowired + @Qualifier("beaconEndpointUrl") + private List endpointUrls; + @Scheduled(cron="#{@getBeaconCronValue}") - @SchedulerLock(name = "generateEntityDescriptorFiles") + @SchedulerLock(name = "sendBeaconData") @Transactional(readOnly = true) - public void generateEntityDescriptorFiles() { + public void sendBeaconData() { + endpointUrls.forEach(url -> { + try { + HttpURLConnection con = (HttpURLConnection) url.openConnection(); + con.setRequestMethod("POST"); + con.setRequestProperty("Content-Type", "application/json; utf-8"); + con.setRequestProperty("Accept", "application/json"); + con.setDoOutput(true); + try(OutputStream os = con.getOutputStream()){ + byte[] input = dataService.getBeaconData().getBytes("utf-8"); + os.write(input, 0, input.length); + } + } catch (IOException e) { + e.printStackTrace(); + } + }); } } \ No newline at end of file diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index ac76ebb36..3c4d83008 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -124,6 +124,7 @@ shibui.pac4j-enabled=false shibui.beacon.enabled=true shibui.beacon.productName=ShibUi shibui.beacon.installationID=UNICON-SHIBUI-TESTING +shibui.beacon.url=http://collector.testbed.tier.internet2.edu:5001 ### Swagger/Springdoc patterns springdoc.use-management-port=true From bd1265078f652e6855e46d49c3edcb9be24619ce Mon Sep 17 00:00:00 2001 From: chasegawa Date: Tue, 30 May 2023 11:19:08 -0700 Subject: [PATCH 06/12] SHIBUI-2571 Added database to output --- .../ui/configuration/CoreShibUiConfiguration.java | 6 ++++-- .../admin/ui/service/BeaconDataServiceImpl.java | 13 ++++++++++--- .../admin/ui/{ => service}/beacon/BeaconDetail.java | 2 +- .../admin/ui/{ => service}/beacon/ShibuiDetail.java | 3 ++- 4 files changed, 17 insertions(+), 7 deletions(-) rename backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/{ => service}/beacon/BeaconDetail.java (86%) rename backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/{ => service}/beacon/ShibuiDetail.java (82%) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java index 7d001b90f..4781fe0c0 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java @@ -52,6 +52,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.info.InfoEndpoint; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -278,8 +279,9 @@ public String getBeaconCronValue(BeaconConfigurationRepository repo) @Bean public IBeaconDataService getBeaconDataService(@Value("${shibui.beacon.productName:ShibUi}") String productName, InfoEndpoint info, @Value("#{environment.TIERVERSION}") String tierVersion, EntityDescriptorRepository entityDescriptorRepository, MetadataResolverRepository metadataResolverRepository, FilterRepository filterRepository, - GroupsRepository groupsRepository, RoleRepository roleRepository, BeaconConfigurationRepository beaconConfigurationRepository, UserService userService) { - BeaconDataServiceImpl result = new BeaconDataServiceImpl(productName, info, tierVersion, entityDescriptorRepository, metadataResolverRepository, filterRepository, groupsRepository, roleRepository, beaconConfigurationRepository, userService); + GroupsRepository groupsRepository, RoleRepository roleRepository, BeaconConfigurationRepository beaconConfigurationRepository, + UserService userService, HealthEndpoint healthEndpoint) { + BeaconDataServiceImpl result = new BeaconDataServiceImpl(productName, info, tierVersion, entityDescriptorRepository, metadataResolverRepository, filterRepository, groupsRepository, roleRepository, beaconConfigurationRepository, userService, healthEndpoint); return result; } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/BeaconDataServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/BeaconDataServiceImpl.java index f1b4a7e8a..0047275d7 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/BeaconDataServiceImpl.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/BeaconDataServiceImpl.java @@ -2,8 +2,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; -import edu.internet2.tier.shibboleth.admin.ui.beacon.BeaconDetail; -import edu.internet2.tier.shibboleth.admin.ui.beacon.ShibuiDetail; +import edu.internet2.tier.shibboleth.admin.ui.service.beacon.BeaconDetail; +import edu.internet2.tier.shibboleth.admin.ui.service.beacon.ShibuiDetail; import edu.internet2.tier.shibboleth.admin.ui.domain.BeaconConfiguration; import edu.internet2.tier.shibboleth.admin.ui.repository.BeaconConfigurationRepository; import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository; @@ -14,9 +14,11 @@ import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService; import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.info.InfoEndpoint; import java.util.Arrays; +import java.util.HashMap; import java.util.Map; public class BeaconDataServiceImpl implements IBeaconDataService { @@ -24,6 +26,7 @@ public class BeaconDataServiceImpl implements IBeaconDataService { private String productName; private String tierVersion; private String version; + private String db; private EntityDescriptorRepository entityDescriptorRepository; private MetadataResolverRepository metadataResolverRepository; @@ -35,12 +38,15 @@ public class BeaconDataServiceImpl implements IBeaconDataService { public BeaconDataServiceImpl(String productName, InfoEndpoint info, String tierVersion, EntityDescriptorRepository entityDescriptorRepository, MetadataResolverRepository metadataResolverRepository, FilterRepository filterRepository, GroupsRepository groupsRepository, - RoleRepository roleRepository, BeaconConfigurationRepository beaconConfigurationRepository, UserService userService) { + RoleRepository roleRepository, BeaconConfigurationRepository beaconConfigurationRepository, UserService userService, + HealthEndpoint health) { mapper = new ObjectMapper(); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // skip any null values this.productName = productName; this.version = info.info().get("build") == null ? "unknown" : ((Map)info.info().get("build")).get("version").toString(); + this.db = StringUtils.substringBetween(health.healthForPath("db").toString(), "database=", ","); + this.tierVersion = StringUtils.isBlank(tierVersion) ? "NA" : tierVersion; this.entityDescriptorRepository = entityDescriptorRepository; @@ -72,6 +78,7 @@ private ShibuiDetail getShibuiDetailData() { .dailyLogins(userService.getDailyLoginCount()) .numberOfGroups((int) groupsRepository.count()) .numberOfRoles((int) roleRepository.count()) + .db(this.db) .build(); return detail; } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/BeaconDetail.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/beacon/BeaconDetail.java similarity index 86% rename from backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/BeaconDetail.java rename to backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/beacon/BeaconDetail.java index ac690b1cb..bef94540f 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/BeaconDetail.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/beacon/BeaconDetail.java @@ -1,4 +1,4 @@ -package edu.internet2.tier.shibboleth.admin.ui.beacon; +package edu.internet2.tier.shibboleth.admin.ui.service.beacon; import lombok.Data; import lombok.experimental.Accessors; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/ShibuiDetail.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/beacon/ShibuiDetail.java similarity index 82% rename from backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/ShibuiDetail.java rename to backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/beacon/ShibuiDetail.java index ec89e5f00..6bab7ef96 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/beacon/ShibuiDetail.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/beacon/ShibuiDetail.java @@ -1,4 +1,4 @@ -package edu.internet2.tier.shibboleth.admin.ui.beacon; +package edu.internet2.tier.shibboleth.admin.ui.service.beacon; import lombok.Builder; import lombok.Data; @@ -9,6 +9,7 @@ @Builder public class ShibuiDetail { private List authMechanisms; + private String db; private int numberOfMetadataSources; private int numberOfMetadataProviders; private int numberOfFilters; From da14d5b7606785119c7f5d8aea44e18776d60e86 Mon Sep 17 00:00:00 2001 From: chasegawa Date: Tue, 30 May 2023 14:35:31 -0700 Subject: [PATCH 07/12] SHIBUI-2571 Changed output for logins --- .../ui/security/model/UserLoginRecord.java | 8 +--- .../repository/UserLoginRecordRepository.java | 5 ++- .../ui/security/service/UserService.java | 22 ++++++---- .../ui/service/BeaconDataServiceImpl.java | 1 + .../admin/ui/service/beacon/ShibuiDetail.java | 1 + .../ui/configuration/TestConfiguration.groovy | 24 ++++++++++ .../security/service/UserServiceTests.groovy | 42 ++++++++++-------- delete.json | 16 ------- .../unicon/shibui/pac4j/AddNewUserFilter.java | 1 - .../ShibuiSAML2Authenticator.java | 1 + .../shibui/pac4j/AddNewUserFilterTests.groovy | 44 ------------------- testbed/authentication/shibui/application.yml | 2 +- 12 files changed, 71 insertions(+), 96 deletions(-) delete mode 100644 delete.json diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/UserLoginRecord.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/UserLoginRecord.java index 75e5fa728..7ed12a93e 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/UserLoginRecord.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/UserLoginRecord.java @@ -11,7 +11,6 @@ @Data @Entity -@Table(uniqueConstraints = { @UniqueConstraint(columnNames = { "username", "login" }) }) public class UserLoginRecord { @Id @GeneratedValue @@ -19,16 +18,13 @@ public class UserLoginRecord { private String username; - private String login; - private Date loginDate; public UserLoginRecord() { } - public UserLoginRecord(String username, Date loginDate, String formattedDate) { + public UserLoginRecord(String username) { this.username = username; - this.login = formattedDate; - this.loginDate = loginDate; + this.loginDate = new Date(); } } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/UserLoginRecordRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/UserLoginRecordRepository.java index 22a97fabb..db583b786 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/UserLoginRecordRepository.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/repository/UserLoginRecordRepository.java @@ -9,8 +9,11 @@ import java.util.Optional; public interface UserLoginRecordRepository extends JpaRepository { - Optional findByUsernameAndLogin(String username, String formattedDate); + Optional findTopByUsername(String username); @Query(value = "SELECT count(*) FROM UserLoginRecord ulr WHERE ulr.loginDate >= :sinceDate") int countLoginsSince(@Param("sinceDate") Date sinceDate); + + @Query(value = "SELECT count(DISTINCT ulr.username) FROM UserLoginRecord ulr WHERE ulr.loginDate >= :sinceDate") + int countUniqueUserLoginsSince(@Param("sinceDate") Date sinceDate); } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java index e2a45b12f..735ce0614 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java @@ -251,21 +251,27 @@ public boolean currentUserCanEnable() { } /** - * Ensure there exists a login record for this username and today's date + * Ensure there exists a login record for this username and current time * @param username */ public void updateLoginRecord(String username) { - Date current = new Date(); - String formattedDate = DATE_FORMAT.format(current); - - if (userLoginRecordRepository.findByUsernameAndLogin(username,formattedDate).isEmpty()) { - UserLoginRecord ulr = new UserLoginRecord(username, current, formattedDate); - userLoginRecordRepository.saveAndFlush(ulr); - } + UserLoginRecord ulr = new UserLoginRecord(username); + userLoginRecordRepository.saveAndFlush(ulr); } + /** + * @return count of all logins in the last 24 hours + */ public int getDailyLoginCount() { Date since = DateUtils.addDays(new Date(), -1); return userLoginRecordRepository.countLoginsSince(since); } + + /** + * @return count of unique users logged in during the last 24 hours + */ + public int getDailyUniqueUserLogins() { + Date since = DateUtils.addDays(new Date(), -1); + return userLoginRecordRepository.countUniqueUserLoginsSince(since); + } } \ No newline at end of file diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/BeaconDataServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/BeaconDataServiceImpl.java index 0047275d7..97cb5f06d 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/BeaconDataServiceImpl.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/BeaconDataServiceImpl.java @@ -76,6 +76,7 @@ private ShibuiDetail getShibuiDetailData() { .numberOfMetadataProviders(metadataResolverRepository.getActiveMetadataProviderCount()) .numberOfFilters(filterRepository.getActiveFilterCount()) .dailyLogins(userService.getDailyLoginCount()) + .dailyUniqueUserLogins(userService.getDailyUniqueUserLogins()) .numberOfGroups((int) groupsRepository.count()) .numberOfRoles((int) roleRepository.count()) .db(this.db) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/beacon/ShibuiDetail.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/beacon/ShibuiDetail.java index 6bab7ef96..8d3b01330 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/beacon/ShibuiDetail.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/beacon/ShibuiDetail.java @@ -14,6 +14,7 @@ public class ShibuiDetail { private int numberOfMetadataProviders; private int numberOfFilters; private int dailyLogins; + private int dailyUniqueUserLogins; private int numberOfGroups; private int numberOfRoles; private String installationID; diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy index a04043631..d756032e8 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy @@ -24,6 +24,14 @@ import org.opensaml.saml.metadata.resolver.impl.ResourceBackedMetadataResolver import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.actuate.endpoint.annotation.Selector +import org.springframework.boot.actuate.health.DefaultHealthContributorRegistry +import org.springframework.boot.actuate.health.Health +import org.springframework.boot.actuate.health.HealthComponent +import org.springframework.boot.actuate.health.HealthEndpoint +import org.springframework.boot.actuate.health.HealthEndpointGroup +import org.springframework.boot.actuate.health.HealthEndpointGroups +import org.springframework.boot.actuate.health.Status import org.springframework.boot.actuate.info.InfoContributor import org.springframework.boot.actuate.info.InfoEndpoint import org.springframework.boot.SpringBootConfiguration @@ -141,4 +149,20 @@ class TestConfiguration { public InfoEndpoint getInfoEndpoint() { return new InfoEndpoint(new ArrayList()); } + + @Bean + public HealthEndpoint getHealthEndpoint() { + return new HealthEndpoint(new DefaultHealthContributorRegistry(), new HealthEndpointGroups() { + @Override HealthEndpointGroup getPrimary() { return null } + + @Override Set getNames() { return null } + + @Override HealthEndpointGroup get(String name) { return null } + }) { + @Override + HealthComponent healthForPath(@Selector(match = Selector.Match.ALL_REMAINING) String... path) { + return new Health(new Status(""), new HashMap()); + } + } + } } \ No newline at end of file diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/UserServiceTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/UserServiceTests.groovy index 5b7b9feb0..df02e801c 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/UserServiceTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/security/service/UserServiceTests.groovy @@ -171,47 +171,51 @@ class UserServiceTests extends AbstractBaseDataJpaTest { def "when user login - ensure record is created"() { given: - SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd-MM-yyyy") - - Date current = new Date(); - String formattedDate = DATE_FORMAT.format(current) - UserLoginRecord ulr = new UserLoginRecord("username", current, formattedDate) + def sinceDate = DateUtils.addDays(new Date(), -1); + UserLoginRecord ulr = new UserLoginRecord("username") expect: - userLoginRecordRepository.findByUsernameAndLogin("username", formattedDate).isEmpty() + userLoginRecordRepository.findTopByUsername("username").isEmpty() userLoginRecordRepository.count() == 0 when: userService.updateLoginRecord("username") then: - userLoginRecordRepository.findByUsernameAndLogin("username", formattedDate).isPresent() + userLoginRecordRepository.findTopByUsername("username").isPresent() userLoginRecordRepository.count() == 1 + userLoginRecordRepository.countLoginsSince(sinceDate) == 1 + userLoginRecordRepository.countUniqueUserLoginsSince(sinceDate) == 1 - when: 'try adding again should not change result' + when: 'repeat login change results appropriately' userService.updateLoginRecord("username") then: - userLoginRecordRepository.findByUsernameAndLogin("username", formattedDate).isPresent() - userLoginRecordRepository.count() == 1 - userService.getDailyLoginCount() == 1 + userLoginRecordRepository.findTopByUsername("username").isPresent() + userLoginRecordRepository.count() == 2 + userLoginRecordRepository.countLoginsSince(sinceDate) == 2 + userLoginRecordRepository.countUniqueUserLoginsSince(sinceDate) == 1 - when: + when: 'new login' userService.updateLoginRecord("username2") then: - userLoginRecordRepository.findByUsernameAndLogin("username2", formattedDate).isPresent() - userLoginRecordRepository.count() == 2 - userService.getDailyLoginCount() == 2 + userLoginRecordRepository.findTopByUsername("username2").isPresent() + userLoginRecordRepository.count() == 3 + userLoginRecordRepository.countLoginsSince(sinceDate) == 3 + userLoginRecordRepository.countUniqueUserLoginsSince(sinceDate) == 2 when: 'older logins in db, should be same counts' Date older = DateUtils.addDays(new Date(), -3) - String formattedDate2 = DATE_FORMAT.format(older) - UserLoginRecord ulr2 = new UserLoginRecord("username", older, formattedDate2) + UserLoginRecord ulr2 = new UserLoginRecord("username").with { + it.loginDate = older + it + } userLoginRecordRepository.save(ulr2) then: - userLoginRecordRepository.count() == 3 - userService.getDailyLoginCount() == 2 + userLoginRecordRepository.count() == 4 + userLoginRecordRepository.countLoginsSince(sinceDate) == 3 + userLoginRecordRepository.countUniqueUserLoginsSince(sinceDate) == 2 } } \ No newline at end of file diff --git a/delete.json b/delete.json deleted file mode 100644 index 93b3d2534..000000000 --- a/delete.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "git": { - "branch": "feature/shibui-2571", - "commit": { - "id": "b547925", - "time": "2023-05-23T20:32:38Z" - } - }, - "build": { - "artifact": "shibui", - "name": "backend", - "time": "2023-05-24T17:15:28.585Z", - "version": "1.18.0-SNAPSHOT", - "group": "edu.internet2.tier.shibboleth.admin.ui" - } -} \ No newline at end of file diff --git a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/AddNewUserFilter.java b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/AddNewUserFilter.java index d9365ac06..fb757313f 100644 --- a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/AddNewUserFilter.java +++ b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/AddNewUserFilter.java @@ -136,7 +136,6 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha ((HttpServletResponse) response).sendRedirect("/unsecured/error.html"); } else { // User exists or has been created and has a role so we can continue on. - userService.updateLoginRecord(username); chain.doFilter(request, response); // else, user is in the system already, carry on } } diff --git a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/authenticator/ShibuiSAML2Authenticator.java b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/authenticator/ShibuiSAML2Authenticator.java index f2d2738e0..110611f2d 100644 --- a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/authenticator/ShibuiSAML2Authenticator.java +++ b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/authenticator/ShibuiSAML2Authenticator.java @@ -28,5 +28,6 @@ public void validate(final Credentials credentials, final WebContext context, fi CommonProfile profile = (CommonProfile) credentials.getUserProfile(); profile.setRoles(userService.getUserRoles(profile.getUsername())); credentials.setUserProfile(profile); + userService.updateLoginRecord(profile.getUsername()); } } \ No newline at end of file diff --git a/pac4j-module/src/test/groovy/net/unicon/shibui/pac4j/AddNewUserFilterTests.groovy b/pac4j-module/src/test/groovy/net/unicon/shibui/pac4j/AddNewUserFilterTests.groovy index 2097ccaa1..216ccdafe 100644 --- a/pac4j-module/src/test/groovy/net/unicon/shibui/pac4j/AddNewUserFilterTests.groovy +++ b/pac4j-module/src/test/groovy/net/unicon/shibui/pac4j/AddNewUserFilterTests.groovy @@ -1,11 +1,9 @@ package net.unicon.shibui.pac4j -import edu.internet2.tier.shibboleth.admin.ui.security.model.Group import edu.internet2.tier.shibboleth.admin.ui.security.model.Role import edu.internet2.tier.shibboleth.admin.ui.security.model.User import edu.internet2.tier.shibboleth.admin.ui.security.repository.OwnershipRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository -import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserLoginRecordRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository import edu.internet2.tier.shibboleth.admin.ui.security.service.GroupServiceForTesting import edu.internet2.tier.shibboleth.admin.ui.security.service.IRolesService @@ -30,7 +28,6 @@ import spock.lang.Subject import javax.servlet.FilterChain import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse -import java.text.SimpleDateFormat @DataJpaTest @ContextConfiguration(classes=[Pac4JTestingConfig]) @@ -57,9 +54,6 @@ class AddNewUserFilterTests extends Specification { @Autowired Pac4jConfigurationProperties pac4jConfigurationProperties - @Autowired - UserLoginRecordRepository userLoginRecordRepository - @Autowired UserRepository userRepository @@ -107,44 +101,6 @@ class AddNewUserFilterTests extends Specification { roles.each { roleRepository.save(it) } - - Group gb = new Group() - gb.setResourceId("testingGroupBBB") - gb.setName("Group BBB") - gb.setValidationRegex("^(?:https?:\\/\\/)?(?:[^.]+\\.)?shib\\.org(\\/.*)?\$") - gb = groupService.createGroup(gb) - - Optional userRole = roleRepository.findByName("ROLE_USER") - User user = new User(username: "someUser", roles:[userRole.get()], password: "foo") - user.setGroup(gb) - userService.save(user) - } - - def "user logged in"() { - given: - ['Username': 'someUser', - 'FirstName': 'Some', - 'LastName': 'User', - 'Email': 'someUser@institution.edu'].each { key, value -> - saml2Profile.getAttribute(profileMapping."get${key}"()) >> [value] - } - saml2Profile.getUsername() >> "someUser" - - SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd-MM-yyyy") - - Date current = new Date(); - String formattedDate = DATE_FORMAT.format(current) - - expect: - userLoginRecordRepository.findByUsernameAndLogin("someUser", formattedDate).isEmpty() - userLoginRecordRepository.count() == 0 - - when: - addNewUserFilter.doFilter(request, response, chain) - - then: - userLoginRecordRepository.findByUsernameAndLogin("someUser", formattedDate).isPresent() - userLoginRecordRepository.count() == 1 } def "new user created"() { diff --git a/testbed/authentication/shibui/application.yml b/testbed/authentication/shibui/application.yml index 942d3aaaf..fb64e02d0 100644 --- a/testbed/authentication/shibui/application.yml +++ b/testbed/authentication/shibui/application.yml @@ -3,7 +3,7 @@ server: forward-headers-strategy: NATIVE spring: profiles: - include: + include: dev shibui: user-bootstrap-resource: file:/conf/users.csv roles: ROLE_ADMIN,ROLE_NONE,ROLE_USER,ROLE_ENABLE,ROLE_PONY From 2371618af9985b26dcab87a4b1f32a901dc8d308 Mon Sep 17 00:00:00 2001 From: chasegawa Date: Wed, 31 May 2023 14:29:32 -0700 Subject: [PATCH 08/12] SHIBUI-2578/2579 Fixes for issues found by QA --- .../admin/ui/security/springsecurity/AdminUserService.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java index c7da5f668..02a468848 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/springsecurity/AdminUserService.java @@ -28,7 +28,6 @@ public class AdminUserService implements UserDetailsService { private final UserService userService; @Override - @Transactional(readOnly = true) public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userService .findByUsername(username) @@ -44,7 +43,6 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx throw new UsernameNotFoundException(String.format("No roles are defined for user [%s]", username)); } - userService.updateLoginRecord(username); return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities); } } \ No newline at end of file From d36b4d4cb9195d07d51cba7863ee7e6f4c9a63c9 Mon Sep 17 00:00:00 2001 From: chasegawa Date: Wed, 31 May 2023 15:59:13 -0700 Subject: [PATCH 09/12] SHIBUI-2578/2579 Removed old beacon code that is unused --- beacon/core/build.gradle | 42 --------- .../java/edu/internet2/tap/beacon/Beacon.java | 22 ----- .../internet2/tap/beacon/BeaconPublisher.java | 10 --- .../tap/beacon/DefaultBeaconPublisher.java | 90 ------------------- .../beacon/DefaultBeaconPublisherTests.groovy | 45 ---------- beacon/gradle.properties | 2 - beacon/spring/build.gradle | 38 -------- .../BeaconPublishingConfiguration.java | 61 ------------- .../BeaconEnvironmentVariablesCondition.java | 48 ---------- ...alOnBeaconEnvironmentVariablesPresent.java | 21 ----- .../main/resources/META-INF/spring.factories | 1 - 11 files changed, 380 deletions(-) delete mode 100644 beacon/core/build.gradle delete mode 100644 beacon/core/src/main/java/edu/internet2/tap/beacon/Beacon.java delete mode 100644 beacon/core/src/main/java/edu/internet2/tap/beacon/BeaconPublisher.java delete mode 100644 beacon/core/src/main/java/edu/internet2/tap/beacon/DefaultBeaconPublisher.java delete mode 100644 beacon/core/src/test/groovy/edu/internet2/tap/beacon/DefaultBeaconPublisherTests.groovy delete mode 100644 beacon/gradle.properties delete mode 100644 beacon/spring/build.gradle delete mode 100644 beacon/spring/src/main/java/edu/internet2/tap/beacon/configuration/BeaconPublishingConfiguration.java delete mode 100644 beacon/spring/src/main/java/edu/internet2/tap/beacon/configuration/condition/BeaconEnvironmentVariablesCondition.java delete mode 100644 beacon/spring/src/main/java/edu/internet2/tap/beacon/configuration/condition/ConditionalOnBeaconEnvironmentVariablesPresent.java delete mode 100644 beacon/spring/src/main/resources/META-INF/spring.factories diff --git a/beacon/core/build.gradle b/beacon/core/build.gradle deleted file mode 100644 index 6c2705605..000000000 --- a/beacon/core/build.gradle +++ /dev/null @@ -1,42 +0,0 @@ -import org.springframework.boot.gradle.plugin.SpringBootPlugin - -plugins { - id 'org.springframework.boot' - id 'io.spring.dependency-management' version '1.0.6.RELEASE' - id 'groovy' -} - -sourceCompatibility = 17 -targetCompatibility = 17 - -bootJar.enabled = false - -repositories { - jcenter() - maven { // for the springboot plugin - url "https://plugins.gradle.org/m2/" - } -} - -configurations { - compile - testCompile -} - -dependencyManagement { - imports { - mavenBom SpringBootPlugin.BOM_COORDINATES - } -} - -dependencies { - testCompile "org.springframework.boot:spring-boot-starter-test:${project.'springbootVersion'}" - implementation "org.apache.groovy:groovy-all:${project.'groovyVersion'}" - testImplementation platform("org.spockframework:spock-bom:2.3-groovy-4.0") - testImplementation "org.spockframework:spock-core" - testImplementation "org.spockframework:spock-spring" -} - -jar { - archiveName = "beacon-core-${version}.jar" -} \ No newline at end of file diff --git a/beacon/core/src/main/java/edu/internet2/tap/beacon/Beacon.java b/beacon/core/src/main/java/edu/internet2/tap/beacon/Beacon.java deleted file mode 100644 index 7e7dec5a5..000000000 --- a/beacon/core/src/main/java/edu/internet2/tap/beacon/Beacon.java +++ /dev/null @@ -1,22 +0,0 @@ -package edu.internet2.tap.beacon; - -/** - * Exposes expected names of environment variables holding beacon config data. - */ -public final class Beacon { - - private Beacon() { - } - - public static final String LOG_HOST = "LOGHOST"; - - public static final String LOG_PORT = "LOGPORT"; - - public static final String IMAGE = "IMAGE"; - - public static final String VERSION = "VERSION"; - - public static final String TIERVERSION = "TIERVERSION"; - - public static final String MAINTAINER = "MAINTAINER"; -} diff --git a/beacon/core/src/main/java/edu/internet2/tap/beacon/BeaconPublisher.java b/beacon/core/src/main/java/edu/internet2/tap/beacon/BeaconPublisher.java deleted file mode 100644 index 80f4f77e4..000000000 --- a/beacon/core/src/main/java/edu/internet2/tap/beacon/BeaconPublisher.java +++ /dev/null @@ -1,10 +0,0 @@ -package edu.internet2.tap.beacon; - -/** - * Simple SPI allowing implementations to publish to beacon service utilizing Runnable API - * so that publishing code could run in separate threads of execution. - * - * @author Dmitriy Kopylenko - */ -public interface BeaconPublisher extends Runnable { -} diff --git a/beacon/core/src/main/java/edu/internet2/tap/beacon/DefaultBeaconPublisher.java b/beacon/core/src/main/java/edu/internet2/tap/beacon/DefaultBeaconPublisher.java deleted file mode 100644 index eb077ddc9..000000000 --- a/beacon/core/src/main/java/edu/internet2/tap/beacon/DefaultBeaconPublisher.java +++ /dev/null @@ -1,90 +0,0 @@ -package edu.internet2.tap.beacon; - -import java.io.IOException; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Collectors; - -import static edu.internet2.tap.beacon.Beacon.IMAGE; -import static edu.internet2.tap.beacon.Beacon.LOG_HOST; -import static edu.internet2.tap.beacon.Beacon.LOG_PORT; -import static edu.internet2.tap.beacon.Beacon.MAINTAINER; -import static edu.internet2.tap.beacon.Beacon.TIERVERSION; -import static edu.internet2.tap.beacon.Beacon.VERSION; - -/** - * Default implementation that knows the details about payload structure with its data and beacon endpoint details - * gathered by upstream components and passed to this implementation at object construction site. - * - * @author Dmitriy Kopylenko - */ -public class DefaultBeaconPublisher implements BeaconPublisher { - - private final URL endpointUrl; - - private final String jsonPayload; - - public DefaultBeaconPublisher(Map beaconDetails) { - //Do data validation checks here. If any of the necessary beacon data not available here, throw a Runtime exception - if (beaconDetails == null) { - throw new IllegalArgumentException("beaconDetails Map must not be null"); - } - if (beaconDetails.get(LOG_HOST) == null - || beaconDetails.get(LOG_PORT) == null - || beaconDetails.get(IMAGE) == null - || beaconDetails.get(VERSION) == null - || beaconDetails.get(TIERVERSION) == null - || beaconDetails.get(MAINTAINER) == null) { - throw new IllegalArgumentException("Not all the necessary beacon data is available to be able to publish to beacon"); - } - try { - this.endpointUrl = new URL(String.format("http://%s:%s", beaconDetails.get(LOG_HOST), beaconDetails.get(LOG_PORT))); - } catch (MalformedURLException ex) { - throw new IllegalArgumentException(ex.getMessage()); - } - - Map dataToPublish = new HashMap<>(); - dataToPublish.put("msgType", "TIERBEACON"); - dataToPublish.put("msgName", "TIER"); - dataToPublish.put("msgVersion", "1.0"); - dataToPublish.put("tbProduct", beaconDetails.get(IMAGE)); - dataToPublish.put("tbProductVersion", beaconDetails.get(VERSION)); - dataToPublish.put("tbTIERRelease", beaconDetails.get(TIERVERSION)); - dataToPublish.put("tbMaintainer", beaconDetails.get(MAINTAINER)); - - //Create JSON payload without any 3-rd party library - this.jsonPayload = "{" + dataToPublish.entrySet().stream() - .map(e -> "\"" + e.getKey() + "\"" + ":\"" + e.getValue() + "\"") - .collect(Collectors.joining(", ")) + "}"; - } - - @Override - public void run() { - try { - HttpURLConnection con = (HttpURLConnection) this.endpointUrl.openConnection(); - con.setRequestMethod("POST"); - con.setRequestProperty("Content-Type", "application/json; utf-8"); - con.setRequestProperty("Accept", "application/json"); - con.setDoOutput(true); - try(OutputStream os = con.getOutputStream()){ - byte[] input = jsonPayload.getBytes("utf-8"); - os.write(input, 0, input.length); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - - //getters used in unit tests and calling components for debugging purposes - public String getEndpointUri() { - return endpointUrl.toString(); - } - - public String getJsonPayload() { - return jsonPayload; - } -} diff --git a/beacon/core/src/test/groovy/edu/internet2/tap/beacon/DefaultBeaconPublisherTests.groovy b/beacon/core/src/test/groovy/edu/internet2/tap/beacon/DefaultBeaconPublisherTests.groovy deleted file mode 100644 index d3004ef13..000000000 --- a/beacon/core/src/test/groovy/edu/internet2/tap/beacon/DefaultBeaconPublisherTests.groovy +++ /dev/null @@ -1,45 +0,0 @@ -package edu.internet2.tap.beacon - -import spock.lang.Specification -import sun.security.x509.OtherName - -class DefaultBeaconPublisherTests extends Specification { - - def "DefaultBeaconPublisher invariants are enforced during object creation - null Map is passed"() { - when: - new DefaultBeaconPublisher(null) - - then: - thrown IllegalArgumentException - - } - - def "DefaultBeaconPublisher invariants are enforced during object creation - empty Map is passed"() { - when: - new DefaultBeaconPublisher([:]) - - then: - thrown IllegalArgumentException - } - - def "DefaultBeaconPublisher invariants are enforced during object creation - valid Beacon data Map is passed"() { - when: - def expectedJsonPaylaod = """{"msgType":"TIERBEACON", "tbMaintainer":"unittest_maintainer", "msgName":"TIER", "tbProduct":"image", "msgVersion":"1.0", "tbProductVersion":"v1", "tbTIERRelease":"tv1"}""" - - def configuredBeaconData = [LOGHOST : 'collector.testbed.tier.internet2.edu', - LOGPORT : '5001', - IMAGE : 'image', - VERSION : 'v1', - TIERVERSION: 'tv1', - MAINTAINER : 'unittest_maintainer'] - def p = new DefaultBeaconPublisher(configuredBeaconData) - println p.jsonPayload - - then: - noExceptionThrown() - p.endpointUri == 'http://collector.testbed.tier.internet2.edu:5001' - p.jsonPayload == expectedJsonPaylaod - - } - -} diff --git a/beacon/gradle.properties b/beacon/gradle.properties deleted file mode 100644 index 23dbf7481..000000000 --- a/beacon/gradle.properties +++ /dev/null @@ -1,2 +0,0 @@ -group=edu.internet2.tap.beacon -version=1.0.0-SNAPSHOT diff --git a/beacon/spring/build.gradle b/beacon/spring/build.gradle deleted file mode 100644 index 4e7d46206..000000000 --- a/beacon/spring/build.gradle +++ /dev/null @@ -1,38 +0,0 @@ -import org.springframework.boot.gradle.plugin.SpringBootPlugin - -plugins { - id 'org.springframework.boot' - id 'io.spring.dependency-management' version '1.0.6.RELEASE' -} - -apply plugin: 'java' -sourceCompatibility = 17 -targetCompatibility = 17 - -bootJar.enabled = false - -repositories { - jcenter() - maven { // for the springboot plugin - url "https://plugins.gradle.org/m2/" - } -} - -configurations { - compile -} - -jar { - archiveName = "beacon-spring-${version}.jar" -} - -dependencyManagement { - imports { - mavenBom SpringBootPlugin.BOM_COORDINATES - } -} - -dependencies { - implementation project(':beacon:core') - implementation "org.springframework.boot:spring-boot-starter" -} \ No newline at end of file diff --git a/beacon/spring/src/main/java/edu/internet2/tap/beacon/configuration/BeaconPublishingConfiguration.java b/beacon/spring/src/main/java/edu/internet2/tap/beacon/configuration/BeaconPublishingConfiguration.java deleted file mode 100644 index fc8264794..000000000 --- a/beacon/spring/src/main/java/edu/internet2/tap/beacon/configuration/BeaconPublishingConfiguration.java +++ /dev/null @@ -1,61 +0,0 @@ -package edu.internet2.tap.beacon.configuration; - -import edu.internet2.tap.beacon.DefaultBeaconPublisher; -import edu.internet2.tap.beacon.configuration.condition.ConditionalOnBeaconEnvironmentVariablesPresent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.SpringBootConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.core.env.Environment; -import org.springframework.scheduling.annotation.Async; -import org.springframework.scheduling.annotation.Scheduled; - -import java.util.HashMap; -import java.util.Map; - -import static edu.internet2.tap.beacon.Beacon.IMAGE; -import static edu.internet2.tap.beacon.Beacon.LOG_HOST; -import static edu.internet2.tap.beacon.Beacon.LOG_PORT; -import static edu.internet2.tap.beacon.Beacon.MAINTAINER; -import static edu.internet2.tap.beacon.Beacon.TIERVERSION; -import static edu.internet2.tap.beacon.Beacon.VERSION; - -@SpringBootConfiguration -@ConditionalOnProperty(name = "shibui.beacon-enabled", havingValue = "true") -public class BeaconPublishingConfiguration { - - private static final Logger logger = LoggerFactory.getLogger(BeaconPublishingConfiguration.class); - - @Bean - @ConditionalOnBeaconEnvironmentVariablesPresent - public BeaconPublishingTask beaconPublisher(Environment env) { - logger.debug("Creating BeaconPublishingTask..."); - Map beaconData = new HashMap<>(); - beaconData.put(LOG_HOST, env.getProperty(LOG_HOST)); - beaconData.put(LOG_PORT, env.getProperty(LOG_PORT)); - beaconData.put(IMAGE, env.getProperty(IMAGE)); - beaconData.put(VERSION, env.getProperty(VERSION)); - beaconData.put(TIERVERSION, env.getProperty(TIERVERSION)); - beaconData.put(MAINTAINER, env.getProperty(MAINTAINER)); - return new BeaconPublishingTask(new DefaultBeaconPublisher(beaconData)); - } - - public static class BeaconPublishingTask { - private DefaultBeaconPublisher beaconPublisher; - - public BeaconPublishingTask(DefaultBeaconPublisher beaconPublisher) { - this.beaconPublisher = beaconPublisher; - } - - //Cron is based on the spec defined here: https://spaces.at.internet2.edu/display/TWGH/TIER+Instrumentation+-+The+TIER+Beacon - @Scheduled(cron = "0 ${random.int[0,59]} ${random.int[0,3]} ? * *") - @Async - void publish() { - logger.debug("Publishing payload: {} to beacon endpoint: {}", - beaconPublisher.getJsonPayload(), - beaconPublisher.getEndpointUri()); - beaconPublisher.run(); - } - } -} \ No newline at end of file diff --git a/beacon/spring/src/main/java/edu/internet2/tap/beacon/configuration/condition/BeaconEnvironmentVariablesCondition.java b/beacon/spring/src/main/java/edu/internet2/tap/beacon/configuration/condition/BeaconEnvironmentVariablesCondition.java deleted file mode 100644 index 84f8857fc..000000000 --- a/beacon/spring/src/main/java/edu/internet2/tap/beacon/configuration/condition/BeaconEnvironmentVariablesCondition.java +++ /dev/null @@ -1,48 +0,0 @@ -package edu.internet2.tap.beacon.configuration.condition; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.autoconfigure.condition.ConditionOutcome; -import org.springframework.boot.autoconfigure.condition.SpringBootCondition; -import org.springframework.context.annotation.Condition; -import org.springframework.context.annotation.ConditionContext; -import org.springframework.core.env.Environment; -import org.springframework.core.type.AnnotatedTypeMetadata; - -import static edu.internet2.tap.beacon.Beacon.IMAGE; -import static edu.internet2.tap.beacon.Beacon.LOG_HOST; -import static edu.internet2.tap.beacon.Beacon.LOG_PORT; -import static edu.internet2.tap.beacon.Beacon.MAINTAINER; -import static edu.internet2.tap.beacon.Beacon.TIERVERSION; -import static edu.internet2.tap.beacon.Beacon.VERSION; - -/** - * {@link Condition} that checks for required beacon environment variables. - * - * @author Dmitriy Kopylenko - * @see ConditionalOnBeaconEnvironmentVariablesPresent - */ -public class BeaconEnvironmentVariablesCondition extends SpringBootCondition { - - private static final String MATCHED_MSG = "Beacon properties are present. Beacon activation condition is matched."; - - private static final String NOT_MATCHED_MSG = "Beacon properties are not present. Beacon activation condition is not matched."; - - private static final Logger logger = LoggerFactory.getLogger(BeaconEnvironmentVariablesCondition.class); - - @Override - public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { - Environment env = context.getEnvironment(); - if (env.containsProperty(LOG_HOST) - && env.containsProperty(LOG_PORT) - && env.containsProperty(IMAGE) - && env.containsProperty(VERSION) - && env.containsProperty(TIERVERSION) - && env.containsProperty(MAINTAINER)) { - logger.debug(MATCHED_MSG); - return ConditionOutcome.match(MATCHED_MSG); - } - logger.debug(NOT_MATCHED_MSG); - return ConditionOutcome.noMatch(NOT_MATCHED_MSG); - } -} diff --git a/beacon/spring/src/main/java/edu/internet2/tap/beacon/configuration/condition/ConditionalOnBeaconEnvironmentVariablesPresent.java b/beacon/spring/src/main/java/edu/internet2/tap/beacon/configuration/condition/ConditionalOnBeaconEnvironmentVariablesPresent.java deleted file mode 100644 index 66049923b..000000000 --- a/beacon/spring/src/main/java/edu/internet2/tap/beacon/configuration/condition/ConditionalOnBeaconEnvironmentVariablesPresent.java +++ /dev/null @@ -1,21 +0,0 @@ -package edu.internet2.tap.beacon.configuration.condition; - -import org.springframework.context.annotation.Conditional; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * {@link Conditional} that matches specific beacon environment variables are all present. - * - * @author Dmitriy Kopylenko - */ -@Target({ ElementType.TYPE, ElementType.METHOD }) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Conditional(BeaconEnvironmentVariablesCondition.class) -public @interface ConditionalOnBeaconEnvironmentVariablesPresent { -} diff --git a/beacon/spring/src/main/resources/META-INF/spring.factories b/beacon/spring/src/main/resources/META-INF/spring.factories deleted file mode 100644 index ae9c29c00..000000000 --- a/beacon/spring/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1 +0,0 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=edu.internet2.tap.beacon.configuration.BeaconPublishingConfiguration \ No newline at end of file From 07ca894dc44f1a47079e6445d9f341b8f92094c9 Mon Sep 17 00:00:00 2001 From: chasegawa Date: Wed, 31 May 2023 16:00:04 -0700 Subject: [PATCH 10/12] SHIBUI-2578/2579 Slight change to handling the auth event --- .../CoreShibUiConfiguration.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java index 4781fe0c0..66ad8db9d 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java @@ -58,12 +58,17 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.core.io.Resource; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.security.authentication.AuthenticationEventPublisher; +import org.springframework.security.authentication.DefaultAuthenticationEventPublisher; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; @@ -293,4 +298,24 @@ public List beaconEndpointUrl(@Value("${shibui.beacon.url}") String urls) t } return result; } + + @Bean + public AuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher, UserService userService) { + return new RecordLoginHandler(applicationEventPublisher, userService); + } + + class RecordLoginHandler extends DefaultAuthenticationEventPublisher { + private UserService userService; + + public RecordLoginHandler(ApplicationEventPublisher applicationEventPublisher, UserService userService) { + super(applicationEventPublisher); + this.userService = userService; + } + + @Override + public void publishAuthenticationSuccess(Authentication authentication) { + super.publishAuthenticationSuccess(authentication); + userService.updateLoginRecord(authentication.getName()); + } + } } \ No newline at end of file From a20a26c49bea1e3196fcca896d4e706f298916ab Mon Sep 17 00:00:00 2001 From: chasegawa Date: Fri, 2 Jun 2023 16:38:57 -0700 Subject: [PATCH 11/12] SHIBUI-2568/2571 Merging changes to develop --- .../admin/ui/configuration/SpringSecurityConfig.java | 5 +++++ .../net/unicon/shibui/pac4j/Pac4jSpringSecurityConfig.java | 2 ++ 2 files changed, 7 insertions(+) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/SpringSecurityConfig.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/SpringSecurityConfig.java index a0b58ff01..76b3299af 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/SpringSecurityConfig.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/SpringSecurityConfig.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.configuration; +import edu.internet2.tier.shibboleth.admin.ui.domain.BeaconConfiguration; import edu.internet2.tier.shibboleth.admin.ui.security.DefaultAuditorAware; import edu.internet2.tier.shibboleth.admin.ui.security.model.Role; import edu.internet2.tier.shibboleth.admin.ui.security.model.User; @@ -65,6 +66,10 @@ public class SpringSecurityConfig { @Autowired private UserService userService; + public SpringSecurityConfig() { + BeaconConfiguration.setAuthMechanisms("default"); + } + private UserDetailsService adminUserService() { if (defaultPassword != null && !"".equals(defaultPassword)) { User adminUser = userRepository.findByUsername(rootUser).orElseGet(() ->{ diff --git a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/Pac4jSpringSecurityConfig.java b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/Pac4jSpringSecurityConfig.java index 777fec504..a1bc022ca 100644 --- a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/Pac4jSpringSecurityConfig.java +++ b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/Pac4jSpringSecurityConfig.java @@ -2,6 +2,7 @@ import edu.internet2.tier.shibboleth.admin.ui.configuration.EmailConfiguration; import edu.internet2.tier.shibboleth.admin.ui.configuration.SpringSecurityConfig; +import edu.internet2.tier.shibboleth.admin.ui.domain.BeaconConfiguration; import edu.internet2.tier.shibboleth.admin.ui.security.service.IGroupService; import edu.internet2.tier.shibboleth.admin.ui.security.service.IRolesService; import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService; @@ -58,6 +59,7 @@ public Pac4jSpringSecurityConfig(final Config config, UserService userService, this.emailService = emailService; this.groupService = groupService; this.pac4jConfigurationProperties = pac4jConfigurationProperties; + BeaconConfiguration.setAuthMechanisms("Pac4J - SAML2 provider"); } @Bean From 606f224cd1fc2c40f119a27df978ee525e17f92a Mon Sep 17 00:00:00 2001 From: chasegawa Date: Tue, 6 Jun 2023 14:12:26 -0700 Subject: [PATCH 12/12] SHIBUI-2576 Updates to develop branch to enable the beacon changes --- backend/build.gradle | 2 -- .../BeaconConfigurationStartupProcessing.java | 4 ++-- .../shibboleth/admin/ui/domain/BeaconConfiguration.java | 7 +++---- .../admin/ui/security/model/UserLoginRecord.java | 8 +++----- .../admin/ui/configuration/TestConfiguration.groovy | 4 +++- .../net/unicon/shibui/pac4j/AddNewUserFilterTests.groovy | 8 +++----- 6 files changed, 14 insertions(+), 19 deletions(-) diff --git a/backend/build.gradle b/backend/build.gradle index 925499873..ee84e1673 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -154,8 +154,6 @@ dependencies { runtimeOnly "com.microsoft.sqlserver:mssql-jdbc:${project.'sqlserverVersion'}" //Pacj4 sub-project runtimeOnly project(':pac4j-module') - //Beacon - runtimeOnly project(':beacon:spring') // runtime libraries for later java versions runtimeOnly 'org.glassfish.jaxb:jaxb-runtime:2.3.1' // com.sun.xml.bind package diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/postprocessors/BeaconConfigurationStartupProcessing.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/postprocessors/BeaconConfigurationStartupProcessing.java index ee7e1a1e9..928a0f8fd 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/postprocessors/BeaconConfigurationStartupProcessing.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/postprocessors/BeaconConfigurationStartupProcessing.java @@ -2,14 +2,14 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.BeaconConfiguration; import edu.internet2.tier.shibboleth.admin.ui.repository.BeaconConfigurationRepository; +import jakarta.persistence.EntityExistsException; +import jakarta.transaction.Transactional; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; -import javax.persistence.EntityExistsException; -import javax.transaction.Transactional; import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/BeaconConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/BeaconConfiguration.java index c3b263c77..fcc9990fd 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/BeaconConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/BeaconConfiguration.java @@ -1,12 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.domain; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Transient; import lombok.Data; import lombok.Setter; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Transient; - @Data @Entity public class BeaconConfiguration { diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/UserLoginRecord.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/UserLoginRecord.java index 7ed12a93e..4981fd352 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/UserLoginRecord.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/model/UserLoginRecord.java @@ -1,12 +1,10 @@ package edu.internet2.tier.shibboleth.admin.ui.security.model; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; import lombok.Data; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; import java.util.Date; @Data diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy index d756032e8..dfe0851cc 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy @@ -42,6 +42,8 @@ import org.springframework.data.domain.AuditorAware import org.springframework.mail.javamail.JavaMailSender import org.springframework.mail.javamail.JavaMailSenderImpl +import java.time.Duration + /** * NOT A TEST - this is configuration FOR tests */ @@ -158,7 +160,7 @@ class TestConfiguration { @Override Set getNames() { return null } @Override HealthEndpointGroup get(String name) { return null } - }) { + }, Duration.ZERO.plusSeconds(1)) { @Override HealthComponent healthForPath(@Selector(match = Selector.Match.ALL_REMAINING) String... path) { return new Health(new Status(""), new HashMap()); diff --git a/pac4j-module/src/test/groovy/net/unicon/shibui/pac4j/AddNewUserFilterTests.groovy b/pac4j-module/src/test/groovy/net/unicon/shibui/pac4j/AddNewUserFilterTests.groovy index 216ccdafe..d58ecbd4c 100644 --- a/pac4j-module/src/test/groovy/net/unicon/shibui/pac4j/AddNewUserFilterTests.groovy +++ b/pac4j-module/src/test/groovy/net/unicon/shibui/pac4j/AddNewUserFilterTests.groovy @@ -6,9 +6,11 @@ import edu.internet2.tier.shibboleth.admin.ui.security.repository.OwnershipRepos import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository import edu.internet2.tier.shibboleth.admin.ui.security.service.GroupServiceForTesting -import edu.internet2.tier.shibboleth.admin.ui.security.service.IRolesService import edu.internet2.tier.shibboleth.admin.ui.security.service.RolesServiceImpl import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService +import jakarta.servlet.FilterChain +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse import org.pac4j.core.matching.matcher.PathMatcher import org.pac4j.saml.profile.SAML2Profile import org.springframework.beans.factory.annotation.Autowired @@ -25,10 +27,6 @@ import org.springframework.transaction.annotation.Transactional import spock.lang.Specification import spock.lang.Subject -import javax.servlet.FilterChain -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse - @DataJpaTest @ContextConfiguration(classes=[Pac4JTestingConfig]) @EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"])