/* * 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.usergrid.security.shiro; import java.util.HashSet; import java.util.List; import java.util.Set; import org.springframework.util.AntPathMatcher; import org.apache.usergrid.management.UserInfo; import org.apache.usergrid.security.shiro.utils.SubjectUtils; import org.apache.shiro.authz.Permission; import org.apache.shiro.authz.permission.WildcardPermission; public class CustomPermission extends WildcardPermission { /** * */ private static final String ME = "/me/"; static AntPathMatcher matcher = new AntPathMatcher(); private static final long serialVersionUID = 1L; public CustomPermission() { } public CustomPermission( String wildcardString ) { super( wildcardString ); } public CustomPermission( String wildcardString, boolean caseSensitive ) { super( wildcardString, caseSensitive ); } @Override public List<Set<String>> getParts() { return super.getParts(); } @Override public boolean implies( Permission p ) { // By default only supports comparisons with other // PathBasedWildcardPermission if ( !( p instanceof CustomPermission ) ) { return false; } CustomPermission wp = ( CustomPermission ) p; List<Set<String>> otherParts = wp.getParts(); boolean isApp = false; int i = 0; for ( Set<String> otherPart : otherParts ) { // If this permission has less parts than the other permission, // everything after the number of parts contained // in this permission is automatically implied, so return true if ( ( getParts().size() - 1 ) < i ) { return true; } else { if ( ( i == 0 ) && otherPart.contains( "applications" ) ) { isApp = true; } // this part is the permission, the other part is the challenger Set<String> part = getParts().get( i ); // if we know we're doing an application compare // then make sure all the parts from the third onwards // are normalized as paths if ( isApp && ( i > 2 ) ) { part = makePaths( part ); otherPart = makePaths( otherPart ); } if ( !part.contains( WILDCARD_TOKEN ) && !partContainsPart( part, otherPart ) ) { return false; } i++; } } // If this permission has more parts than the other parts, only imply it // if all of the other parts are wildcards for (; i < getParts().size(); i++ ) { Set<String> part = getParts().get( i ); if ( !part.contains( WILDCARD_TOKEN ) ) { return false; } } return true; } static String normalizeIfPath( String p ) { if ( p.startsWith( "/" ) ) { if ( !p.endsWith( "/" ) && !p.endsWith( "*" ) ) { p += "/"; } } return p; } static String makePath( String p ) { if ( p.equals( "*" ) ) { p = "/**"; } if ( !p.startsWith( "/" ) ) { p = "/" + p; } // if (!p.endsWith("/") && !p.endsWith("*")) { // p += "/"; // } return p; } static Set<String> makePaths( Set<String> part ) { Set<String> newPart = new HashSet<String>(); for ( String p : part ) { newPart.add( makePath( p ) ); } return newPart; } static boolean isPath( String p ) { return p.contains( "/" ); } private static boolean doCompare( String p1, String p2 ) { if ( p1.contains( "${user}" ) ) { UserInfo user = SubjectUtils.getUser(); if ( user != null ) { if ( doCompare( p1.replace( "${user}", user.getUsername() ), p2 ) ) { return true; } if ( doCompare( p1.replace( "${user}", user.getUuid().toString() ), p2 ) ) { return true; } } } else if ( p1.contains( ME ) ) { UserInfo user = SubjectUtils.getUser(); if ( user != null ) { if ( doCompare( p1.replace( ME, String.format( "/%s/", user.getUsername() ) ), p2 ) ) { return true; } if ( doCompare( p1.replace( ME, String.format( "/%s/", user.getUuid().toString() ) ), p2 ) ) { return true; } } } if ( isPath( p1 ) || isPath( p2 ) ) { p1 = makePath( p1 ); p2 = makePath( p2 ); } if ( matcher.isPattern( p1 ) ) { if ( matcher.match( p1, p2 ) ) { return true; } return matcher.match(normalizeIfPath(p1), normalizeIfPath(p2)); } return p1.equalsIgnoreCase( p2 ); } public static boolean partContainsPath( Set<String> part, String path ) { for ( String subpart : part ) { if ( doCompare( subpart, path ) ) { return true; } } return false; } public static boolean partContainsPart( Set<String> part, Set<String> otherPart ) { boolean containsAll = true; for ( String path : otherPart ) { boolean contains = false; for ( String subpart : part ) { if ( doCompare( subpart, path ) ) { contains = true; break; } } containsAll &= contains; } return containsAll; } }