001package org.apache.archiva.redback.rest.services.interceptors; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.archiva.redback.authentication.AuthenticationResult; 023import org.apache.archiva.redback.authorization.AuthorizationException; 024import org.apache.archiva.redback.authorization.AuthorizationResult; 025import org.apache.archiva.redback.authorization.RedbackAuthorization; 026import org.apache.archiva.redback.integration.filter.authentication.basic.HttpBasicAuthentication; 027import org.apache.archiva.redback.rest.services.RedbackAuthenticationThreadLocal; 028import org.apache.archiva.redback.rest.services.RedbackRequestInformation; 029import org.apache.archiva.redback.system.SecuritySession; 030import org.apache.archiva.redback.system.SecuritySystem; 031import org.apache.archiva.redback.users.User; 032import org.apache.commons.lang3.StringUtils; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035import org.springframework.stereotype.Service; 036 037import javax.annotation.Priority; 038import javax.inject.Inject; 039import javax.inject.Named; 040import javax.servlet.http.HttpServletRequest; 041import javax.ws.rs.container.ContainerRequestContext; 042import javax.ws.rs.container.ContainerRequestFilter; 043import javax.ws.rs.container.ResourceInfo; 044import javax.ws.rs.core.Context; 045import javax.ws.rs.core.Response; 046import javax.ws.rs.core.UriInfo; 047import javax.ws.rs.ext.Provider; 048 049/** 050 * @author Olivier Lamy 051 * @since 1.3 052 */ 053@Service( "permissionInterceptor#rest" ) 054@Provider 055@Priority( Priorities.AUTHORIZATION ) 056public class PermissionsInterceptor 057 extends AbstractInterceptor 058 implements ContainerRequestFilter 059{ 060 061 @Inject 062 @Named( value = "securitySystem" ) 063 private SecuritySystem securitySystem; 064 065 @Inject 066 @Named( value = "httpAuthenticator#basic" ) 067 private HttpBasicAuthentication httpAuthenticator; 068 069 private static final String DEFAULT_AUTHENTICATION_REALM = "archiva"; 070 private String authenticationRealm = DEFAULT_AUTHENTICATION_REALM; 071 072 @Context 073 private ResourceInfo resourceInfo; 074 075 private static final Logger log = LoggerFactory.getLogger( PermissionsInterceptor.class ); 076 077 @Override 078 public void filter( ContainerRequestContext containerRequestContext ) 079 { 080 log.debug( "Filtering request" ); 081 final String requestPath = containerRequestContext.getUriInfo( ).getPath( ); 082 if (ignoreAuth( requestPath )) { 083 return; 084 } 085 086 RedbackAuthorization redbackAuthorization = getRedbackAuthorization( resourceInfo ); 087 088 if ( redbackAuthorization != null ) 089 { 090 if ( redbackAuthorization.noRestriction() ) 091 { 092 log.debug( "redbackAuthorization.noRestriction() so skip permission check" ); 093 // we are fine this services is marked as non restrictive access 094 return; 095 } 096 String[] permissions = redbackAuthorization.permissions(); 097 HttpServletRequest request = getHttpServletRequest( ); 098 //olamy: no value is an array with an empty String 099 if ( permissions != null && permissions.length > 0 // 100 && !( permissions.length == 1 && StringUtils.isEmpty( permissions[0] ) ) ) 101 { 102 SecuritySession securitySession = getSecuritySession( containerRequestContext, httpAuthenticator, request ); 103 AuthenticationResult authenticationResult = getAuthenticationResult( containerRequestContext, httpAuthenticator, request ); 104 log.debug( "authenticationResult from message: {}", authenticationResult ); 105 106 if ( authenticationResult != null && authenticationResult.isAuthenticated() ) 107 { 108 109 User userObject = securitySession == null ? authenticationResult.getUser( ) : securitySession.getUser( ); 110 for ( String permission : permissions ) 111 { 112 log.debug( "check permission: {} with securitySession {}", permission, securitySession ); 113 if ( StringUtils.isBlank( permission ) ) 114 { 115 continue; 116 } 117 try 118 { 119 String resource = redbackAuthorization.resource(); 120 if (resource.startsWith("{") && resource.endsWith("}") && resource.length()>2) { 121 resource = getMethodParameter(containerRequestContext, resource.substring(1,resource.length()-1)); 122 log.debug("Found resource from annotated parameter: {}",resource); 123 } 124 125 AuthorizationResult authorizationResult = null; 126 if (userObject!=null) 127 { 128 authorizationResult = securitySystem.authorize( userObject, permission, // 129 StringUtils.isBlank( resource ) // 130 ? null : resource ); 131 } 132 if ( authenticationResult != null && authorizationResult.isAuthorized() ) 133 { 134 log.debug( "isAuthorized for permission {}", permission ); 135 return; 136 } 137 else 138 { 139 if ( securitySession != null && securitySession.getUser() != null ) 140 { 141 log.debug( "user {} not authorized for permission {}", // 142 securitySession.getUser().getUsername(), // 143 permission ); 144 } 145 } 146 } 147 catch ( AuthorizationException e ) 148 { 149 log.debug( " AuthorizationException " + e.getMessage() // 150 + " checking permission " + permission, e ); 151 containerRequestContext.abortWith( Response.status( Response.Status.FORBIDDEN ).build() ); 152 return; 153 } 154 } 155 } 156 else 157 { 158 if ( securitySession != null && securitySession.getUser() != null ) 159 { 160 log.debug( "user {} not authenticated, but permissions are set", securitySession.getUser().getUsername() ); 161 } 162 containerRequestContext.abortWith( Response.status( Response.Status.FORBIDDEN ).build() ); 163 return; 164 } 165 } 166 else 167 { 168 // The noPermission is only valid, if the user is authenticated 169 if ( redbackAuthorization.noPermission() ) 170 { 171 AuthenticationResult authenticationResult = getAuthenticationResult( containerRequestContext, httpAuthenticator, request ); 172 if (authenticationResult!=null && authenticationResult.isAuthenticated()) 173 { 174 log.debug( "Path {} doesn't need special permission. User authenticated.", requestPath ); 175 return; 176 } else { 177 log.debug( "Path {} is protected and needs authentication. User not authenticated.", requestPath ); 178 containerRequestContext.abortWith( Response.status( Response.Status.UNAUTHORIZED ) 179 .header( "WWW-Authenticate", "Bearer realm=\""+getAuthenticationRealm()+"\"" ) 180 .build() ); 181 return; 182 } 183 } 184 log.warn( "No permissions defined for the REST method and noPermission=false" ); 185 containerRequestContext.abortWith( Response.status( Response.Status.FORBIDDEN ).build() ); 186 return; 187 } 188 } 189 190 log.warn( "http path {} doesn't contain any informations regarding permissions ", // 191 containerRequestContext.getUriInfo().getRequestUri() ); 192 // here we failed to authenticate so 403 as there is no detail on karma for this 193 // it must be marked as it's exposed 194 containerRequestContext.abortWith( Response.status( Response.Status.FORBIDDEN ).build() ); 195 196 } 197 198 /* 199 * Extracts a request parameter value from the message. Currently checks only path and query parameter. 200 */ 201 private String getMethodParameter( final ContainerRequestContext requestContext, final String parameterName ) { 202 UriInfo uriInfo = requestContext.getUriInfo( ); 203 if (uriInfo.getPathParameters().containsKey( parameterName )) { 204 return uriInfo.getPathParameters( ).get( parameterName ).get( 0 ); 205 } else if (uriInfo.getQueryParameters().containsKey( parameterName )) { 206 return uriInfo.getQueryParameters( ).get( parameterName ).get( 0 ); 207 } 208 return ""; 209 } 210 211 212 public String getAuthenticationRealm( ) 213 { 214 return authenticationRealm; 215 } 216 217 public void setAuthenticationRealm( String authenticationRealm ) 218 { 219 this.authenticationRealm = authenticationRealm; 220 } 221}