Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.1.3] Release #282

Merged
merged 9 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</parent>

<artifactId>termit</artifactId>
<version>3.1.2</version>
<version>3.1.3</version>
<name>TermIt</name>
<description>Terminology manager based on Semantic Web technologies.</description>
<packaging>${packaging}</packaging>
Expand Down
30 changes: 27 additions & 3 deletions src/main/java/cz/cvut/kbss/termit/persistence/dao/DataDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import cz.cvut.kbss.termit.util.Configuration;
import cz.cvut.kbss.termit.util.Configuration.Persistence;
import cz.cvut.kbss.termit.util.TypeAwareResource;
import jakarta.annotation.Nullable;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.ValueFactory;
import org.eclipse.rdf4j.repository.RepositoryConnection;
Expand All @@ -41,7 +42,13 @@

import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

@Repository
Expand Down Expand Up @@ -139,13 +146,30 @@ public Optional<RdfsResource> find(URI id) {
/**
* Gets the {@link RDFS#LABEL} of a resource with the specified identifier.
* <p>
* Note that the label has to have matching language tag or no language tag at all (matching tag is preferred).
* Note that the label has to have language tag matching the configured persistence unit language
* or no language tag at all (matching tag is preferred).
*
* @param id Resource ({@link RDFS#RESOURCE}) identifier
* @return Matching resource identifier (if found)
*/
public Optional<String> getLabel(URI id) {
return getLabel(id, null);
}

/**
* Gets the {@link RDFS#LABEL} of a resource with the specified identifier.
* <p>
* Note that the label has to have matching language tag or no language tag at all (matching tag is preferred).
*
* @param id Resource ({@link RDFS#RESOURCE}) identifier
* @param language Label language, if null, configured persistence unit language is used instead
* @return Matching resource identifier (if found)
*/
public Optional<String> getLabel(URI id, @Nullable String language) {
Objects.requireNonNull(id);
if(language == null) {
language = config.getLanguage();
}
if (!id.isAbsolute()) {
return Optional.of(id.toString());
}
Expand All @@ -159,7 +183,7 @@ public Optional<String> getLabel(URI id) {
String.class)
.setParameter("x", id).setParameter("has-label", RDFS_LABEL)
.setParameter("has-title", URI.create(DC.Terms.TITLE))
.setParameter("tag", config.getLanguage(), null).getSingleResult());
.setParameter("tag", language, null).getSingleResult());
} catch (NoResultException | NoUniqueResultException e) {
return Optional.empty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import cz.cvut.kbss.jopa.model.EntityManager;
import cz.cvut.kbss.termit.exception.PersistenceException;
import cz.cvut.kbss.termit.model.PasswordChangeRequest;
import cz.cvut.kbss.termit.util.Configuration;
import cz.cvut.kbss.termit.model.UserAccount;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

Expand All @@ -13,19 +13,16 @@
@Repository
public class PasswordChangeRequestDao extends BaseDao<PasswordChangeRequest> {

private final Configuration.Persistence persistenceConfig;

@Autowired
public PasswordChangeRequestDao(EntityManager em, Configuration configuration) {
public PasswordChangeRequestDao(EntityManager em) {
super(PasswordChangeRequest.class, em);
this.persistenceConfig = configuration.getPersistence();
}

public List<PasswordChangeRequest> findAllByUsername(String username) {
Objects.requireNonNull(username);
public List<PasswordChangeRequest> findAllByUserAccount(UserAccount userAccount) {
Objects.requireNonNull(userAccount);
try {
return em.createQuery("SELECT DISTINCT t FROM " + type.getSimpleName() + " t WHERE t.userAccount.username = :username", type)
.setParameter("username", username, persistenceConfig.getLanguage())
return em.createQuery("SELECT DISTINCT t FROM " + type.getSimpleName() + " t WHERE t.userAccount = :userAccount", type)
.setParameter("userAccount", userAccount)
.getResultList();
} catch (RuntimeException e) {
throw new PersistenceException(e);
Expand Down
21 changes: 15 additions & 6 deletions src/main/java/cz/cvut/kbss/termit/persistence/dao/TermDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,8 @@ public List<TermDto> findAllIncludingImported(Vocabulary vocabulary) {

/**
* Loads a page of root terms (terms without a parent) contained in the specified vocabulary.
* <p>
* Terms with a label in the instance language are prepended.
*
* @param vocabulary Vocabulary whose root terms should be returned
* @param pageSpec Page specification
Expand All @@ -401,13 +403,14 @@ public List<TermDto> findAllRoots(Vocabulary vocabulary, Pageable pageSpec, Coll
Objects.requireNonNull(vocabulary);
Objects.requireNonNull(pageSpec);
TypedQuery<TermDto> query = em.createNativeQuery("SELECT DISTINCT ?term WHERE {" +
"SELECT DISTINCT ?term ?hasLocaleLabel WHERE {" +
"GRAPH ?context { " +
"?term a ?type ;" +
"?hasLabel ?label ." +
"?vocabulary ?hasGlossary/?hasTerm ?term ." +
"FILTER (lang(?label) = ?labelLang) ." +
"BIND((lang(?label) = ?labelLang) as ?hasLocaleLabel) ." +
"FILTER (?term NOT IN (?included))" +
"}} ORDER BY " + orderSentence("?label"),
"}} ORDER BY DESC(?hasLocaleLabel) lang(?label) " + orderSentence("?label") + "}",
TermDto.class);
query = setCommonFindAllRootsQueryParams(query, false);
try {
Expand Down Expand Up @@ -453,6 +456,8 @@ private static String r(String string, String from, String to) {

/**
* Loads a page of root terms (terms without a parent).
* <p>
* Terms with a label in the instance language are prepended.
*
* @param pageSpec Page specification
* @param includeTerms Identifiers of terms which should be a part of the result. Optional
Expand All @@ -462,13 +467,14 @@ private static String r(String string, String from, String to) {
public List<TermDto> findAllRoots(Pageable pageSpec, Collection<URI> includeTerms) {
Objects.requireNonNull(pageSpec);
TypedQuery<TermDto> query = em.createNativeQuery("SELECT DISTINCT ?term WHERE {" +
"SELECT DISTINCT ?term ?hasLocaleLabel WHERE {" +
"?term a ?type ; " +
"?hasLabel ?label . " +
"?vocabulary ?hasGlossary/?hasTerm ?term . " +
"FILTER (lang(?label) = ?labelLang) . " +
"BIND((lang(?label) = ?labelLang) as ?hasLocaleLabel) ." +
"FILTER (?term NOT IN (?included)) . " +
"FILTER NOT EXISTS {?term a ?snapshot .} " +
"} ORDER BY " + orderSentence("?label"),
"} ORDER BY DESC(?hasLocaleLabel) lang(?label) " + orderSentence("?label") + "}",
TermDto.class);
query = setCommonFindAllRootsQueryParams(query, false);
try {
Expand Down Expand Up @@ -533,6 +539,8 @@ private void recursivelyLoadParentTermSubTerms(TermDto term) {
* <p>
* This method basically does a transitive closure of the vocabulary import relationship and retrieves a page of
* root terms from this closure.
* <p>
* Terms with a label in the instance language are prepended.
*
* @param vocabulary The last vocabulary in the vocabulary import chain
* @param pageSpec Page specification
Expand All @@ -544,13 +552,14 @@ public List<TermDto> findAllRootsIncludingImports(Vocabulary vocabulary, Pageabl
Objects.requireNonNull(vocabulary);
Objects.requireNonNull(pageSpec);
TypedQuery<TermDto> query = em.createNativeQuery("SELECT DISTINCT ?term WHERE {" +
"SELECT DISTINCT ?term ?hasLocaleLabel WHERE {" +
"?term a ?type ;" +
"?hasLabel ?label ." +
"?vocabulary ?imports* ?parent ." +
"?parent ?hasGlossary/?hasTerm ?term ." +
"FILTER (lang(?label) = ?labelLang) ." +
"BIND((lang(?label) = ?labelLang) as ?hasLocaleLabel) ." +
"FILTER (?term NOT IN (?included))" +
"} ORDER BY " + orderSentence("?label"),
"} ORDER BY DESC(?hasLocaleLabel) lang(?label) " + orderSentence("?label") + "}",
TermDto.class);
query = setCommonFindAllRootsQueryParams(query, true);
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import cz.cvut.kbss.termit.model.validation.ValidationResult;
import cz.cvut.kbss.termit.persistence.context.VocabularyContextMapper;
import cz.cvut.kbss.termit.util.Configuration;
import cz.cvut.kbss.termit.util.Constants;
import cz.cvut.kbss.termit.util.Utils;
import org.apache.jena.rdf.model.Literal;
import org.apache.jena.rdf.model.Model;
Expand Down Expand Up @@ -113,7 +114,7 @@ private Model initValidationModel(com.github.sgov.server.Validator validator, St
// Currently, only using content rules, not OntoUml, as TermIt does not support adding OntoUml rules
validator.getModelRules().stream()
.filter(r -> MODEL_RULES_TO_ADD.stream().anyMatch(s -> r.toString().contains(s)))
.collect(Collectors.toList())
.toList()
);
final Model validationModel = com.github.sgov.server.Validator.getRulesModel(rules);
loadOverrideRules(validationModel, language);
Expand Down Expand Up @@ -152,7 +153,8 @@ public List<ValidationResult> validate(final Collection<URI> vocabularyIris) {
final MultilingualString messages = new MultilingualString(result.getMessages().stream()
.map(RDFNode::asLiteral)
.collect(Collectors.toMap(
Literal::getLanguage,
lit -> lit.getLanguage().isBlank() ?
Constants.DEFAULT_LANGUAGE : lit.getLanguage(),
Literal::getLexicalForm)));

return new ValidationResult()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@
import cz.cvut.kbss.termit.model.UserAccount;
import cz.cvut.kbss.termit.security.SecurityConstants;
import cz.cvut.kbss.termit.service.business.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
Expand All @@ -41,12 +45,12 @@
* Available only if internal security is used.
*/
@ConditionalOnProperty(prefix = "termit.security", name = "provider", havingValue = "internal", matchIfMissing = true)
@Tag(name = "Admin User Registration", description = "Allows admins to register new users.")
@RestController
@RequestMapping("/users")
@Profile("admin-registration-only")
@RequestMapping("/admin/users")
public class AdminBasedRegistrationController {

private static final Logger LOG = LoggerFactory.getLogger(FreeRegistrationController.class);
private static final Logger LOG = LoggerFactory.getLogger(AdminBasedRegistrationController.class);

private final UserService userService;

Expand All @@ -56,11 +60,17 @@ public AdminBasedRegistrationController(UserService userService) {
LOG.debug("Instantiating admin-based registration controller.");
}

@Operation(security = {@SecurityRequirement(name="bearer-key")},
description = "Creates a new user account. If the password is blank, the account is locked, and an email will be sent to the new user with a link to create a password.")
@ApiResponses({
@ApiResponse(responseCode = "201", description = "User created"),
@ApiResponse(responseCode = "409", description = "User data are invalid")
})
@PreAuthorize("hasRole('" + SecurityConstants.ROLE_ADMIN + "')")
@PostMapping(consumes = {MediaType.APPLICATION_JSON_VALUE, JsonLd.MEDIA_TYPE})
public ResponseEntity<Void> createUser(@RequestBody UserAccount user) {
userService.persist(user);
LOG.info("User {} successfully registered.", user);
userService.adminCreateUser(user);
LOG.info("User {} successfully registered by {}.", user, userService.getCurrent().getUsername());
return new ResponseEntity<>(HttpStatus.CREATED);
}
}
14 changes: 11 additions & 3 deletions src/main/java/cz/cvut/kbss/termit/rest/DataController.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.net.URI;
import java.util.List;
Expand Down Expand Up @@ -92,8 +97,11 @@ public RdfsResource getById(@Parameter(description = "Identifier of the resource
})
@GetMapping(value = "/label")
public String getLabel(@Parameter(description = "Resource identifier.")
@RequestParam("iri") URI id) {
return dataService.getLabel(id).orElseThrow(
@RequestParam("iri") URI id,
@Parameter(description = "Label language")
@RequestParam(value = "language", required = false) String language
) {
return dataService.getLabel(id, language).orElseThrow(
() -> new NotFoundException("Resource with id " + id + " not found or it has no matching label."));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import cz.cvut.kbss.jsonld.JsonLd;
import cz.cvut.kbss.termit.dto.PasswordChangeDto;
import cz.cvut.kbss.termit.service.business.PasswordChangeService;
import cz.cvut.kbss.termit.service.business.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
Expand Down Expand Up @@ -30,11 +30,11 @@
public class PasswordChangeController {
private static final Logger LOG = LoggerFactory.getLogger(PasswordChangeController.class);

private final PasswordChangeService passwordChangeService;
private final UserService userService;

@Autowired
public PasswordChangeController(PasswordChangeService passwordChangeService) {
this.passwordChangeService = passwordChangeService;
public PasswordChangeController(UserService userService) {
this.userService = userService;
}

@Operation(description = "Requests a password reset for the specified username.")
Expand All @@ -47,7 +47,7 @@ public PasswordChangeController(PasswordChangeService passwordChangeService) {
public ResponseEntity<Void> requestPasswordReset(
@Parameter(description = "Username of the user") @RequestBody String username) {
LOG.info("Password reset requested for user {}.", username);
passwordChangeService.requestPasswordReset(username);
userService.requestPasswordReset(username);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}

Expand All @@ -62,7 +62,7 @@ public ResponseEntity<Void> changePassword(
@Parameter(
description = "Token with URI for password reset") @RequestBody PasswordChangeDto passwordChangeDto) {
LOG.info("Password change requested with token {}", passwordChangeDto.getToken());
passwordChangeService.changePassword(passwordChangeDto);
userService.changePassword(passwordChangeDto);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
Loading