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}