blob: 887b4f0298021088448cb3bd61bd3c1ac3c40b43 [file] [log] [blame]
/*
* 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.guacamole.rest.auth;
import com.google.common.io.BaseEncoding;
import com.google.inject.Inject;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleResourceNotFoundException;
import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.Credentials;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.GuacamoleSession;
import org.apache.guacamole.rest.APIRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A service for managing auth tokens via the Guacamole REST API.
*/
@Path("/tokens")
@Produces(MediaType.APPLICATION_JSON)
public class TokenRESTService {
/**
* Logger for this class.
*/
private static final Logger logger = LoggerFactory.getLogger(TokenRESTService.class);
/**
* Service for authenticating users and managing their Guacamole sessions.
*/
@Inject
private AuthenticationService authenticationService;
/**
* Returns the credentials associated with the given request, using the
* provided username and password.
*
* @param request
* The request to use to derive the credentials.
*
* @param username
* The username to associate with the credentials, or null if the
* username should be derived from the request.
*
* @param password
* The password to associate with the credentials, or null if the
* password should be derived from the request.
*
* @return
* A new Credentials object whose contents have been derived from the
* given request, along with the provided username and password.
*/
private Credentials getCredentials(HttpServletRequest request,
String username, String password) {
// If no username/password given, try Authorization header
if (username == null && password == null) {
String authorization = request.getHeader("Authorization");
if (authorization != null && authorization.startsWith("Basic ")) {
try {
// Decode base64 authorization
String basicBase64 = authorization.substring(6);
String basicCredentials = new String(
BaseEncoding.base64().decode(basicBase64), "UTF-8");
// Pull username/password from auth data
int colon = basicCredentials.indexOf(':');
if (colon != -1) {
username = basicCredentials.substring(0, colon);
password = basicCredentials.substring(colon + 1);
}
else
logger.debug("Invalid HTTP Basic \"Authorization\" header received.");
}
// UTF-8 support is required by the Java specification
catch (UnsupportedEncodingException e) {
throw new UnsupportedOperationException("Unexpected lack of UTF-8 support.", e);
}
}
} // end Authorization header fallback
// Build credentials
return new Credentials(username, password, request);
}
/**
* Authenticates a user, generates an auth token, associates that auth token
* with the user's UserContext for use by further requests. If an existing
* token is provided, the authentication procedure will attempt to update
* or reuse the provided token.
*
* @param username
* The username of the user who is to be authenticated.
*
* @param password
* The password of the user who is to be authenticated.
*
* @param token
* An optional existing auth token for the user who is to be
* authenticated.
*
* @param consumedRequest
* The HttpServletRequest associated with the login attempt. The
* parameters of this request may not be accessible, as the request may
* have been fully consumed by JAX-RS.
*
* @param parameters
* A MultivaluedMap containing all parameters from the given HTTP
* request. All request parameters must be made available through this
* map, even if those parameters are no longer accessible within the
* now-fully-consumed HTTP request.
*
* @return
* An authentication response object containing the possible-new auth
* token, as well as other related data.
*
* @throws GuacamoleException
* If an error prevents successful authentication.
*/
@POST
public APIAuthenticationResult createToken(@FormParam("username") String username,
@FormParam("password") String password,
@FormParam("token") String token,
@Context HttpServletRequest consumedRequest,
MultivaluedMap<String, String> parameters)
throws GuacamoleException {
// Reconstitute the HTTP request with the map of parameters
HttpServletRequest request = new APIRequest(consumedRequest, parameters);
// Build credentials from request
Credentials credentials = getCredentials(request, username, password);
// Create/update session producing possibly-new token
token = authenticationService.authenticate(credentials, token);
// Pull corresponding session
GuacamoleSession session = authenticationService.getGuacamoleSession(token);
if (session == null)
throw new GuacamoleResourceNotFoundException("No such token.");
// Build list of all available auth providers
List<DecoratedUserContext> userContexts = session.getUserContexts();
List<String> authProviderIdentifiers = new ArrayList<String>(userContexts.size());
for (UserContext userContext : userContexts)
authProviderIdentifiers.add(userContext.getAuthenticationProvider().getIdentifier());
// Return possibly-new auth token
AuthenticatedUser authenticatedUser = session.getAuthenticatedUser();
return new APIAuthenticationResult(
token,
authenticatedUser.getIdentifier(),
authenticatedUser.getAuthenticationProvider().getIdentifier(),
authProviderIdentifiers
);
}
/**
* Invalidates a specific auth token, effectively logging out the associated
* user.
*
* @param authToken
* The token being invalidated.
*
* @throws GuacamoleException
* If the specified token does not exist.
*/
@DELETE
@Path("/{token}")
public void invalidateToken(@PathParam("token") String authToken)
throws GuacamoleException {
// Invalidate session, if it exists
if (!authenticationService.destroyGuacamoleSession(authToken))
throw new GuacamoleResourceNotFoundException("No such token.");
}
}