api to export journal logs

This commit is contained in:
Michele Artini 2023-11-20 11:10:00 +01:00
parent 3638a0ea84
commit e4cbfe863b
8 changed files with 237 additions and 61 deletions

View File

@ -23,9 +23,9 @@ public class MainApplication extends AbstractDnetApp {
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.group("D-Net Organizations Service APIs")
.pathsToMatch("/api/**", "/oa_api/**")
.build();
.group("D-Net Organizations Service APIs")
.pathsToMatch("/api/**", "/oa_api/**", "/public-api/**")
.build();
}
@Override

View File

@ -44,28 +44,28 @@ public class MockSecurityConfig extends WebSecurityConfigurerAdapter {
http.headers().frameOptions().sameOrigin();
http.csrf()
.disable()
.authorizeRequests()
.antMatchers("/", "/api/**")
.hasAnyRole(OpenOrgsConstants.VALID_ROLES)
.antMatchers("/registration_api/**")
.hasRole(OpenOrgsConstants.NOT_AUTORIZED_ROLE)
.antMatchers("/common/**", "/resources/**", "/webjars/**", "/metrics", "/health", "/kpis", "/dbmodel/**")
.permitAll()
.antMatchers("/oa_api/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll()
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler());
.disable()
.authorizeRequests()
.antMatchers("/", "/api/**")
.hasAnyRole(OpenOrgsConstants.VALID_ROLES)
.antMatchers("/registration_api/**")
.hasRole(OpenOrgsConstants.NOT_AUTORIZED_ROLE)
.antMatchers("/common/**", "/resources/**", "/webjars/**", "/metrics", "/health", "/kpis", "/dbmodel/**", "/public-api/**")
.permitAll()
.antMatchers("/oa_api/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll()
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler());
}
private AccessDeniedHandler accessDeniedHandler() {
@ -74,7 +74,8 @@ public class MockSecurityConfig extends WebSecurityConfigurerAdapter {
if (auth != null) {
logger
.warn(String.format("User '%s' (%s) attempted to access the protected URL: %s", auth.getName(), req.getRemoteAddr(), req.getRequestURI()));
.warn(String
.format("User '%s' (%s) attempted to access the protected URL: %s", auth.getName(), req.getRemoteAddr(), req.getRequestURI()));
}
if (UserInfo.isNotAuthorized(auth)) {
@ -89,12 +90,12 @@ public class MockSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select ?, '{MD5}" + DigestUtils.md5Hex(DEFAULT_PASSWORD) + "', true")
.authoritiesByUsernameQuery("with const as (SELECT ? as email) "
+ "select c.email, 'ROLE_" + OpenOrgsConstants.OPENORGS_ROLE_PREFIX + "'||coalesce(u.role, '"
+ UserRole.NOT_AUTHORIZED
+ "') from const c left outer join users u on (u.email = c.email)");
.dataSource(dataSource)
.usersByUsernameQuery("select ?, '{MD5}" + DigestUtils.md5Hex(DEFAULT_PASSWORD) + "', true")
.authoritiesByUsernameQuery("with const as (SELECT ? as email) "
+ "select c.email, 'ROLE_" + OpenOrgsConstants.OPENORGS_ROLE_PREFIX + "'||coalesce(u.role, '"
+ UserRole.NOT_AUTHORIZED
+ "') from const c left outer join users u on (u.email = c.email)");
}
@Bean

View File

@ -57,29 +57,29 @@ public class OAuth2WebSecurityConfig extends WebSecurityConfigurerAdapter {
http.headers().frameOptions().sameOrigin();
http.csrf()
.disable()
.authorizeRequests()
.antMatchers("/main", "/api/**")
.hasAnyRole(OpenOrgsConstants.VALID_ROLES)
.antMatchers("/registration_api/**")
.hasRole(OpenOrgsConstants.NOT_AUTORIZED_ROLE)
.antMatchers("/", "/common/**", "/resources/**", "/webjars/**", "/metrics", "/health", "/kpis", "/dbmodel/**")
.permitAll()
.antMatchers("/oa_api/**")
.hasIpAddress(openaireApiValidSubnet)
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
.and()
.logout()
.logoutSuccessHandler(oidcLogoutSuccessHandler())
.invalidateHttpSession(true)
.clearAuthentication(true)
.deleteCookies("JSESSIONID")
.and()
.oauth2Login(oauth2 -> oauth2.userInfoEndpoint(userInfo -> userInfo.oidcUserService(this.oidcUserService())));
.disable()
.authorizeRequests()
.antMatchers("/main", "/api/**")
.hasAnyRole(OpenOrgsConstants.VALID_ROLES)
.antMatchers("/registration_api/**")
.hasRole(OpenOrgsConstants.NOT_AUTORIZED_ROLE)
.antMatchers("/", "/common/**", "/resources/**", "/webjars/**", "/metrics", "/health", "/kpis", "/dbmodel/**", "/public-api/**")
.permitAll()
.antMatchers("/oa_api/**")
.hasIpAddress(openaireApiValidSubnet)
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
.and()
.logout()
.logoutSuccessHandler(oidcLogoutSuccessHandler())
.invalidateHttpSession(true)
.clearAuthentication(true)
.deleteCookies("JSESSIONID")
.and()
.oauth2Login(oauth2 -> oauth2.userInfoEndpoint(userInfo -> userInfo.oidcUserService(oidcUserService())));
}
private AccessDeniedHandler accessDeniedHandler() {
@ -87,8 +87,8 @@ public class OAuth2WebSecurityConfig extends WebSecurityConfigurerAdapter {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
log.warn(String
.format("User '%s' (%s) attempted to access the protected URL: %s", UserInfo.getEmail(authentication), req
.getRemoteAddr(), req.getRequestURI()));
.format("User '%s' (%s) attempted to access the protected URL: %s", UserInfo.getEmail(authentication), req
.getRemoteAddr(), req.getRequestURI()));
}
if (UserInfo.isNotAuthorized(authentication)) {
@ -122,7 +122,7 @@ public class OAuth2WebSecurityConfig extends WebSecurityConfigurerAdapter {
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
final OidcUserService delegate = new OidcUserService();
return (userRequest) -> {
return userRequest -> {
final OidcUser oidcUser = delegate.loadUser(userRequest);
log.debug("User attributes:");
@ -137,9 +137,9 @@ public class OAuth2WebSecurityConfig extends WebSecurityConfigurerAdapter {
}
final String role = "ROLE_" + OpenOrgsConstants.OPENORGS_ROLE_PREFIX + user
.map(User::getRole)
.filter(StringUtils::isNotBlank)
.orElse(UserRole.NOT_AUTHORIZED.toString());
.map(User::getRole)
.filter(StringUtils::isNotBlank)
.orElse(UserRole.NOT_AUTHORIZED.toString());
final Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
mappedAuthorities.add(new SimpleGrantedAuthority(role));

View File

@ -0,0 +1,35 @@
package eu.dnetlib.organizations.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import eu.dnetlib.organizations.model.view.ApiJournalView;
import eu.dnetlib.organizations.repository.OrganizationRepository;
import eu.dnetlib.organizations.repository.readonly.ApiJournalViewRepository;
@RestController
@RequestMapping("/public-api")
public class PublicApiController {
@Autowired
private ApiJournalViewRepository apiJournalViewRepository;
@Autowired
private OrganizationRepository organizationRepository;
@GetMapping("/logs")
public ApiJournalView findJournalByDsId(@RequestParam final String id) {
return apiJournalViewRepository.findById(id).orElse(organizationRepository.findById(id).map(ApiJournalView::new).orElse(new ApiJournalView()));
}
@GetMapping("/logs/{country}/{page}/{size}")
public Page<ApiJournalView> findJournalByCountry(@PathVariable final String country, @PathVariable final int page, @PathVariable final int size) {
return apiJournalViewRepository.findByCountry(country, PageRequest.of(page, size));
}
}

View File

@ -0,0 +1,37 @@
package eu.dnetlib.organizations.model.utils;
import java.io.Serializable;
import java.time.LocalDateTime;
public class ApiOperation implements Serializable {
private static final long serialVersionUID = -7111524441502889608L;
private String operation;
private String description;
private LocalDateTime date;
public String getOperation() {
return operation;
}
public void setOperation(final String operation) {
this.operation = operation;
}
public String getDescription() {
return description;
}
public void setDescription(final String description) {
this.description = description;
}
public LocalDateTime getDate() {
return date;
}
public void setDate(final LocalDateTime date) {
this.date = date;
}
}

View File

@ -0,0 +1,87 @@
package eu.dnetlib.organizations.model.view;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;
import com.vladmihalcea.hibernate.type.json.JsonBinaryType;
import com.vladmihalcea.hibernate.type.json.JsonStringType;
import eu.dnetlib.organizations.model.Organization;
import eu.dnetlib.organizations.model.utils.ApiOperation;
@Entity
@Table(name = "api_journal_view")
@TypeDefs({
@TypeDef(name = "json", typeClass = JsonStringType.class),
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
})
public class ApiJournalView implements Serializable {
private static final long serialVersionUID = 1270660185726854334L;
@Id
@Column(name = "id")
private String id;
@Column(name = "name")
private String name;
@Column(name = "country")
private String country;
@Type(type = "json")
@Column(name = "logs", columnDefinition = "json")
private List<ApiOperation> logs;
public ApiJournalView() {}
public ApiJournalView(final Organization o) {
id = o.getId();
name = o.getName();
country = o.getCountry();
logs = new ArrayList<>();
}
public String getId() {
return id;
}
public void setId(final String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public String getCountry() {
return country;
}
public void setCountry(final String country) {
this.country = country;
}
public List<ApiOperation> getLogs() {
return logs;
}
public void setLogs(final List<ApiOperation> logs) {
this.logs = logs;
}
}

View File

@ -0,0 +1,14 @@
package eu.dnetlib.organizations.repository.readonly;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
import eu.dnetlib.organizations.model.view.ApiJournalView;
@Repository
public interface ApiJournalViewRepository extends ReadOnlyRepository<ApiJournalView, String> {
Page<ApiJournalView> findByCountry(String country, Pageable page);
}

View File

@ -6,6 +6,7 @@ DROP VIEW IF EXISTS conflict_groups_view;
DROP VIEW IF EXISTS suggestions_info_by_country_view;
DROP VIEW IF EXISTS duplicate_groups_view;
DROP VIEW IF EXISTS persistent_orgs_view;
DROP VIEW IF EXISTS api_journal_view;
DROP TABLE IF EXISTS sysconf;
DROP TABLE IF EXISTS other_ids;
@ -748,3 +749,4 @@ $$;
CREATE TRIGGER insert_or_update_index_search_trigger AFTER INSERT OR UPDATE ON organizations FOR EACH ROW EXECUTE PROCEDURE insert_or_update_index_search_trigger();
CREATE TRIGGER delete_index_search_trigger BEFORE DELETE ON organizations FOR EACH ROW EXECUTE PROCEDURE delete_index_search();