/*
* #%L
* Wisdom-Framework
* %%
* Copyright (C) 2013 - 2014 Wisdom Framework
* %%
* Licensed 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.
* #L%
*/
package org.wisdom.router.security;
import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Instantiate;
import org.apache.felix.ipojo.annotations.Provides;
import org.apache.felix.ipojo.annotations.Requires;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wisdom.api.http.Result;
import org.wisdom.api.http.Results;
import org.wisdom.api.interception.Interceptor;
import org.wisdom.api.interception.RequestContext;
import org.wisdom.api.security.Authenticated;
import org.wisdom.api.security.Authenticator;
/**
* An interceptor checking that action methods are accessed while being authenticated only.
* It handles the {@link org.wisdom.api.security.Authenticated} annotation.
*/
@Component(immediate = true)
@Provides(specifications = Interceptor.class)
@Instantiate
public class AuthenticationInterceptor extends Interceptor<Authenticated> {
private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationInterceptor.class);
@Requires(optional = true)
Authenticator[] authenticators;
/**
* Intercepts the action, and checks if the current request is authenticated.
* the results depends on two factors: if the request is authenticated and the availability of authenticator.
* <p>
* If there are no authenticator, it returns a unauthorized response.
* If there are an authenticator matching the set ones (in the annotation), use it. If the authenticator cannot
* authenticate the request, it will be used to get the unauthorized response.
* If these are no authenticator matching the request, it returns an unauthorized response.
* <p>
* If the annotation does not specify the authenticator, it uses the first one. If several ones are available,
* a warning is thrown.
*
* @param configuration the authenticated annotation instance
* @param context the interception context
* @return the result
* @throws Exception if anything bad happen
*/
@Override
public Result call(Authenticated configuration, RequestContext context) throws Exception {
Authenticator authenticator = getAuthenticator(context, configuration.value());
if (authenticator != null) {
String username = authenticator.getUserName(context.context());
if (username == null) {
// We cut the interception chain on purpose.
context.context().request().setUsername(null);
return authenticator.onUnauthorized(context.context());
} else {
// Set the username.
context.context().request().setUsername(username);
return context.proceed();
}
} else {
context.context().request().setUsername(null);
// No authenticator
return Results.unauthorized();
}
}
private Authenticator getAuthenticator(RequestContext context, String value) {
if (authenticators.length == 0) {
return null;
}
if (value == null || value.length() == 0) {
// This is the default value.
if (authenticators.length > 1) {
// Default value but several authenticator
LOGGER.warn("The action {} require authentication, but does not specify the authenticator. " +
"But, several authenticators are available, picked one randomly ({})",
context.context().path(), authenticators[0]
);
}
return authenticators[0];
}
// Iterate over the authenticator to find the right one.
for (Authenticator authenticator : authenticators) {
if (authenticator.getName().equals(value)) {
return authenticator;
}
}
return null;
}
/**
* Gets the {@link org.wisdom.api.security.Authenticated} annotation class.
*
* @return the annotation
*/
@Override
public Class<Authenticated> annotation() {
return Authenticated.class;
}
}