diff --git a/awsprocesscreds/cli.py b/awsprocesscreds/cli.py index ca6c98d..ae69ebc 100644 --- a/awsprocesscreds/cli.py +++ b/awsprocesscreds/cli.py @@ -26,7 +26,7 @@ def saml(argv=None, prompter=getpass.getpass, client_creator=None, help='Your SAML username.' ) parser.add_argument( - '-p', '--provider', required=True, choices=['okta', 'adfs'], + '-p', '--provider', required=True, choices=['okta', 'adfs', 'keycloak'], help=( 'The name of your SAML provider. Currently okta and adfs ' 'form-based auth is supported.' diff --git a/awsprocesscreds/saml.py b/awsprocesscreds/saml.py index 16ed432..ccc0bc1 100644 --- a/awsprocesscreds/saml.py +++ b/awsprocesscreds/saml.py @@ -262,6 +262,14 @@ def is_suitable(self, config): return (config.get('saml_authentication_type') == 'form' and config.get('saml_provider') == 'adfs') +class KeycloakAuthenticator(GenericFormsBasedAuthenticator): + USERNAME_FIELD = 'username' + PASSWORD_FIELD = 'password' + + def is_suitable(self, config): + return (config.get('saml_authentication_type') == 'form' and + config.get('saml_provider') == 'keycloak') + class FormParser(six.moves.html_parser.HTMLParser): def __init__(self): @@ -287,6 +295,8 @@ def _dict2str(self, d): # so that the output will be suitable to be fed into an ET later. parts = [] for k, v in d.items(): + if v == None: + v = 'true' escaped_value = escape(v) # pylint: disable=deprecated-method parts.append('%s="%s"' % (k, escaped_value)) return ' '.join(sorted(parts)) @@ -308,8 +318,8 @@ def error(self, message): class SAMLCredentialFetcher(CachedCredentialFetcher): SAML_FORM_AUTHENTICATORS = { 'okta': OktaAuthenticator, - 'adfs': ADFSFormsBasedAuthenticator - + 'adfs': ADFSFormsBasedAuthenticator, + 'keycloak': KeycloakAuthenticator } def __init__(self, client_creator, provider_name, saml_config,