blob: 987f3e967b53e464bdc9826fb07b96eec13e07ea [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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. You may obtain a copy of the License at
*
* 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 org.apache.fineract.cn.anubis.security;
import org.apache.fineract.cn.anubis.api.v1.domain.AllowedOperation;
import org.apache.fineract.cn.anubis.service.PermissionSegmentMatcher;
import org.apache.fineract.cn.api.util.ApiConstants;
import org.apache.fineract.cn.lang.ApplicationName;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;
import javax.annotation.Nonnull;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiPredicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author Myrle Krantz
*/
public class ApplicationPermission implements GrantedAuthority {
public static final String URL_AUTHORITY = "maats_feather";
private final List<PermissionSegmentMatcher> servletPathSegmentMatchers;
private final AllowedOperation allowedOperation;
private final String accountOperation;
private final boolean acceptTokenIntendedForForeignApplication;
public ApplicationPermission(
final String servletPath,
final AllowedOperation allowedOperation,
final boolean acceptTokenIntendedForForeignApplication) {
this.allowedOperation = allowedOperation;
this.accountOperation = null;
servletPathSegmentMatchers = PermissionSegmentMatcher.getServletPathSegmentMatchers(servletPath);
this.acceptTokenIntendedForForeignApplication = acceptTokenIntendedForForeignApplication;
}
public ApplicationPermission(
final String servletPath,
final String accountOperation,
final boolean acceptTokenIntendedForForeignApplication) {
this.allowedOperation = AllowedOperation.READ;
this.accountOperation = accountOperation;
servletPathSegmentMatchers = PermissionSegmentMatcher.getServletPathSegmentMatchers(servletPath);
this.acceptTokenIntendedForForeignApplication = acceptTokenIntendedForForeignApplication;
}
AllowedOperation getAllowedOperation() {
return allowedOperation;
}
@Override public String getAuthority() {
return URL_AUTHORITY;
}
public String getAccountOperation() {
return accountOperation;
}
boolean matches(final FilterInvocation filterInvocation,
final ApplicationName applicationName,
final AnubisPrincipal principal) {
return matches(filterInvocation.getRequest(), applicationName, principal);
}
boolean matches(final HttpServletRequest request,
final ApplicationName applicationName,
final AnubisPrincipal principal) {
if (!acceptTokenIntendedForForeignApplication && !applicationName.toString().equals(principal.getForApplicationName()))
return false;
boolean isSu = principal.getUser().equals(ApiConstants.SYSTEM_SU);
return matchesHelper(
request.getServletPath(),
request.getMethod(),
(matcher, segment) -> matcher.matches(segment, principal, acceptTokenIntendedForForeignApplication, isSu));
}
boolean matches(final String path, String method,
final String applicationName,
final AnubisPrincipal principal) {
if (!acceptTokenIntendedForForeignApplication && !applicationName.equals(principal.getForApplicationName()))
return false;
boolean isSu = principal.getUser().equals(ApiConstants.SYSTEM_SU);
return matchesHelper(
path,
method,
(matcher, segment) -> matcher.matches(segment, principal, acceptTokenIntendedForForeignApplication, isSu));
}
private boolean matchesHelper(final String servletPath, final String method,
@Nonnull final BiPredicate<PermissionSegmentMatcher, String> segmentMatcher) {
final boolean opMatches = allowedOperation.containsHttpMethod(method);
final String[] requestPathSegments = servletPath.split("/");
if (servletPathSegmentMatchers.size() > requestPathSegments.length + 1)
return false;
if (servletPathSegmentMatchers.size() == requestPathSegments.length + 1)
if (!lastSegmentIsStarSegment(servletPathSegmentMatchers))
return false;
if (servletPathSegmentMatchers.size() < requestPathSegments.length)
if (!lastSegmentIsStarSegment(servletPathSegmentMatchers))
return false;
final Optional<Integer> indexOfFirstNonMappableSegment =
Stream.iterate(0, n -> n + 1)
.limit(Math.min(servletPathSegmentMatchers.size(), requestPathSegments.length))
.filter(i -> !segmentMatcher.test(servletPathSegmentMatchers.get(i), requestPathSegments[i]))
.findFirst();
return opMatches && !indexOfFirstNonMappableSegment.isPresent();
}
private static boolean lastSegmentIsStarSegment(
final List<PermissionSegmentMatcher> servletPathSegmentMatchers) {
return servletPathSegmentMatchers.get(servletPathSegmentMatchers.size() -1).isStarSegment();
}
@Override public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof ApplicationPermission))
return false;
ApplicationPermission that = (ApplicationPermission) o;
return Objects.equals(servletPathSegmentMatchers, that.servletPathSegmentMatchers)
&& allowedOperation == that.allowedOperation;
}
@Override public int hashCode() {
return Objects.hash(servletPathSegmentMatchers, allowedOperation);
}
@Override
public String toString() {
return "ApplicationPermission{" +
"servletPathSegmentMatchers='" + servletPathSegmentMatchers.stream().map(PermissionSegmentMatcher::getPermissionSegment).collect(Collectors.joining("/")) +
"', allowedOperation=" + allowedOperation +
'}';
}
}