Skip to content

Commit

Permalink
Merge branch 'release/29.x'
Browse files Browse the repository at this point in the history
* release/29.x:
  #4266 - Improve backToProject call
  • Loading branch information
reckart committed Nov 2, 2023
2 parents 8053e54 + 214b1d5 commit ecf43d6
Show file tree
Hide file tree
Showing 13 changed files with 306 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,13 @@ public void setProject(Project aProject)
project = aProject;
}

@Override
public void clearProject()
{
project = null;
clearDocument();
}

@Override
public ScriptDirection getScriptDirection()
{
Expand Down Expand Up @@ -277,6 +284,12 @@ public int getNumberOfDocuments()
return numberOfDocuments;
}

@Override
public void clearDocument()
{
setDocument(null, null);
}

@Override
public void setDocument(SourceDocument aDocument, List<SourceDocument> aDocuments)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public interface AnnotatorDocumentNavigation
// ---------------------------------------------------------------------------------------------
// Document
// ---------------------------------------------------------------------------------------------
void clearDocument();

SourceDocument getDocument();

void setDocument(SourceDocument aDocument, List<SourceDocument> aDocuments);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ public interface AnnotatorState
@Override
Project getProject();

void clearProject();

void setProject(Project aProject);

// ---------------------------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,7 @@ void savePropertiesFile(Project aProject, InputStream aInputStream, String aFile
* additional roles.
* @param aMoreRoles
* more roles.
* @return whether the user has any role in the project.
* @return whether the user has any of the roles in the project.
*/
boolean hasRole(User aUser, Project aProject, PermissionLevel aRole,
PermissionLevel... aMoreRoles);
Expand All @@ -527,11 +527,43 @@ boolean hasRole(User aUser, Project aProject, PermissionLevel aRole,
* additional roles.
* @param aMoreRoles
* more roles.
* @return whether the user has any role in the project.
* @return whether the user has any of the roles in the project.
*/
boolean hasRole(String aUser, Project aProject, PermissionLevel aRole,
PermissionLevel... aMoreRoles);

/**
* Check whether the given user has one or more roles in any project. Note that the split into
* two arguments is only for the compiler to be able to check if at least one role has been
* specified. The first role is not privileged over the other roles in any way!
*
* @param aUser
* a user.
* @param aRole
* at least one role must be given, but the check is against this role OR any of the
* additional roles.
* @param aMoreRoles
* more roles.
* @return whether the user has any of the roles in any project.
*/
boolean hasRoleInAnyProject(User aUser, PermissionLevel aRole, PermissionLevel... aMoreRoles);

/**
* Check whether the given user has one or more roles in any project. Note that the split into
* two arguments is only for the compiler to be able to check if at least one role has been
* specified. The first role is not privileged over the other roles in any way!
*
* @param aUser
* a user.
* @param aRole
* at least one role must be given, but the check is against this role OR any of the
* additional roles.
* @param aMoreRoles
* more roles.
* @return whether the user has any of the roles in any project.
*/
boolean hasRoleInAnyProject(String aUser, PermissionLevel aRole, PermissionLevel... aMoreRoles);

// --------------------------------------------------------------------------------------------
// Methods related to other things
// --------------------------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public static void accessToStaticResources(
.antMatchers("/images/**").permitAll() //
.antMatchers("/resources/**").permitAll() //
.antMatchers("/whoops").permitAll() //
.antMatchers("/nowhere").permitAll() //
.antMatchers("/about/**").permitAll() //
.antMatchers("/wicket/resource/**").permitAll();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,37 @@ public boolean hasRole(String aUser, Project aProject, PermissionLevel aRole,
.getSingleResult() > 0;
}

@Override
@Transactional
public boolean hasRoleInAnyProject(User aUser, PermissionLevel aRole,
PermissionLevel... aMoreRoles)
{
return hasRoleInAnyProject(aUser.getUsername(), aRole, aMoreRoles);
}

@Override
@Transactional
public boolean hasRoleInAnyProject(String aUser, PermissionLevel aRole,
PermissionLevel... aMoreRoles)
{
Validate.notNull(aRole, "hasRoleInAnyProject() requires at least one role to check");

var roles = new LinkedHashSet<>();
roles.add(aRole);
if (aMoreRoles != null) {
roles.addAll(asList(aMoreRoles));
}

String query = String.join("\n", //
"SELECT COUNT(*) FROM ProjectPermission ", //
"WHERE user = :user AND level IN (:roles)");

return entityManager.createQuery(query, Long.class) //
.setParameter("user", aUser) //
.setParameter("roles", roles) //
.getSingleResult() > 0;
}

@Deprecated
@Override
@Transactional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,7 @@ protected void handleParameters(StringValue aDocumentParameter, StringValue aFoc
var requestedUser = userRepository.get(aUserParameter.toString());
if (requestedUser == null) {
failWithDocumentNotFound("User not found [" + aUserParameter + "]");
return;
}
else {
LOG.trace("Changing data owner: {}", requestedUser);
Expand All @@ -622,6 +623,7 @@ protected void handleParameters(StringValue aDocumentParameter, StringValue aFoc
String.valueOf(project.getId()), doc.getId(), state.getUser().getUsername())) {
failWithDocumentNotFound("Access to document [" + aDocumentParameter + "] in project ["
+ project.getName() + "] is denied");
return;
}

// If we arrive here and the document is not null, then we have a change of document
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package de.tudarmstadt.ukp.clarin.webanno.ui.core.page;

import static de.tudarmstadt.ukp.clarin.webanno.model.PermissionLevel.MANAGER;
import static java.lang.String.format;
import static java.util.stream.Collectors.joining;

Expand All @@ -41,6 +42,7 @@
import de.tudarmstadt.ukp.clarin.webanno.model.Project;
import de.tudarmstadt.ukp.clarin.webanno.security.UserDao;
import de.tudarmstadt.ukp.clarin.webanno.security.model.User;
import de.tudarmstadt.ukp.inception.ui.core.AccessDeniedPage;
import de.tudarmstadt.ukp.inception.ui.core.config.DashboardProperties;

public abstract class ProjectPageBase
Expand Down Expand Up @@ -75,6 +77,7 @@ protected final void requireAnyProjectRole(User aUser)
if (aUser == null || !projectService.hasAnyRole(aUser, project)) {
getSession().error(format("To access the [%s] you need to be a member of the project",
getClass().getSimpleName()));

backToProjectPage();
}
}
Expand All @@ -97,22 +100,18 @@ protected final void requireProjectRole(User aUser, PermissionLevel aRole,

public void backToProjectPage()
{
Class<? extends Page> projectDashboard = WicketObjects.resolveClass(
"de.tudarmstadt.ukp.inception.ui.core.dashboard.project.ProjectDashboardPage");

// If the current user cannot access the dashboard, at least update the feedback panel on
// the current page so that the user can see the error message that has most likely been
// queued
if (!projectService.hasRole(userService.getCurrentUsername(), getProject(),
PermissionLevel.MANAGER,
// If the current user cannot access the dashboard, send them to an access denied page
if (!projectService.hasRole(userService.getCurrentUsername(), getProject(), MANAGER,
dashboardProperties.getAccessibleByRoles().toArray(PermissionLevel[]::new))) {
getRequestCycle().find(AjaxRequestTarget.class)
.ifPresent(_target -> _target.addChildren(getPage(), IFeedback.class));
return;
throw new RestartResponseException(AccessDeniedPage.class);
}

PageParameters pageParameters = new PageParameters();
var pageParameters = new PageParameters();
setProjectPageParameter(pageParameters, getProject());
Class<? extends Page> projectDashboard = WicketObjects.resolveClass(
"de.tudarmstadt.ukp.inception.ui.core.dashboard.project.ProjectDashboardPage");
throw new RestartResponseException(projectDashboard, pageParameters);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<!--
Licensed to the Technische Universität Darmstadt under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The Technische Universität Darmstadt
licenses this file to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html xmlns:wicket="http://wicket.apache.org">
<body>
<wicket:extend>
<div class="scrolling flex-content flex-h-container mt-5 pt-5" style="min-width: 30em;">
<div class="flex-content flex-v-container flex-centered" style="min-height: 20em;">
<h1 class="display-1">No access</h1>
<p class="lead">There is nothing here for you...</p>
</div>
</div>
</wicket:extend>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Licensed to the Technische Universität Darmstadt under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The Technische Universität Darmstadt
* licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.tudarmstadt.ukp.inception.ui.core;

import static de.tudarmstadt.ukp.clarin.webanno.support.JSONUtil.toPrettyJsonString;
import static java.util.Arrays.asList;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.http.MediaType.parseMediaTypes;

import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.util.Map;
import java.util.Optional;

import org.apache.commons.lang3.StringUtils;
import org.apache.wicket.protocol.http.servlet.ErrorAttributes;
import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
import org.apache.wicket.request.Request;
import org.apache.wicket.request.Url;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.handler.TextRequestHandler;
import org.apache.wicket.request.http.WebRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.wicketstuff.annotation.mount.MountPath;

import com.giffing.wicket.spring.boot.context.scan.WicketAccessDeniedPage;

import de.tudarmstadt.ukp.clarin.webanno.ui.core.page.ApplicationPageBase;

@WicketAccessDeniedPage
@MountPath("/nowhere")
public class AccessDeniedPage
extends ApplicationPageBase
{
private static final long serialVersionUID = 7848496813044538495L;
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

public AccessDeniedPage()
{
if (isJsonResponseRequested() || StringUtils.startsWith(getRequestUri(), "/api")) {
produceJsonResponseIfRequested();
}
}

private void produceJsonResponseIfRequested()
{
String response;
try {
response = toPrettyJsonString(Map.of( //
"messages", asList( //
Map.of( //
"level", "ERROR", //
"message", getStatusReasonPhrase()))));
}
catch (IOException e) {
response = "{\"messages\": [{\"level\": \"ERROR\", \"message\": \"Unable to render error.\"}] }";
LOG.error("Unable to render error message", e);
}

getRequestCycle().scheduleRequestHandlerAfterCurrent(
new TextRequestHandler(APPLICATION_JSON.toString(), null, response));
}

private boolean isJsonResponseRequested()
{
if (!(getRequestCycle().getRequest() instanceof WebRequest)) {
return false;
}

WebRequest request = (WebRequest) getRequestCycle().getRequest();
if (!APPLICATION_JSON.isPresentIn(parseMediaTypes(request.getHeader("Accept")))) {
return false;
}

return true;
}

private Optional<ErrorAttributes> getErrorAttributes()
{
RequestCycle cycle = RequestCycle.get();

Request req = cycle.getRequest();
if (req instanceof ServletWebRequest) {
ServletWebRequest webRequest = (ServletWebRequest) req;
ErrorAttributes errorAttributes = ErrorAttributes.of(webRequest.getContainerRequest(),
webRequest.getFilterPrefix());

return Optional.ofNullable(errorAttributes);
}

return Optional.empty();
}

private String getStatusReasonPhrase()
{
return getErrorAttributes() //
.map(ErrorAttributes::getStatusCode) //
.map(code -> {
try {
return HttpStatus.valueOf(code).getReasonPhrase();
}
catch (IllegalArgumentException e) {
return null;
}
}) //
.orElse(null);
}

private String getRequestUri()
{
Optional<String> uri = getErrorAttributes().map(ErrorAttributes::getRequestUri);

if (uri.isPresent()) {
return uri.get();
}

Url url = RequestCycle.get().getRequest().getOriginalUrl();
if (url != null) {
return "/" + url.toString();
}

return null;
}

@Override
public boolean isErrorPage()
{
return true;
}

@Override
public boolean isVersioned()
{
return false;
}
}
Loading

0 comments on commit ecf43d6

Please sign in to comment.