How to create a user with credentials in okta using python sdk - python

Is there any way to create okta user with credentials using python
Creates a user without a recovery question & answer. The new user will immediately be able to login after activation with the assigned password. This flow is common when developing a custom user registration experience.
curl -v -X POST \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: SSWS ${api_token}" \
-d '{
"profile": {
"firstName": "Isaac",
"lastName": "Brock",
"email": "isaac.brock#example.com",
"login": "isaac.brock#example.com",
"mobilePhone": "555-415-1337"
},
"credentials": {
"password" : { "value": "tlpWENT2m" }
}
}' "https://${org}.okta.com/api/v1/users?activate=false"
This one is by using Curl

Using the requests library in Python:
import requests
url = '{{org}}.okta.com/api/v1/users'
headers = {
'accept': 'application/json',
'authorization' : 'SSWS {{api_token}}',
'content-type': 'application/json'
}
body = {
'profile': {
'firstName': 'Isaac',
'lastName': 'Brock',
'email': 'isaac#{{email_suffix}}',
'login': 'isaac#{{email_suffix}}'
},
'credentials': {
'password' : { 'value': '{{password}}' }
}
}
r = requests.post(url, headers=headers, json=body)
# r.json
Using the Okta Python SDK, you need to create a client first, then call the create_user() method.
from okta import UsersClient
from okta.models.user import User
usersClient = UsersClient("https://{{org}}.okta.com", "{{api_token}}")
user = User(login='isaac#{{email_suffix}}',
email='isaac#{{email_suffix}}',
firstName='Isacc',
lastName='Brock')
user = usersClient.create_user(user, activate=False)

Based on Okta API reference guide for create user (here), And Okta Python SDK docs for create user (here):
There are two options, that worked for me:
Pass data as dict:
import asyncio
from okta.client import Client as OktaClient
from okta.models import User, UserProfile
config = {
'orgUrl': 'https://your_domain.okta.com',
'token': 'your_token'
}
async def main():
async with OktaClient(config) as client:
data = {
"profile": {
"firstName": "Omid",
"lastName": "Raha",
"email": "omid#example.com",
"login": "omid#example.com",
"mobilePhone": "{123}-{456}-{789}"
},
"credentials": {
"password": {"value": "Password!123"}
}
}
res = await client.create_user(data, {'activate': True, })
print('user created. info: {}'.format(res))
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Or pass data as okta object model:
import asyncio
from okta.client import Client as OktaClient
from okta import models
config = {
'orgUrl': 'https://your_domain.okta.com',
'token': 'your_token'
}
# Create Password
password = models.PasswordCredential({
'value': 'Password!123'
})
# Create User Credentials
user_creds = models.UserCredentials({
'password': password
})
# Create User Profile and CreateUser Request
user_profile = models.UserProfile()
user_profile.first_name = 'Omid'
user_profile.last_name = 'Raha'
user_profile.email = 'omid#example.com'
user_profile.login = 'omid#example.com'
create_user_request = models.CreateUserRequest({
'credentials': user_creds,
'profile': user_profile
})
async def main():
async with OktaClient(config) as client:
res = await client.create_user(create_user_request, {'activate': True, })
print('User created, info: {}'.format(res))
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Response (same for both methods):
User created, info:
({
'embedded': None,
'links': None,
'activated': '2022-08-17T10:05:02.000Z',
'created': '2022-08-17T10:05:02.000Z',
'credentials': {
'password': {'hash': None, 'hook': None, 'value': None},
'provider': {'name': 'OKTA', 'type': <AuthenticationProviderType.OKTA: 'OKTA'>},
'recovery_question': None
},
'id': '23d83739Ejdj',
'last_login': None,
'last_updated': '2022-08-17T10:05:02.000Z',
'password_changed': '2022-08-17T10:05:02.000Z',
'profile': {
'city': None,
'costCenter': None,
'countryCode': None,
'department': None,
'displayName': None,
'division': None,
'email': 'omid#example.com',
'employeeNumber': None,
'firstName': 'Omid',
'honorificPrefix': None,
'honorificSuffix': None,
'lastName': 'Raha',
'locale': None,
'login': 'omid#example.com',
'manager': None,
'managerId': None, 'middleName': None,
'mobilePhone': '123-456-789',
'nickName': None, 'organization': None,
'postalAddress': None,
'preferredLanguage': None,
'primaryPhone': None, 'profileUrl': None,
'secondEmail': None, 'state': None,
'streetAddress': None,
'timezone': None, 'title': None,
'userType': None, 'zipCode': None
},
'status': <UserStatus.ACTIVE: 'ACTIVE'>,
'status_changed': '2022-08-17T10:05:02.000Z',
'transitioning_to_status': None,
'type': {
'links': None,
'created': None,
'created_by': None,
'default': None,
'description': None,
'display_name': None,
'id': '83Dm32dje3',
'last_updated': None,
'last_updated_by': None,
'name': None
}
},
<okta.api_response.OktaAPIResponse object at 0x7fd5e286f130>, None
)

Related

mlflow: INVALID_PARAMETER_VALUE Parameters can not be modified after a value is set

I get the following exception raised, help me please
mlflow.exceptions.RestException: INVALID_PARAMETER_VALUE: Response:
{
'Error': {
'Code': 'ValidationError',
'Severity': None,
'Message': 'Parameters can not be modified after a value is set.',
'MessageFormat': None,
'MessageParameters': None,
'ReferenceCode': None,
'DetailsUri': None,
'Target': None,
'Details': [],
'InnerError': None,
'DebugInfo': None,
'AdditionalInfo': None
},
'Correlation': {
'operation': '13b09a7297c0e920a41eeaa728c4cb4e',
'request': 'ae8a75a01a168e65'
},
'Environment': 'westeurope',
'Location': 'westeurope',
'Time': '2022-12-22T23:37:07.4626959+00:00',
'ComponentName': 'mlflow',
'error_code': 'INVALID_PARAMETER_VALUE'
}
my code pretty much looks like this
mlflow.start_run()
mlflow.log_params({'key1':1, 'key2':2})
mlflow.log_param('key3', 3) //Exception raised
PS: the mlflow version i am using is 2.1.0

How can I use the batchupdate function of the google form api with python to modify the url and answers?

Here is an example of my form. How can I use python to modify the url and answer for the first question, as I am not familiar with using batchupdate?
I can use "get" to retrieve information from the form.
{'formId': '1q4pJMDtiLxQ2cjmLXxowqJ5VPfI68bUUo',
'info': {'title': 'PIXEL ', 'documentTitle': 'daily'},
'settings': {'quizSettings': {'isQuiz': True}},
'revisionId': '00000067',
'responderUri': 'https://docs.google.com/forms/d/e/1FAIpQLScap6ZdpOnWIyxWqZNXjlfWW9DgPe-Wv_CUtziWw/viewform',
'items': [{'itemId': '7c0ddb37', 'pageBreakItem': {}},
{'itemId': '2870b06c', 'videoItem': {'video': {'youtubeUri': 'www.youtube.com/watch?v=Lt5HqPvM-eI', 'properties': {'alignment': 'LEFT', 'width': 320}}}},
{'itemId': '381aedf6', 'questionGroupItem': {'questions': [{'questionId': '4d7f011e', 'required': True, 'rowQuestion': {'title': 'pick'}}], 'grid': {'columns': {'type': 'RADIO', 'options': [{'value': '1'}, {'value': '2'}, {'value': '3'}]}}}, 'title': 'pay'},
{'itemId': '0f9dc00b', 'title': 'number', 'questionItem': {'question': {'questionId': '39523976', 'required': True,
'grading': {'correctAnswers': {'answers': [{'value':'1115'}]}}, 'textQuestion': {}}}},
{'itemId': '0a12a42e', 'pageBreakItem': {}},
{'itemId': '19640fea', 'videoItem': {'video': {'youtubeUri': 'www.youtube.com/watch?v=Lt5HqPvM-eI', 'properties': {'alignment': 'LEFT', 'width': 320}}}},
{'itemId': '685ba545', 'questionGroupItem': {'questions': [{'questionId': '044f9f9b', 'required': True, 'rowQuestion': {'title': 'pick'}}], 'grid': {'columns': {'type': 'RADIO', 'options': [{'value': '1'}, {'value': '2'}, {'value': '3'}]}}}, 'title': 'pay'},
{'itemId': '6a9d1b88', 'title': 'number', 'questionItem': {'question': {'questionId': '2199beb0', 'required': True,
'grading': {'correctAnswers': {'answers': [{'value': '1115'}]}}, 'textQuestion': {}}}}]}
The official documentation has too few examples for me to understand how to apply it to my form.
update = {
"requests": [{
"updateItem": {
"item": {
"title": "Homework video",
"description": "Quizzes in Google Forms",
"videoItem": {
"video": {
"youtubeUri": "https://www.youtube.com/watch?v=Lt5HqPvM-eI"
}
}
},"location": {
"index": 0},
"updateMask": "description,youtubeUri"
}
}]
}
question_setting = service.forms().batchUpdate(
formId=form_id, body=update).execute()
From your following reply,
I want to update the youtubeUri item and use a new URL. How can I do this? i have two question use the video,how do i update the first question URL ?
I understood your question is as follows.
You want to update youtubeUri of 1st question in Google Forms using googleapis for python.
In this case, how about the following sample script?
Sample script:
service = # Please use your client
formId = "###" # Please set your Google Form ID.
after = "https://www.youtube.com/watch?v=###" # Please set YouTube URL you want to replace. In this sample, the existing URL is changed to this URL.
res = service.forms().get(formId=formId).execute()
itemIds = [[i, e["itemId"]] for i, e in enumerate(res.get("items")) if "videoItem" in e]
topItem = itemIds[0] # From your question, `youtubeUri` of the 1st question.
req = {
"requests": [
{
"updateItem": {
"item": {
"itemId": topItem[1],
"videoItem": {
"video": {
"youtubeUri": after,
}
},
},
"location": {"index": topItem[0]},
"updateMask": "videoItem.video.youtubeUri",
}
}
]
}
service.forms().batchUpdate(formId=formId, body=req).execute()
When this script is run, first, all items are retrieved. And, the item IDs including youtubeUri are retrieved. And, using the 1st item ID, the value of youtubeUri is changed to the value of after you set.
Note:
In this sample script, it supposes that you have already been able to get and out values to Google Form using Google Form API. Please be careful about this.
Reference:
Method: forms.batchUpdate

Djangosaml2 the use of metadata

I'm manage to integrate SAML authentication in my Django application using the package Djangosaml2 and Pysaml2 with Azure as IdP provider.
everything is working properly I can login with SAML and log out.
What I don't understand is what is the use of having a metadata at the url https://panda.company.com/saml/metadata and what is the use of having a url https://panda.company.com/saml2/ls/ ?
Because with just the remote_metadata.xml provided by Azure is enough to login and logout.
SAML_CONFIG = {
'xmlsec_binary': '/usr/bin/xmlsec1',
'name': 'CloudBolt SP',
'entityid': 'https://panda.company.com/',
'service': {
'sp': {
'want_assertions_signed': False,
'want_response_signed': False,
'allow_unsolicited': True,
'endpoints': {
'assertion_consumer_service': [
('https://panda.company.com/saml2/acs/', saml2.BINDING_HTTP_POST),
],
'single_logout_service': [
('https://panda.company.com/saml2/ls/', saml2.BINDING_HTTP_REDIRECT),
],
},
'required_attributes': ['email'],
},
},
'debug': 1,
'key_file': os.path.join(SAML2_DIR, 'saml.key'), # private part
'cert_file': os.path.join(SAML2_DIR, 'saml.crt'), # public part
'allow_unknown_attributes': True,
'attribute_map_dir': os.path.join(/usr/local/lib/python3.6/site-packages/saml2/attributemaps'),
'metadata': {
'local': [os.path.join(SAML2_DIR, 'remote_metadata.xml')],
},
'contact_person': [{
'given_name': 'First',
'sur_name': 'Last',
'company': 'Company',
'email_address': 'me#company.com',
'contact_type': 'technical'
}],
'organization': {
'name': 'Company',
'display_name': 'Company',
'url': 'http://www.company.com',
},
'valid_for': 24, # how long is our metadata valid
'accepted_time_diff': 120, #seconds
}
SAML_DJANGO_USER_MAIN_ATTRIBUTE = 'username'
SAML_CREATE_UNKNOWN_USER = True
SAML_ATTRIBUTE_MAPPING = {
'email': ('email', ),
'givenName': ('first_name', ),
'sn': ('last_name', ),
'uid': ('username', ),
}

Process JSON Responses, Editing and Sending

I am working with an API. I get a response from the API which looks like this:
from oauthlib.oauth2 import BackendApplicationClient
from requests.auth import HTTPBasicAuth
from requests_oauthlib import OAuth2Session
auth = HTTPBasicAuth(client_id, client_secret)
client = BackendApplicationClient(client_id=client_id)
oauth = OAuth2Session(client=client)
token = oauth.fetch_token(token_url=token_url, auth=auth)
client = OAuth2Session(client_id, token=token, auto_refresh_url=token_url,token_updater=token_saver)
token_saver = []
device_policy = client.get('{URL}/v1?ids='+ids)
I get this response
[{'id': '',
'name': 'A Name',
'description': '',
'platform_name': 'Windows',
'groups': [],
'enabled': True,
'created_by': 'An Email',
'created_timestamp': '2019-03-28T12:51:30.989736386Z',
'modified_by': 'An Email ,
'modified_timestamp': '2019-11-19T21:14:53.0189419Z',
'settings': {'enforcement_mode': 'MONITOR_ENFORCE',
'end_user_notification': 'SILENT',
'classes': [{'id': 'ANY', 'action': 'FULL_ACCESS', 'exceptions': []},
{'id': 'IMAGING', 'action': 'FULL_ACCESS', 'exceptions': []},
{'id': 'MASS_STORAGE', 'action': 'BLOCK_ALL', 'exceptions': []},
{'id': 'MOBILE', 'action': 'BLOCK_ALL', 'exceptions': []},
{'id': 'PRINTER', 'action': 'FULL_ACCESS', 'exceptions': []},
{'id': 'WIRELESS', 'action': 'BLOCK_ALL', 'exceptions': []}]}}]
In each class there is list for hold exceptions. The API accepts a patch (not really a patch) that if this data is resubmitted with the exception field holding the contents of this function then an exception is accepted.
`
file_info = {
"class": "ANY",
"vendor_name": "",
"product_name": "",
"serial_number": serial_number,
"combined_id": "",
"action": "FULL_ACCESS",
"match_method": "VID_PID_SERIAL"
}
`
The challenge I have is accepting the first document and then adding the exception material to create a this patch request. I can "walk" the document but cannot work how to create a new body text to send. I think I want to do something like this but not using append as this throws an error.
new_walk_json = walk_json.append(['classes'][0]['exceptions']['Test'])
Realised .update function can used.

Can't post Flask form data for testing (FieldList) [DeprecationWarning]

I'm writing several different flask forms. So far, what I've done to test the routes that I use with those forms is.
Creating the form object
Populating the form
Posting the form data
For example, this works (short version of the class):
class RequestForm(FlaskForm):
project = SelectField(label='Project', id='project', coerce=str)
name = StringField('Environment Name', id='env_name', validators=[DataRequired()])
requested_by = StringField('Requested By', validators=[DataRequired()])
tag = StringField('Tag')
required_from = DateField('Required From', format='%Y-%m-%d', validators=[DataRequired()])
required_to = DateField('Required To', format='%Y-%m-%d', validators=[Optional()])
And when testing:
def test_post_full_request_is_correct(self):
with self.app.test_client() as http_client:
self.login(http_client)
required_from = str(datetime.datetime.today().date() + datetime.timedelta(days=1))
form = RequestForm()
form.project.data = 'SO'
form.name.data = 'TEST02'
form.requested_by.data = 'dev01'
form.required_from.data = required_from
form.env_type.data = 'DEV'
form.location.data = 'Narnia'
form.size.data = 'L'
form.resilience.data = '0'
form.submit.data = True
response = http_client.post('/request', data=form.data)
This works fine.
However, I've also got these forms. The main one being RequestsComponentsForm.
class RequestComponentForm(FlaskForm):
component = StringField('Name', validators=[DataRequired()])
source = SelectField('Source', choices=[('nexus', 'Nexus')])
version = StringField('Version')
def __init__(self, *args, **kwargs):
kwargs['csrf_enabled'] = False
super(RequestComponentForm, self).__init__(*args, **kwargs)
class RequestComponentsForm(FlaskForm):
components = FieldList(FormField(RequestComponentForm))
submit = SubmitField('Request Environment')
It works like a charm when testing it manually. However, when I tried automated testing:
c_form = RequestComponentsForm()
components = ['app-a', 'app-b', 'app-c', 'app-d', 'app-e', 'app-f']
test_versions = ['1.2.3', '3.2.1', '2.1.3', '5.7.1', '3.6.3', '1.4.6']
for c, v in zip(log_design_components, test_versions):
entry = RequestComponentForm()
entry.component = c
entry.source = 'nexus'
entry.version = v
c_form.components.append_entry(entry)
response = http_client.post('/request{0}components'.format(env_request_id),
headers={'Referer': good_referrer},
data=c_form.data)
I get the following:
venv/lib/python3.6/site-packages/werkzeug/test.py:349: DeprecationWarning: it's no longer possible to pass dicts as `data`. Use tuples or FileStorage objects instead.
I don't understand why my approach to testing the first form works and the same approach for the 2nd one doesn't.
Any help would be greatly appreciated.
Thanks!
Update
This is what the first form's data looks like:
{
'project': 'SO',
'name': 'TEST02',
'requested_by': 'dev01',
'tag': '',
'required_from': '2018-07-11',
'required_to': None,
'monday_on': None,
'monday_off': None,
'tuesday_on': None,
'tuesday_off': None,
'wednesday_on': None,
'wednesday_off': None,
'thursday_on': None,
'thursday_off': None,
'friday_on': None,
'friday_off': None,
'saturday_on': None,
'saturday_off': None,
'sunday_on': None,
'sunday_off': None,
'env_type': 'DEV',
'location': 'Narnia',
'size': 'L',
'resilience': '0',
'submit': True
}
And the second:
{
'components': [
{
'component': 'app-a',
'source': 'nexus',
'version': '1.2.3'
},
{
'component': 'app-b',
'source': 'nexus',
'version': '3.2.1'
},
{
'component': 'app-c',
'source': 'nexus',
'version': '2.1.3'
},
{
'component': 'app-d',
'source': 'nexus',
'version': '5.7.1'
},
{
'component': 'app-e',
'source': 'nexus',
'version': '3.6.3'
},
{
'component': 'app-f',
'source': 'nexus',
'version': '1.4.6'
}
],
'submit': True
}
They're both dictionaries, so I really don't understand why the first one would work and the other one won't.
FieldList is a handy wrapper of a group of fields in WTF. While posting data, you should use the original form, for the second form, the payload should be as follows:
{
'components-0-component': 'app-a',
'components-0-source': 'nexus',
'components-0-version': '1.2.3',
'components-1-component': 'app-b',
'components-1-source': 'nexus',
'components-1-version': '3.2.1',
...
}

Categories