| # 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. |
| """ |
| Handles loading of AuthProvider for CQLSH authentication. |
| """ |
| |
| import configparser |
| import sys |
| from importlib import import_module |
| from cqlshlib.util import is_file_secure |
| |
| |
| def _warn_for_plain_text_security(config_file, provider_settings): |
| """ |
| Call when using PlainTextAuthProvider |
| check to see if password appears in the basic provider settings |
| as this is a security risk |
| |
| Will write errors to stderr |
| """ |
| if 'password' in provider_settings: |
| if not is_file_secure(config_file): |
| print("""\nWarning: Password is found in an insecure cqlshrc file. |
| The file is owned or readable by other users on the system.""", |
| end='', |
| file=sys.stderr) |
| print("""\nNotice: Credentials in the cqlshrc file is deprecated and |
| will be ignored in the future.\n |
| Please use a credentials file to |
| specify the username and password.\n""", |
| file=sys.stderr) |
| |
| |
| def load_auth_provider(config_file=None, cred_file=None, username=None, password=None): |
| """ |
| Function which loads an auth provider from available config. |
| |
| Params: |
| * config_file ..: path to cqlsh config file (usually ~/.cassandra/cqlshrc). |
| * cred_file ....: path to cqlsh credentials file (default is ~/.cassandra/credentials). |
| * username .....: override used to return PlainTextAuthProvider according to legacy case |
| * password .....: override used to return PlainTextAuthProvider according to legacy case |
| |
| Will attempt to load an auth provider from available config file, using what's found in |
| credentials file as an override. |
| |
| Config file is expected to list module name /class in the *auth_provider* |
| section for dynamic loading (which is to be of type auth_provider) |
| |
| Additional params passed to the constructor of class should be specified |
| in the *auth_provider* section and can be freely named to match |
| auth provider's expectation. |
| |
| If passed username and password these will be overridden and passed to auth provider |
| |
| None is returned if no possible auth provider is found, and no username/password can be |
| returned. If a username is found, system will assume that PlainTextAuthProvider was |
| specified |
| |
| EXAMPLE CQLSHRC: |
| # .. inside cqlshrc file |
| |
| [auth_provider] |
| module = cassandra.auth |
| classname = PlainTextAuthProvider |
| username = user1 |
| password = password1 |
| |
| if credentials file is specified put relevant properties under the class name |
| EXAMPLE |
| # ... inside credentials file for above example |
| [PlainTextAuthProvider] |
| password = password2 |
| |
| Credential attributes will override found in the cqlshrc. |
| in the above example, PlainTextAuthProvider would be used with a password of 'password2', |
| and username of 'user1' |
| """ |
| |
| def get_settings_from_config(section_name, |
| conf_file, |
| interpolation=configparser.BasicInterpolation()): |
| """ |
| Returns dict from section_name, and ini based conf_file |
| |
| * section_name ..: Section to read map of properties from (ex: [auth_provider]) |
| * conf_file .....: Ini based config file to read. Will return empty dict if None. |
| * interpolation .: Interpolation to use. |
| |
| If section is not found, or conf_file is None, function will return an empty dictionary. |
| """ |
| conf = configparser.ConfigParser(interpolation=interpolation) |
| if conf_file is None: |
| return {} |
| |
| conf.read(conf_file) |
| if section_name in conf.sections(): |
| return dict(conf.items(section_name)) |
| return {} |
| |
| def get_cred_file_settings(classname, creds_file): |
| # Since this is the credentials file we may be encountering raw strings |
| # as these are what passwords, or security tokens may inadvertently fall into |
| # we don't want interpolation to mess with them. |
| return get_settings_from_config( |
| section_name=classname, |
| conf_file=creds_file, |
| interpolation=None) |
| |
| def get_auth_provider_settings(conf_file): |
| return get_settings_from_config( |
| section_name='auth_provider', |
| conf_file=conf_file) |
| |
| def get_legacy_settings(legacy_username, legacy_password): |
| result = {} |
| if legacy_username is not None: |
| result['username'] = legacy_username |
| if legacy_password is not None: |
| result['password'] = legacy_password |
| return result |
| |
| provider_settings = get_auth_provider_settings(config_file) |
| |
| module_name = provider_settings.pop('module', None) |
| class_name = provider_settings.pop('classname', None) |
| |
| if module_name is None and class_name is None: |
| # not specified, default to plaintext auth provider |
| module_name = 'cassandra.auth' |
| class_name = 'PlainTextAuthProvider' |
| elif module_name is None or class_name is None: |
| # then this was PARTIALLY specified. |
| return None |
| |
| credential_settings = get_cred_file_settings(class_name, cred_file) |
| |
| if module_name == 'cassandra.auth' and class_name == 'PlainTextAuthProvider': |
| # merge credential settings as overrides on top of provider settings. |
| |
| # we need to ensure that password property gets "set" in all cases. |
| # this is to support the ability to give the user a prompt in other parts |
| # of the code. |
| _warn_for_plain_text_security(config_file, provider_settings) |
| ctor_args = {'password': None, |
| **provider_settings, |
| **credential_settings, |
| **get_legacy_settings(username, password)} |
| # if no username, we can't create PlainTextAuthProvider |
| if 'username' not in ctor_args: |
| return None |
| else: |
| # merge credential settings as overrides on top of provider settings. |
| ctor_args = {**provider_settings, |
| **credential_settings, |
| **get_legacy_settings(username, password)} |
| |
| # Load class definitions |
| module = import_module(module_name) |
| auth_provider_klass = getattr(module, class_name) |
| |
| # instantiate the class |
| return auth_provider_klass(**ctor_args) |