All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.cassandra.auth.CassandraLoginModule Maven / Gradle / Ivy

Go to download

The Apache Cassandra Project develops a highly scalable second-generation distributed database, bringing together Dynamo's fully distributed design and Bigtable's ColumnFamily-based data model.

There is a newer version: 5.0-rc1
Show newest version
/*
 * 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.cassandra.auth;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.*;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.exceptions.AuthenticationException;
import org.apache.cassandra.service.StorageService;

/**
 * LoginModule which authenticates a user towards the Cassandra database using
 * the internal authentication mechanism.
 */
public class CassandraLoginModule implements LoginModule
{
    private static final Logger logger = LoggerFactory.getLogger(CassandraLoginModule.class);

    // initial state
    private Subject subject;
    private CallbackHandler callbackHandler;

    // the authentication status
    private boolean succeeded = false;
    private boolean commitSucceeded = false;

    // username and password
    private String username;
    private char[] password;

    private CassandraPrincipal principal;

    /**
     * Initialize this {@code}LoginModule{@code}.
     *
     * @param subject the {@code}Subject{@code} to be authenticated. 

* @param callbackHandler a {@code}CallbackHandler{@code} for communicating * with the end user (prompting for user names and passwords, for example) * @param sharedState shared {@code}LoginModule{@code} state. This param is unused. * @param options options specified in the login {@code}Configuration{@code} for this particular * {@code}LoginModule{@code}. This param is unused */ @Override public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { this.subject = subject; this.callbackHandler = callbackHandler; } /** * Authenticate the user, obtaining credentials from the CallbackHandler * supplied in {@code}initialize{@code}. As long as the configured * {@code}IAuthenticator{@code} supports the optional * {@code}legacyAuthenticate{@code} method, it can be used here. * * @return true in all cases since this {@code}LoginModule{@code} * should not be ignored. * @exception FailedLoginException if the authentication fails. * @exception LoginException if this {@code}LoginModule{@code} is unable to * perform the authentication. */ @Override public boolean login() throws LoginException { // prompt for a user name and password if (callbackHandler == null) { logger.info("No CallbackHandler available for authentication"); throw new LoginException("Authentication failed"); } NameCallback nc = new NameCallback("username: "); PasswordCallback pc = new PasswordCallback("password: ", false); try { callbackHandler.handle(new Callback[]{nc, pc}); username = nc.getName(); char[] tmpPassword = pc.getPassword(); if (tmpPassword == null) tmpPassword = new char[0]; password = new char[tmpPassword.length]; System.arraycopy(tmpPassword, 0, password, 0, tmpPassword.length); pc.clearPassword(); } catch (IOException | UnsupportedCallbackException e) { logger.info("Unexpected exception processing authentication callbacks", e); throw new LoginException("Authentication failed"); } // verify the credentials try { authenticate(); } catch (AuthenticationException e) { // authentication failed -- clean up succeeded = false; cleanUpInternalState(); throw new FailedLoginException(e.getMessage()); } succeeded = true; return true; } private void authenticate() { if (!StorageService.instance.isAuthSetupComplete()) throw new AuthenticationException("Cannot login as server authentication setup is not yet completed"); IAuthenticator authenticator = DatabaseDescriptor.getAuthenticator(); Map credentials = new HashMap<>(); credentials.put(PasswordAuthenticator.USERNAME_KEY, username); credentials.put(PasswordAuthenticator.PASSWORD_KEY, String.valueOf(password)); AuthenticatedUser user = authenticator.legacyAuthenticate(credentials); // Only actual users should be allowed to authenticate for JMX if (user.isAnonymous() || user.isSystem()) throw new AuthenticationException(String.format("Invalid user %s", user.getName())); // The LOGIN privilege is required to authenticate - c.f. ClientState::login if (!DatabaseDescriptor.getRoleManager().canLogin(user.getPrimaryRole())) throw new AuthenticationException(user.getName() + " is not permitted to log in"); } /** * This method is called if the LoginContext's overall authentication succeeded * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules * succeeded). * * If this LoginModule's own authentication attempt succeeded (checked by * retrieving the private state saved by the {@code}login{@code} method), * then this method associates a {@code}CassandraPrincipal{@code} * with the {@code}Subject{@code}. * If this LoginModule's own authentication attempted failed, then this * method removes any state that was originally saved. * * @return true if this LoginModule's own login and commit attempts succeeded, false otherwise. * @exception LoginException if the commit fails. */ @Override public boolean commit() throws LoginException { if (!succeeded) { return false; } else { // add a Principal (authenticated identity) // to the Subject principal = new CassandraPrincipal(username); if (!subject.getPrincipals().contains(principal)) subject.getPrincipals().add(principal); cleanUpInternalState(); commitSucceeded = true; return true; } } /** * This method is called if the LoginContext's overall authentication failed. * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules * did not succeed). * * If this LoginModule's own authentication attempt succeeded (checked by * retrieving the private state saved by the {@code}login{@code} and * {@code}commit{@code} methods), then this method cleans up any state that * was originally saved. * * @return false if this LoginModule's own login and/or commit attempts failed, true otherwise. * @throws LoginException if the abort fails. */ @Override public boolean abort() throws LoginException { if (!succeeded) { return false; } else if (!commitSucceeded) { // login succeeded but overall authentication failed succeeded = false; cleanUpInternalState(); principal = null; } else { // overall authentication succeeded and commit succeeded, // but someone else's commit failed logout(); } return true; } /** * Logout the user. * * This method removes the principal that was added by the * {@code}commit{@code} method. * * @return true in all cases since this {@code}LoginModule{@code} * should not be ignored. * @throws LoginException if the logout fails. */ @Override public boolean logout() throws LoginException { subject.getPrincipals().remove(principal); succeeded = false; cleanUpInternalState(); principal = null; return true; } private void cleanUpInternalState() { username = null; if (password != null) { for (int i = 0; i < password.length; i++) password[i] = ' '; password = null; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy