def export(self, body, chain, key, options, **kwargs): """ Generates a Java Keystore :param key: :param chain: :param body: :param options: :param kwargs: """ if self.get_option('passphrase', options): passphrase = self.get_option('passphrase', options) else: passphrase = get_psuedo_random_string() if self.get_option('alias', options): alias = self.get_option('alias', options) else: alias = "blah" with mktemppath() as jks_tmp: if not key: raise Exception("Unable to export, no private key found.") create_truststore(body, chain, jks_tmp, alias, passphrase) create_keystore(body, jks_tmp, key, alias, passphrase) with open(jks_tmp, 'rb') as f: raw = f.read() return "jks", passphrase, raw
def export(self, body, chain, key, options, **kwargs): """ Generates a Java Truststore :param key: :param chain: :param body: :param options: :param kwargs: """ if self.get_option('alias', options): alias = self.get_option('alias', options) else: alias = "blah" if self.get_option('passphrase', options): passphrase = self.get_option('passphrase', options) else: passphrase = get_psuedo_random_string() with mktemppath() as jks_tmp: create_truststore(body, chain, jks_tmp, alias, passphrase) with open(jks_tmp, 'rb') as f: raw = f.read() return "jks", passphrase, raw
def export(self, body, chain, key, options, **kwargs): """ Generates a Java Keystore or Truststore :param key: :param chain: :param body: :param options: :param kwargs: """ if self.get_option('passphrase', options): passphrase = self.get_option('passphrase', options) else: passphrase = get_psuedo_random_string() if self.get_option('alias', options): alias = self.get_option('alias', options) else: alias = "blah" type = self.get_option('type', options) with mktemppath() as output_tmp: if type == 'PKCS12 (.p12)': create_pkcs12(body, output_tmp, key, alias, passphrase) extension = "p12" else: raise Exception( "Unable to export, unsupported type: {0}".format(type)) with open(output_tmp, 'rb') as f: raw = f.read() return extension, passphrase, raw
def _update_user(self, roles): """ create or update a local user instance. """ # try to get user from local database user = user_service.get_by_email(self.ldap_principal) # create them a local account if not user: user = user_service.create( self.ldap_username, get_psuedo_random_string(), self.ldap_principal, True, '', # thumbnailPhotoUrl list(roles) ) else: # we add 'lemur' specific roles, so they do not get marked as removed for ur in user.roles: if ur.authority_id: roles.add(ur) # update any changes to the user user_service.update( user.id, self.ldap_username, self.ldap_principal, user.active, user.profile_picture, list(roles) ) return user
def export(self, body, chain, key, options, **kwargs): """ Generates a Java Keystore or Truststore :param key: :param chain: :param body: :param options: :param kwargs: """ if self.get_option('passphrase', options): passphrase = self.get_option('passphrase', options) else: passphrase = get_psuedo_random_string() if self.get_option('alias', options): alias = self.get_option('alias', options) else: alias = "blah" type = self.get_option('type', options) with mktemppath() as output_tmp: if type == 'PKCS12 (.p12)': create_pkcs12(body, chain, output_tmp, key, alias, passphrase) extension = "p12" else: raise Exception("Unable to export, unsupported type: {0}".format(type)) with open(output_tmp, 'rb') as f: raw = f.read() return extension, passphrase, raw
def update_user(user, profile, roles): """Updates user with current profile information and associated roles. :param user: :param profile: :param roles: """ # if we get an sso user create them an account if not user: user = user_service.create( profile['email'], get_psuedo_random_string(), profile['email'], True, profile.get('thumbnailPhotoUrl'), roles ) else: # we add 'lemur' specific roles, so they do not get marked as removed for ur in user.roles: if not ur.third_party: roles.append(ur) # update any changes to the user user_service.update( user.id, profile['email'], profile['email'], True, profile.get('thumbnailPhotoUrl'), # profile isn't google+ enabled roles )
def _update_user(self, roles): """ create or update a local user instance. """ # try to get user from local database user = user_service.get_by_email(self.ldap_principal) # create them a local account if not user: user = user_service.create( self.ldap_username, get_psuedo_random_string(), self.ldap_principal, True, '', # thumbnailPhotoUrl list(roles)) else: # we add 'lemur' specific roles, so they do not get marked as removed for ur in user.roles: if not ur.third_party: roles.add(ur) # update any changes to the user user_service.update(user.id, self.ldap_username, self.ldap_principal, user.active, user.profile_picture, list(roles)) return user
def process_options(options): """ Processes and maps the incoming issuer options to fields/options that verisign understands :param options: :return: dict or valid verisign options """ data = { 'challenge': get_psuedo_random_string(), 'serverType': 'Apache', 'certProductType': 'Server', 'firstName': current_app.config.get("VERISIGN_FIRST_NAME"), 'lastName': current_app.config.get("VERISIGN_LAST_NAME"), 'signatureAlgorithm': 'sha256WithRSAEncryption', 'email': current_app.config.get("VERISIGN_EMAIL") } data['subject_alt_names'] = ",".join(get_additional_names(options)) if options.get('validity_end'): period = get_default_issuance(options) data['specificEndDate'] = options['validity_end'].format("MM/DD/YYYY") data['validityPeriod'] = period elif options.get('validity_years'): if options['validity_years'] in [1, 2]: data['validityPeriod'] = str(options['validity_years']) + 'Y' else: raise Exception( "Verisign issued certificates cannot exceed two years in validity" ) return data
def process_options(options): """ Processes and maps the incoming issuer options to fields/options that verisign understands :param options: :return: dict or valid verisign options """ data = { 'challenge': get_psuedo_random_string(), 'serverType': 'Apache', 'certProductType': 'Server', 'firstName': current_app.config.get("VERISIGN_FIRST_NAME"), 'lastName': current_app.config.get("VERISIGN_LAST_NAME"), 'signatureAlgorithm': 'sha256WithRSAEncryption', 'email': current_app.config.get("VERISIGN_EMAIL") } if options.get('validity_end'): end_date, period = get_default_issuance(options) data['specificEndDate'] = str(end_date) data['validityPeriod'] = period elif options.get('validity_years'): if options['validity_years'] in [1, 2]: data['validityPeriod'] = str(options['validity_years']) + 'Y' else: raise Exception("Verisign issued certificates cannot exceed two years in validity") return data
def get(self): active_providers = [] for provider in current_app.config.get("ACTIVE_PROVIDERS", []): provider = provider.lower() if provider == "google": active_providers.append( { "name": "google", "clientId": current_app.config.get("GOOGLE_CLIENT_ID"), "url": api.url_for(Google), } ) elif provider == "ping": active_providers.append( { "name": current_app.config.get("PING_NAME"), "url": current_app.config.get("PING_REDIRECT_URI"), "redirectUri": current_app.config.get("PING_REDIRECT_URI"), "clientId": current_app.config.get("PING_CLIENT_ID"), "responseType": "code", "scope": ["openid", "email", "profile", "address"], "scopeDelimiter": " ", "authorizationEndpoint": current_app.config.get( "PING_AUTH_ENDPOINT" ), "requiredUrlParams": ["scope"], "type": "2.0", } ) elif provider == "oauth2": active_providers.append( { "name": current_app.config.get("OAUTH2_NAME"), "url": current_app.config.get("OAUTH2_REDIRECT_URI"), "redirectUri": current_app.config.get("OAUTH2_REDIRECT_URI"), "clientId": current_app.config.get("OAUTH2_CLIENT_ID"), "responseType": "code", "scope": ["openid", "email", "profile", "groups"], "scopeDelimiter": " ", "authorizationEndpoint": current_app.config.get( "OAUTH2_AUTH_ENDPOINT" ), "requiredUrlParams": ["scope", "state", "nonce"], "state": "STATE", "nonce": get_psuedo_random_string(), "type": "2.0", } ) return active_providers
def process_options(options): """ Processes and maps the incoming issuer options to fields/options that verisign understands :param options: :return: dict or valid verisign options """ # if there is a config variable with VERISIGN_PRODUCT_<upper(authority.name)> take the value as Cert product-type # else default to "Server", to be compatoible with former versions authority = options.get("authority").name.upper() product_type = current_app.config.get( "VERISIGN_PRODUCT_{0}".format(authority), "Server") data = { "challenge": get_psuedo_random_string(), "serverType": "Apache", "certProductType": product_type, "firstName": current_app.config.get("VERISIGN_FIRST_NAME"), "lastName": current_app.config.get("VERISIGN_LAST_NAME"), "signatureAlgorithm": "sha256WithRSAEncryption", "email": current_app.config.get("VERISIGN_EMAIL"), "ctLogOption": current_app.config.get("VERISIGN_CS_LOG_OPTION", "public"), } data["subject_alt_names"] = ",".join(get_additional_names(options)) if options.get("validity_end"): # VeriSign (Symantec) only accepts strictly smaller than 2 year end date if options.get("validity_end") < arrow.utcnow().shift(years=2, days=-1): period = get_default_issuance(options) data["specificEndDate"] = options["validity_end"].format( "MM/DD/YYYY") data["validityPeriod"] = period else: # allowing Symantec website setting the end date, given the validity period data["validityPeriod"] = str(get_default_issuance(options)) options.pop("validity_end", None) elif options.get("validity_years"): if options["validity_years"] in [1, 2]: data["validityPeriod"] = str(options["validity_years"]) + "Y" else: raise Exception( "Verisign issued certificates cannot exceed two years in validity" ) return data
def process_options(options): """ Processes and maps the incoming issuer options to fields/options that verisign understands :param options: :return: dict or valid verisign options """ data = { 'challenge': get_psuedo_random_string(), 'serverType': 'Apache', 'certProductType': 'Server', 'firstName': current_app.config.get("VERISIGN_FIRST_NAME"), 'lastName': current_app.config.get("VERISIGN_LAST_NAME"), 'signatureAlgorithm': 'sha256WithRSAEncryption', 'email': current_app.config.get("VERISIGN_EMAIL"), 'ctLogOption': current_app.config.get("VERISIGN_CS_LOG_OPTION", "public"), } data['subject_alt_names'] = ",".join(get_additional_names(options)) if options.get('validity_end') > arrow.utcnow().replace(years=2): raise Exception( "Verisign issued certificates cannot exceed two years in validity") if options.get('validity_end'): # VeriSign (Symantec) only accepts strictly smaller than 2 year end date if options.get('validity_end') < arrow.utcnow().replace( years=2).replace(days=-1): period = get_default_issuance(options) data['specificEndDate'] = options['validity_end'].format( "MM/DD/YYYY") data['validityPeriod'] = period else: # allowing Symantec website setting the end date, given the validity period data['validityPeriod'] = str(get_default_issuance(options)) options.pop('validity_end', None) elif options.get('validity_years'): if options['validity_years'] in [1, 2]: data['validityPeriod'] = str(options['validity_years']) + 'Y' else: raise Exception( "Verisign issued certificates cannot exceed two years in validity" ) return data
def get(self): active_providers = [] for provider in current_app.config.get("ACTIVE_PROVIDERS", []): provider = provider.lower() if provider == "google": active_providers.append({ 'name': 'google', 'clientId': current_app.config.get("GOOGLE_CLIENT_ID"), 'url': api.url_for(Google) }) elif provider == "ping": active_providers.append({ 'name': current_app.config.get("PING_NAME"), 'url': current_app.config.get('PING_REDIRECT_URI'), 'redirectUri': current_app.config.get("PING_REDIRECT_URI"), 'clientId': current_app.config.get("PING_CLIENT_ID"), 'responseType': 'code', 'scope': ['openid', 'email', 'profile', 'address'], 'scopeDelimiter': ' ', 'authorizationEndpoint': current_app.config.get("PING_AUTH_ENDPOINT"), 'requiredUrlParams': ['scope'], 'type': '2.0' }) elif provider == "oauth2": active_providers.append({ 'name': current_app.config.get("OAUTH2_NAME"), 'url': current_app.config.get('OAUTH2_REDIRECT_URI'), 'redirectUri': current_app.config.get("OAUTH2_REDIRECT_URI"), 'clientId': current_app.config.get("OAUTH2_CLIENT_ID"), 'responseType': 'code', 'scope': ['openid', 'email', 'profile', 'groups'], 'scopeDelimiter': ' ', 'authorizationEndpoint': current_app.config.get("OAUTH2_AUTH_ENDPOINT"), 'requiredUrlParams': ['scope', 'state', 'nonce'], 'state': 'STATE', 'nonce': get_psuedo_random_string(), 'type': '2.0' }) return active_providers
def downgrade(): old_key = current_app.config.get('LEMUR_ENCRYPTION_KEYS') print "Using: {0} as decryption key(s)".format(old_key) # generate aes valid key if current_app.config.get('LEMUR_ENCRYPTION_KEY'): new_key = current_app.config.get('LEMUR_ENCRYPTION_KEY') else: new_key = get_psuedo_random_string() print "Using: {0} as the encryption key, save this and place it in your configuration!".format(new_key) # migrate keys temp_keys = [] for id, private_key in conn.execute(text('select id, private_key from certificates where private_key is not null')): fernet_encrypted = StringIO(private_key).read() aes_encrypted = migrate_from_fernet(fernet_encrypted, old_key, new_key) temp_keys.append({'id': id, 'aes': aes_encrypted, 'fernet': fernet_encrypted}) op.bulk_insert(temp_key_table, temp_keys) for id, aes in conn.execute(text('select id, aes from encrypted_keys')): stmt = text("update certificates set private_key=:key where id=:id") stmt = stmt.bindparams(key=aes, id=id) print stmt op.execute(stmt) print "Certificate {0} has been migrated".format(id) # migrate role_passwords temp_passwords = [] for id, password in conn.execute(text('select id, password from roles where password is not null')): fernet_encrypted = StringIO(password).read() aes_encrypted = migrate_from_fernet(fernet_encrypted, old_key, new_key) temp_passwords.append({'id': id, 'aes': aes_encrypted, 'fernet': fernet_encrypted}) op.bulk_insert(temp_password_table, temp_passwords) for id, aes in conn.execute(text('select id, aes from encrypted_passwords')): stmt = text("update roles set password=:password where id=:id") stmt = stmt.bindparams(password=aes, id=id) op.execute(stmt) print "Password {0} has been migrated".format(id) op.drop_table('encrypted_keys') op.drop_table('encrypted_passwords')
def update_user(user, profile, roles): """Updates user with current profile information and associated roles. :param user: :param profile: :param roles: """ # if we get an sso user create them an account if not user: user = user_service.create( profile["email"], get_psuedo_random_string(), profile["email"], True, profile.get("thumbnailPhotoUrl"), roles, ) else: # we add 'lemur' specific roles, so they do not get marked as removed removed_roles = [] for ur in user.roles: if not ur.third_party: roles.append(ur) elif ur not in roles: # This is a role assigned in lemur, but not returned by sso during current login removed_roles.append(ur.name) if removed_roles: log_service.audit_log("unassign_role", user.username, f"Un-assigning roles {removed_roles}") # update any changes to the user user_service.update( user.id, profile["email"], profile["email"], True, profile.get("thumbnailPhotoUrl"), # profile isn't google+ enabled roles, ) return user
def create_certificate(self, csr, issuer_options): entity = self.get_client().factory.create('userDataVOWS') entity.caName = issuer_options['authority'].cn entity.certificateProfileName = "ENDUSER" entity.clearPwd = True entity.endEntityProfileName = "EMPTY" entity.password = get_psuedo_random_string() entity.status = 10 # STATUS_NEW subjectDN = [] if 'country' in issuer_options and issuer_options['country']: subjectDN.append("c=" + issuer_options['country']) if 'state' in issuer_options and issuer_options['state']: subjectDN.append("s=" + issuer_options['state']) if 'location' in issuer_options and issuer_options['location']: subjectDN.append("l=" + issuer_options['location']) if 'organization' in issuer_options and issuer_options['organization']: subjectDN.append("o=" + issuer_options['organization']) if 'organizationalUnit' in issuer_options and issuer_options['organizationalUnit']: subjectDN.append("ou=" + issuer_options['organizationalUnit']) if 'commonName' in issuer_options and issuer_options['commonName']: subjectDN.append("cn=" + issuer_options['commonName']) entity.subjectDN = ", ".join(subjectDN) entity.startTime = self.to_date_format(issuer_options['validityStart']) entity.endTime = self.to_date_format(issuer_options['validityEnd']) entity.tokenType = "USERGENERATED" entity.username = issuer_options['commonName'] entity.email = issuer_options['owner'] self.get_client().service.editUser(entity) response = self.get_client().service.pkcs10Request(entity.username, entity.password, csr, None, 'CERTIFICATE') full_chain = self.get_client().service.getLastCAChain(issuer_options['authority'].cn) # Omit the root cert from the chain server_chain = full_chain[:-1] return self.to_pem(response.data), self.to_pem_chain(server_chain)
def export(self, body, chain, key, options, **kwargs): """ Generates a PKCS#12 archive. :param key: :param chain: :param body: :param options: :param kwargs: """ if self.get_option('passphrase', options): passphrase = self.get_option('passphrase', options) else: passphrase = get_psuedo_random_string() if self.get_option('alias', options): alias = self.get_option('alias', options) else: alias = common_name(parse_certificate(body)) type = self.get_option('type', options) with mktemppath() as output_tmp: if type == 'PKCS12 (.p12)': if not key: raise Exception("Private Key required by {0}".format(type)) create_pkcs12(body, chain, output_tmp, key, alias, passphrase) extension = "p12" else: raise Exception( "Unable to export, unsupported type: {0}".format(type)) with open(output_tmp, 'rb') as f: raw = f.read() return extension, passphrase, raw
def process_options(options): """ Processes and maps the incoming issuer options to fields/options that verisign understands :param options: :return: dict or valid verisign options """ data = { 'challenge': get_psuedo_random_string(), 'serverType': 'Apache', 'certProductType': 'Server', 'firstName': current_app.config.get("VERISIGN_FIRST_NAME"), 'lastName': current_app.config.get("VERISIGN_LAST_NAME"), 'signatureAlgorithm': 'sha256WithRSAEncryption', 'email': current_app.config.get("VERISIGN_EMAIL") } if options.get('validityEnd'): end_date, period = get_default_issuance(options) data['specificEndDate'] = end_date data['validityPeriod'] = period return data
def post(self): self.reqparse.add_argument('clientId', type=str, required=True, location='json') self.reqparse.add_argument('redirectUri', type=str, required=True, location='json') self.reqparse.add_argument('code', type=str, required=True, location='json') args = self.reqparse.parse_args() # take the information we have received from the provider to create a new request params = { 'client_id': args['clientId'], 'grant_type': 'authorization_code', 'scope': 'openid email profile address', 'redirect_uri': args['redirectUri'], 'code': args['code'] } # you can either discover these dynamically or simply configure them access_token_url = current_app.config.get('PING_ACCESS_TOKEN_URL') user_api_url = current_app.config.get('PING_USER_API_URL') # the secret and cliendId will be given to you when you signup for the provider basic = base64.b64encode('{0}:{1}'.format(args['clientId'], current_app.config.get("PING_SECRET"))) headers = {'Authorization': 'Basic {0}'.format(basic)} # exchange authorization code for access token. r = requests.post(access_token_url, headers=headers, params=params) id_token = r.json()['id_token'] access_token = r.json()['access_token'] # fetch token public key header_data = fetch_token_header(id_token) jwks_url = current_app.config.get('PING_JWKS_URL') # retrieve the key material as specified by the token header r = requests.get(jwks_url) for key in r.json()['keys']: if key['kid'] == header_data['kid']: secret = get_rsa_public_key(key['n'], key['e']) algo = header_data['alg'] break else: return dict(message='Key not found'), 403 # validate your token based on the key it was signed with try: jwt.decode(id_token, secret, algorithms=[algo], audience=args['clientId']) except jwt.DecodeError: return dict(message='Token is invalid'), 403 except jwt.ExpiredSignatureError: return dict(message='Token has expired'), 403 except jwt.InvalidTokenError: return dict(message='Token is invalid'), 403 user_params = dict(access_token=access_token, schema='profile') # retrieve information about the current user. r = requests.get(user_api_url, params=user_params) profile = r.json() user = user_service.get_by_email(profile['email']) # update their google 'roles' roles = [] for group in profile['googleGroups']: role = role_service.get_by_name(group) if not role: role = role_service.create(group, description='This is a google group based role created by Lemur') roles.append(role) # if we get an sso user create them an account # we still pick a random password in case sso is down if not user: # every user is an operator (tied to a default role) if current_app.config.get('LEMUR_DEFAULT_ROLE'): v = role_service.get_by_name(current_app.config.get('LEMUR_DEFAULT_ROLE')) if v: roles.append(v) user = user_service.create( profile['email'], get_psuedo_random_string(), profile['email'], True, profile.get('thumbnailPhotoUrl'), roles ) else: # we add 'lemur' specific roles, so they do not get marked as removed for ur in user.roles: if ur.authority_id: roles.append(ur) # update any changes to the user user_service.update( user.id, profile['email'], profile['email'], True, profile.get('thumbnailPhotoUrl'), # incase profile isn't google+ enabled roles ) # Tell Flask-Principal the identity changed identity_changed.send(current_app._get_current_object(), identity=Identity(user.id)) return dict(token=create_token(user))
def post(self): self.reqparse.add_argument("clientId", type=str, required=True, location="json") self.reqparse.add_argument("redirectUri", type=str, required=True, location="json") self.reqparse.add_argument("code", type=str, required=True, location="json") args = self.reqparse.parse_args() # take the information we have received from the provider to create a new request params = { "client_id": args["clientId"], "grant_type": "authorization_code", "scope": "openid email profile address", "redirect_uri": args["redirectUri"], "code": args["code"], } # you can either discover these dynamically or simply configure them access_token_url = current_app.config.get("PING_ACCESS_TOKEN_URL") user_api_url = current_app.config.get("PING_USER_API_URL") # the secret and cliendId will be given to you when you signup for the provider token = "{0}:{1}".format(args["clientId"], current_app.config.get("PING_SECRET")) if sys.version_info >= (3, 0): basic = base64.b64encode(bytes(token, "utf-8")) headers = {"authorization": "basic {0}".format(basic.decode("utf-8"))} else: basic = base64.b64encode(token, "utf-8") headers = {"authorization": "basic {0}".format(basic)} # exchange authorization code for access token. r = requests.post(access_token_url, headers=headers, params=params) id_token = r.json()["id_token"] access_token = r.json()["access_token"] # fetch token public key header_data = fetch_token_header(id_token) jwks_url = current_app.config.get("PING_JWKS_URL") # retrieve the key material as specified by the token header r = requests.get(jwks_url) for key in r.json()["keys"]: if key["kid"] == header_data["kid"]: secret = get_rsa_public_key(key["n"], key["e"]) algo = header_data["alg"] break else: return dict(message="Key not found"), 403 # validate your token based on the key it was signed with try: if sys.version_info >= (3, 0): jwt.decode(id_token, secret.decode("utf-8"), algorithms=[algo], audience=args["clientId"]) else: jwt.decode(id_token, secret, algorithms=[algo], audience=args["clientId"]) except jwt.DecodeError: return dict(message="Token is invalid"), 403 except jwt.ExpiredSignatureError: return dict(message="Token has expired"), 403 except jwt.InvalidTokenError: return dict(message="Token is invalid"), 403 user_params = dict(access_token=access_token, schema="profile") # retrieve information about the current user. r = requests.get(user_api_url, params=user_params) profile = r.json() user = user_service.get_by_email(profile["email"]) metrics.send("successful_login", "counter", 1) # update their google 'roles' roles = [] for group in profile["googleGroups"]: role = role_service.get_by_name(group) if not role: role = role_service.create(group, description="This is a google group based role created by Lemur") roles.append(role) role = role_service.get_by_name(profile["email"]) if not role: role = role_service.create(profile["email"], description="This is a user specific role") roles.append(role) # if we get an sso user create them an account if not user: # every user is an operator (tied to a default role) if current_app.config.get("LEMUR_DEFAULT_ROLE"): v = role_service.get_by_name(current_app.config.get("LEMUR_DEFAULT_ROLE")) if v: roles.append(v) user = user_service.create( profile["email"], get_psuedo_random_string(), profile["email"], True, profile.get("thumbnailPhotoUrl"), roles, ) else: # we add 'lemur' specific roles, so they do not get marked as removed for ur in user.roles: if ur.authority_id: roles.append(ur) # update any changes to the user user_service.update( user.id, profile["email"], profile["email"], True, profile.get("thumbnailPhotoUrl"), # incase profile isn't google+ enabled roles, ) # Tell Flask-Principal the identity changed identity_changed.send(current_app._get_current_object(), identity=Identity(user.id)) return dict(token=create_token(user))
def post(self): self.reqparse.add_argument('clientId', type=str, required=True, location='json') self.reqparse.add_argument('redirectUri', type=str, required=True, location='json') self.reqparse.add_argument('code', type=str, required=True, location='json') args = self.reqparse.parse_args() # take the information we have received from the provider to create a new request params = { 'client_id': args['clientId'], 'grant_type': 'authorization_code', 'scope': 'openid email profile address', 'redirect_uri': args['redirectUri'], 'code': args['code'] } # you can either discover these dynamically or simply configure them access_token_url = current_app.config.get('PING_ACCESS_TOKEN_URL') user_api_url = current_app.config.get('PING_USER_API_URL') # the secret and cliendId will be given to you when you signup for the provider basic = base64.b64encode('{0}:{1}'.format( args['clientId'], current_app.config.get("PING_SECRET"))) headers = {'Authorization': 'Basic {0}'.format(basic)} # exchange authorization code for access token. r = requests.post(access_token_url, headers=headers, params=params) id_token = r.json()['id_token'] access_token = r.json()['access_token'] # fetch token public key header_data = fetch_token_header(id_token) jwks_url = current_app.config.get('PING_JWKS_URL') # retrieve the key material as specified by the token header r = requests.get(jwks_url) for key in r.json()['keys']: if key['kid'] == header_data['kid']: secret = get_rsa_public_key(key['n'], key['e']) algo = header_data['alg'] break else: return dict(message='Key not found'), 403 # validate your token based on the key it was signed with try: jwt.decode(id_token, secret, algorithms=[algo], audience=args['clientId']) except jwt.DecodeError: return dict(message='Token is invalid'), 403 except jwt.ExpiredSignatureError: return dict(message='Token has expired'), 403 except jwt.InvalidTokenError: return dict(message='Token is invalid'), 403 user_params = dict(access_token=access_token, schema='profile') # retrieve information about the current user. r = requests.get(user_api_url, params=user_params) profile = r.json() user = user_service.get_by_email(profile['email']) # update their google 'roles' roles = [] for group in profile['googleGroups']: role = role_service.get_by_name(group) if not role: role = role_service.create( group, description= 'This is a google group based role created by Lemur') roles.append(role) # if we get an sso user create them an account # we still pick a random password in case sso is down if not user: # every user is an operator (tied to a default role) if current_app.config.get('LEMUR_DEFAULT_ROLE'): v = role_service.get_by_name( current_app.config.get('LEMUR_DEFAULT_ROLE')) if v: roles.append(v) user = user_service.create(profile['email'], get_psuedo_random_string(), profile['email'], True, profile.get('thumbnailPhotoUrl'), roles) else: # we add 'lemur' specific roles, so they do not get marked as removed for ur in user.roles: if ur.authority_id: roles.append(ur) # update any changes to the user user_service.update( user.id, profile['email'], profile['email'], True, profile.get('thumbnailPhotoUrl' ), # incase profile isn't google+ enabled roles) # Tell Flask-Principal the identity changed identity_changed.send(current_app._get_current_object(), identity=Identity(user.id)) return dict(token=create_token(user))
def export(self, body, chain, key, options, **kwargs): """ Generates a Java Keystore or Truststore :param key: :param chain: :param body: :param options: :param kwargs: """ if self.get_option('passphrase', options): passphrase = self.get_option('passphrase', options) else: passphrase = get_psuedo_random_string() if self.get_option('alias', options): alias = self.get_option('alias', options) else: alias = "blah" if not key: raise Exception("Unable to export, no private key found.") with mktempfile() as cert_tmp: with open(cert_tmp, 'w') as f: f.write(body) with mktempfile() as key_tmp: with open(key_tmp, 'w') as f: f.write(key) # Create PKCS12 keystore from private key and public certificate with mktempfile() as p12_tmp: run_process([ "openssl", "pkcs12", "-export", "-name", alias, "-in", cert_tmp, "-inkey", key_tmp, "-out", p12_tmp, "-password", "pass:{}".format(passphrase) ]) # Convert PKCS12 keystore into a JKS keystore with mktemppath() as jks_tmp: run_process([ "keytool", "-importkeystore", "-destkeystore", jks_tmp, "-srckeystore", p12_tmp, "-srcstoretype", "PKCS12", "-alias", alias, "-srcstorepass", passphrase, "-deststorepass", passphrase ]) # Import leaf cert in to JKS keystore run_process([ "keytool", "-importcert", "-file", cert_tmp, "-keystore", jks_tmp, "-alias", "{0}_cert".format(alias), "-storepass", passphrase, "-noprompt" ]) # Import the entire chain for idx, cert in enumerate(split_chain(chain)): with mktempfile() as c_tmp: with open(c_tmp, 'w') as f: f.write(cert) # Import signed cert in to JKS keystore run_process([ "keytool", "-importcert", "-file", c_tmp, "-keystore", jks_tmp, "-alias", "{0}_cert_{1}".format(alias, idx), "-storepass", passphrase, "-noprompt" ]) with open(jks_tmp, 'rb') as f: raw = f.read() return "jks", passphrase, raw
def post(self): self.reqparse.add_argument('clientId', type=str, required=True, location='json') self.reqparse.add_argument('redirectUri', type=str, required=True, location='json') self.reqparse.add_argument('code', type=str, required=True, location='json') args = self.reqparse.parse_args() # take the information we have received from the provider to create a new request params = { 'grant_type': 'authorization_code', 'scope': 'openid email profile groups', 'redirect_uri': args['redirectUri'], 'code': args['code'], } # you can either discover these dynamically or simply configure them access_token_url = current_app.config.get('OAUTH2_ACCESS_TOKEN_URL') user_api_url = current_app.config.get('OAUTH2_USER_API_URL') verify_cert = current_app.config.get('OAUTH2_VERIFY_CERT', True) # the secret and cliendId will be given to you when you signup for the provider token = '{0}:{1}'.format(args['clientId'], current_app.config.get("OAUTH2_SECRET")) basic = base64.b64encode(bytes(token, 'utf-8')) headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'authorization': 'basic {0}'.format(basic.decode('utf-8')) } # exchange authorization code for access token. # Try Params first r = requests.post(access_token_url, headers=headers, params=params, verify=verify_cert) if r.status_code == 400: r = requests.post(access_token_url, headers=headers, data=params, verify=verify_cert) id_token = r.json()['id_token'] access_token = r.json()['access_token'] # fetch token public key header_data = fetch_token_header(id_token) jwks_url = current_app.config.get('OAUTH2_JWKS_URL') # retrieve the key material as specified by the token header r = requests.get(jwks_url, verify=verify_cert) for key in r.json()['keys']: if key['kid'] == header_data['kid']: secret = get_rsa_public_key(key['n'], key['e']) algo = header_data['alg'] break else: return dict(message='Key not found'), 401 # validate your token based on the key it was signed with try: if sys.version_info >= (3, 0): jwt.decode(id_token, secret.decode('utf-8'), algorithms=[algo], audience=args['clientId']) else: jwt.decode(id_token, secret, algorithms=[algo], audience=args['clientId']) except jwt.DecodeError: return dict(message='Token is invalid'), 401 except jwt.ExpiredSignatureError: return dict(message='Token has expired'), 401 except jwt.InvalidTokenError: return dict(message='Token is invalid'), 401 headers = {'authorization': 'Bearer {0}'.format(access_token)} # retrieve information about the current user. r = requests.get(user_api_url, headers=headers, verify=verify_cert) profile = r.json() user = user_service.get_by_email(profile['email']) metrics.send('successful_login', 'counter', 1) # update with roles sent by identity provider roles = [] if 'roles' in profile: for group in profile['roles']: role = role_service.get_by_name(group) if not role: role = role_service.create( group, description= 'This is a group configured by identity provider', third_party=True) if not role.third_party: role = role_service.set_third_party( role.id, third_party_status=True) roles.append(role) role = role_service.get_by_name(profile['email']) if not role: role = role_service.create( profile['email'], description='This is a user specific role', third_party=True) if not role.third_party: role = role_service.set_third_party(role.id, third_party_status=True) roles.append(role) # if we get an sso user create them an account if not user: # every user is an operator (tied to a default role) if current_app.config.get('LEMUR_DEFAULT_ROLE'): v = role_service.get_by_name( current_app.config.get('LEMUR_DEFAULT_ROLE')) if not v.third_party: v = role_service.set_third_party(v.id, third_party_status=True) if v: roles.append(v) user = user_service.create(profile['name'], get_psuedo_random_string(), profile['email'], True, profile.get('thumbnailPhotoUrl'), roles) else: # we add 'lemur' specific roles, so they do not get marked as removed for ur in user.roles: if not ur.third_party: roles.append(ur) # update any changes to the user user_service.update( user.id, profile['name'], profile['email'], True, profile.get('thumbnailPhotoUrl' ), # incase profile isn't google+ enabled roles) # Tell Flask-Principal the identity changed identity_changed.send(current_app._get_current_object(), identity=Identity(user.id)) return dict(token=create_token(user))