| /* |
| * 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 + |
| '}'; |
| } |
| } |