예제 #1
0
def get_web_service_config(args: argparse.Namespace):
    """
    Gets url, username, and password for the Tortuga web service.

    :param argparse.Namespace args: argparse Namespace instance

    :return tuple: (url, username, password)

    """
    username = password = url = None

    cfg_file = os.path.join(os.path.expanduser('~'), '.local', 'tortuga',
                            'credentials')

    if os.path.exists(cfg_file):
        cfg = configparser.ConfigParser()

        cfg.read(cfg_file)

        username = cfg.get('default', 'username') \
            if cfg.has_section('default') and \
            cfg.has_option('default', 'username') else None

        password = cfg.get('default', 'password') \
            if cfg.has_section('default') and \
            cfg.has_option('default', 'password') else None

        url = cfg.get('default', 'url') \
            if cfg.has_section('default') and \
            cfg.has_option('default', 'url') else None

    if args.url:
        url = args.url
    elif os.getenv('TORTUGA_WS_URL'):
        url = os.getenv('TORTUGA_WS_URL')

    if args.username:
        username = args.username
    elif os.getenv('TORTUGA_WS_USERNAME'):
        username = os.getenv('TORTUGA_WS_USERNAME')

    if args.password:
        password = args.password
    elif os.getenv('TORTUGA_WS_PASSWORD'):
        password = os.getenv('TORTUGA_WS_PASSWORD')

    #
    # CLI arguments should override the environment variable
    #
    if os.getenv('TORTUGA_WS_NO_VERIFY'):
        verify = False
    else:
        verify = args.verify

    if username is None and password is None:
        cm = ConfigManager()
        username = cm.getCfmUser()
        password = cm.getCfmPassword()

    return url, username, password, verify
예제 #2
0
class TortugaWsApi(RestApiClient):
    """
    Base tortuga ws api class.

    """
    def __init__(self, username: Optional[str] = None,
                 password: Optional[str] = None,
                 baseurl: Optional[str] = None,
                 verify: bool = True):

        self._cm = ConfigManager()

        if not baseurl:
            baseurl = '{}://{}:{}'.format(
                self._cm.getAdminScheme(),
                self._cm.getInstaller(),
                self._cm.getAdminPort()
            )

        if username is None and password is None:
            logger.debug('Using built-in user credentials')
            username = self._cm.getCfmUser()
            password = self._cm.getCfmPassword()

        super().__init__(username, password, baseurl, verify)

        self.baseurl = '{}/{}'.format(self.baseurl, WS_API_VERSION)

    def process_response(self, response: requests.Response):
        check_status(response.headers)

        return super().process_response(response)
예제 #3
0
파일: client.py 프로젝트: dtougas/tortuga
class TortugaWsApiClient:
    """
    Tortuga ws api client class.

    """
    def __init__(self, endpoint: str,
                 username: Optional[str] = None,
                 password: Optional[str] = None,
                 base_url: Optional[str] = None,
                 verify: bool = True) -> None:

        self._cm = ConfigManager()

        if not base_url:
            base_url = '{}://{}:{}'.format(
                self._cm.getAdminScheme(),
                self._cm.getInstaller(),
                self._cm.getAdminPort()
            )

        if username is None and password is None:
            logger.debug('Using built-in user credentials')
            username = self._cm.getCfmUser()
            password = self._cm.getCfmPassword()

        self._client = RestApiClient(
            username=username,
            password=password,
            baseurl=base_url,
            verify=verify
        )

        self._client.baseurl = '{}/{}/{}/'.format(base_url, WS_API_VERSION,
                                                  endpoint)

    def _build_query_string(self, params: dict) -> str: \
            # pylint: disable=no-self-use
        return '&'.join([f'{k}={v}' for k, v in params.items()])

    def list(self, **params) -> list:
        path = '/'
        query_string = self._build_query_string(params)
        if query_string:
            path += '?{}'.format(query_string)

        return self._client.get(path)

    def get(self, id_: str) -> dict:
        path = '/{}'.format(id_)

        return self._client.get(path)
예제 #4
0
class AuthManager(TortugaObjectManager, Singleton):
    def __init__(self):
        super(AuthManager, self).__init__()

        self._configManager = ConfigManager()

        self.__principals = {}

        self.__loadPrincipals()

    def cryptPassword(self, cleartext, salt="$1$"):         \
            # pylint: disable=no-self-use
        """ Return crypted password.... """
        return crypt.crypt(cleartext, salt)

    def reloadPrincipals(self):
        """ This is used to reload the principals in auth manager """
        self.__principals.clear()

        self.__loadPrincipals()

    def __loadPrincipals(self):
        """ Load principals for config manager and datastore """
        # Create builtin cfm principal
        cfmUser = AuthPrincipal(
            self._configManager.getCfmUser(),
            self.cryptPassword(self._configManager.getCfmPassword()),
            {'roles': 'cfm'})

        # Add cfm user
        self.__principals[cfmUser.getName()] = cfmUser

        # Add users from DB
        if self._configManager.isInstaller():
            for admin in getAdminApi().getAdminList():
                self.__principals[admin.getUsername()] = AuthPrincipal(
                    admin.getUsername(),
                    admin.getPassword(),
                    attributeDict={'id': admin.getId()})

    def getPrincipal(self, username, password):
        """ Get a principal based on a username and password """
        principal = self.__principals.get(username)
        if principal and principal.getPassword() == crypt.crypt(
                password, principal.getPassword()):
            return principal

        return None
예제 #5
0
class TortugaWsApiClient:
    """
    Tortuga ws api client class.

    """
    def __init__(self,
                 endpoint: str,
                 token: Optional[str] = None,
                 username: Optional[str] = None,
                 password: Optional[str] = None,
                 base_url: Optional[str] = None,
                 verify: bool = True) -> None:

        self._cm = ConfigManager()

        if not base_url:
            base_url = '{}://{}:{}'.format(self._cm.getAdminScheme(),
                                           self._cm.getInstaller(),
                                           self._cm.getAdminPort())

        if not token:
            if username is None and password is None:
                logger.debug('Using built-in user credentials')
                username = self._cm.getCfmUser()
                password = self._cm.getCfmPassword()

        self._client = RestApiClient(token=token,
                                     username=username,
                                     password=password,
                                     baseurl=base_url,
                                     verify=verify)

        self._client.baseurl = '{}/{}/{}/'.format(base_url, WS_API_VERSION,
                                                  endpoint)

    def _build_query_string(self, params: dict) -> str:
        return '&'.join([f'{k}={v}' for k, v in params.items()])

    def list(self, **params) -> list:
        path = '/'
        query_string = self._build_query_string(params)
        if query_string:
            path += '?{}'.format(query_string)

        return self._client.get(path)

    def get(self, id_: str) -> dict:
        path = '/{}'.format(id_)

        return self._client.get(path)

    def post(self, data: dict) -> dict:
        path = '/'

        return self._client.post(path, data=data)

    def put(self, data: dict) -> dict:
        if not data or 'id' not in data.keys():
            raise Exception('Object does not have an id field')
        id_ = data['id']
        if not id_:
            raise Exception('Object id is invalid')
        path = '/{}'.format(id_)

        return self._client.put(path, data=data)

    def delete(self, id_: str):
        path = '/{}'.format(id_)

        return self._client.delete(path)
예제 #6
0
class TortugaScriptConfig(Config):
    AUTH_METHOD_PASSWORD = '******'
    AUTH_METHOD_TOKEN = 'token'

    NAVOPS_CLI = '/opt/navops-launch/bin/navopsctl'
    DEFAULT_FILENAME = os.path.join(os.path.expanduser('~'), '.tortuga',
                                    'config')

    def __init__(self, **kwargs):
        #
        # Internal properties
        #
        self._filename = None
        self._cm = ConfigManager()

        #
        # Determine the username/password to use as default
        #
        default_username = self._cm.getCfmUser()
        default_password = self._cm.getCfmPassword()
        if default_password == 'not-set':
            default_username = None
            default_password = None

        #
        # Check for default navops cli location
        #
        default_navops_cli = self.NAVOPS_CLI
        if not os.path.exists(default_navops_cli):
            default_navops_cli = None

        #
        # Configuration settings
        #
        self.url = kwargs.get('url', self._cm.getInstallerUrl())
        self.token = kwargs.get('token', None)
        self.navops_cli = kwargs.get('navops_cli', default_navops_cli)
        self.username = kwargs.get('username', default_username)
        self.password = kwargs.get('password', default_password)
        self.verify = kwargs.get('verify', True)

    def _load_from_environment(self):
        if os.getenv('TORTUGA_WS_URL'):
            self.url = os.getenv('TORTUGA_WS_URL')
        if os.getenv('TORTUGA_WS_USERNAME'):
            self.username = os.getenv('TORTUGA_WS_USERNAME')
        if os.getenv('TORTUGA_WS_PASSWORD'):
            self.password = os.getenv('TORTUGA_WS_PASSWORD')
        if os.getenv('TORTUGA_WS_TOKEN'):
            self.token = os.getenv('TORTUGA_WS_TOKEN')
        if os.getenv('TORTUGA_WS_NO_VERIFY'):
            self.verify = False

    @classmethod
    def load(cls, filename: str = None) -> 'TortugaScriptConfig':
        #
        # If a file name is provided, then we try to load that first
        #
        if filename:
            config = cls._load_from_file(filename)
        #
        # If no filename is provided, then we have to figure out where to
        # get a configuration
        #
        else:
            #
            # First, check if the user has a config in their home directory
            #
            if os.path.exists(cls.DEFAULT_FILENAME):
                config = cls._load_from_file(cls.DEFAULT_FILENAME)
            #
            # Otherwise, create a new config from scratch
            #
            else:
                config = cls()
        #
        # Override the config with any settings provided from the
        # environment
        #
        config._load_from_environment()

        return config

    @classmethod
    def _load_from_file(cls, filename) -> 'TortugaScriptConfig':
        if not os.path.exists(filename):
            raise ConfigFileNotFoundException(
                'Config file not found: {}'.format(filename))

        with open(filename) as fp:
            try:
                config_data = json.load(fp)
            except json.JSONDecodeError:
                raise ConfigException(
                    'Invalid config file: {}'.format(filename))

        try:
            unmarshalled = TortugaScriptConfigSchema().load(config_data)
        except ValidationError:
            raise ConfigException('Invalid config file: {}'.format(filename))

        return TortugaScriptConfig(**unmarshalled.data)

    def save(self, filename: str = None):
        if not filename:
            if self._filename:
                filename = filename
            else:
                filename = TortugaScriptConfig.DEFAULT_FILENAME

        if not os.path.exists(filename):
            os.makedirs(os.path.dirname(filename), exist_ok=True, mode=0o700)

        marshalled = TortugaScriptConfigSchema().dump(self)
        with open(filename, 'w') as fp:
            json.dump(marshalled.data, fp, indent=4)

    def get_auth_method(self) -> str:
        """
        Gets the authentication method that should be used.

        :return str: token or password

        :raises ConfigException: if no auth method is configured

        """
        #
        # For the CFM user, always use password authentication
        #
        if self.username == self._cm.getCfmUser() and self.password:
            return self.AUTH_METHOD_PASSWORD

        #
        # For all other cases, if the navops CLI is present, or there
        # is a token, use token-based authentication
        #
        if self.navops_cli or self.token:
            return self.AUTH_METHOD_TOKEN

        #
        # Otherwise, fall back to password authentication
        #
        if self.username and self.password:
            return self.AUTH_METHOD_PASSWORD

        raise ConfigException('Authentication required. Use "tortuga login".')

    def get_token(self) -> str:
        """
        Gets the current authentication token.

        :return str: the token

        :raises ConfigException: if token is unavailable

        """
        if self.navops_cli:
            try:
                return self._get_navops_token()
            except ConfigException:
                pass

        if self.token:
            return self.token

        raise ConfigException('Authentication required. Use "tortuga login".')

    def _get_navops_token(self) -> str:
        cmd = '{} token'.format(self.navops_cli)

        try:
            p = executeCommand(cmd)
        except Exception as ex:
            logger.info(str(ex))
            raise ConfigException(str(ex))

        if p.getExitStatus() != 0:
            raise ConfigException(p.getStdErr())

        return p.getStdOut().decode()
예제 #7
0
class TortugaWsApi:
    """
    Base tortuga ws api class.
    """
    def __init__(self, username=None, password=None):
        self._logger = logging.getLogger('tortuga.wsapi.{0}'.format(
            self.__class__.__name__))
        self._logger.addHandler(logging.NullHandler())

        self._cm = ConfigManager()

        if username is None and password is None:
            self._logger.debug('[%s] Using built-in user credentials' %
                               (self.__module__))

            username = self._cm.getCfmUser()
            password = self._cm.getCfmPassword()

        self._username = username
        self._password = password
        self._sm = None

    def _getWsUrl(self, url):
        """Extract scheme and net location from provided url. Use defaults
        if none exist."""

        result = urlparse(url)

        scheme = result.scheme if result.scheme else \
            self._cm.getAdminScheme()

        netloc = result.netloc if result.netloc else \
            '{0}:{1}'.format(self._cm.getInstaller(), self._cm.getAdminPort())

        return '{0}://{1}'.format(scheme, netloc)

    def _getSessionManager(self):
        if not self._sm:
            self._sm = sessionManager.createSession()
        return self._sm

    def getLogger(self):
        """ Get logger for this class. """
        return self._logger

    def getConfigManager(self):
        """ Return configmanager reference """
        return self._cm

    def sendSessionRequest(self,
                           url,
                           method='GET',
                           contentType='application/json',
                           data='',
                           acceptType='application/json'):
        """
        Send authorized session request

        Raises:
            UserNotAuthorized
        """

        sm = self._getSessionManager()

        if not sm.hasSession():
            if self._username is None:
                raise UserNotAuthorized('Username not supplied')

            if self._password is None:
                raise UserNotAuthorized('Password not supplied')

            wsUrl = self._getWsUrl(url)

            # establishSession() sets the 'wsUrl' so the explicit call
            # to setHost() is not required
            sm.establishSession(wsUrl, self._username, self._password)

        return sm.sendRequest(url,
                              method,
                              contentType,
                              data,
                              acceptType=acceptType)

    def sendRequest(self,
                    url,
                    method='GET',
                    contentType='application/json',
                    data='',
                    acceptType='application/json'):
        """ Send unauthorized request. """

        sm = self._getSessionManager()

        # Because there's no call to establishSession(), explicitly call
        # setHost()
        sm.setHost(self._getWsUrl(url))

        return self._getSessionManager().sendRequest(url, method, contentType,
                                                     data, acceptType)
예제 #8
0
class AuthManager(TortugaObjectManager):
    def __init__(self, *, session: Session):
        super(AuthManager, self).__init__()

        self.session = session

        self._configManager = ConfigManager()

        self.__principals = {}

        self.__loadPrincipals()

    def cryptPassword(self, cleartext):         \
            # pylint: disable=no-self-use
        """
        Return crypted password
        """

        return pbkdf2_sha256.hash(cleartext)

    def reloadPrincipals(self):
        """
        This is used to reload the principals in auth manager
        """

        self.__principals.clear()

        self.__loadPrincipals()

    def __loadPrincipals(self):
        """
        Load principals for config manager and datastore
        """
        from tortuga.admin.api import AdminApi

        # Create built-in cfm principal
        cfmUser = AuthPrincipal(
            self._configManager.getCfmUser(),
            self.cryptPassword(self._configManager.getCfmPassword()),
            {'roles': 'cfm'})

        # Add cfm user
        self.__principals[cfmUser.get_name()] = cfmUser

        # Add users from DB
        if self._configManager.isInstaller():
            for admin in AdminApi().getAdminList(self.session):
                self.__principals[admin.getUsername()] = AuthPrincipal(
                    admin.getUsername(),
                    admin.getPassword(),
                    attributes={'id': admin.getId()})

    def get_principal(self, username: str) -> AuthPrincipal:
        """
        Get a principal by username.

        :param str username:   the username of the principal to lookup

        :return AuthPrincipal: the principal, if found, otherwise None

        """
        principal: AuthPrincipal = self.__principals.get(username)
        if not principal:
            principal = None

        return principal