I'm trying to access the social site, minds.com via this python api by by installing the module locally with python3 setup.py install && pipenv run python and following the instructions to log in.
However, I get this error message when trying to authenticate:
(For some reason, Stackoverflow doesn't allow me to post the python error-log becuse it isn't indented properly, so here it is https://pastebin.com/0sWa1hmY)
The code, which seems to be called from the python api looks like this:
minds/api.py
# -*- coding: utf-8 -*-
from pprint import pprint
from requests.utils import dict_from_cookiejar, cookiejar_from_dict
from minds.connections import XSRFSession
from minds.exceptions import AuthenticationError
from minds.profile import Profile
from minds.utils import add_url_kwargs
from minds.endpoints import *
from minds.sections import NewsfeedAPI, ChannelAPI, NotificationsAPI,
PostingAPI, InteractAPI
class Minds(NewsfeedAPI, ChannelAPI, NotificationsAPI, PostingAPI, ...):
_xsrf_retries = 5
def __init__(self, profile: Profile = None, login=True):
self.con = XSRFSession()
self.profile = profile
if self.profile:
if profile.cookie:
self.con.cookies = cookiejar_from_dict(profile.cookie)
if profile.proxy:
self.con.proxies = {'https': profile.proxy, 'http':\
profile.proxy}
self._establish_xsrf()
if self.profile and login and not self.is_authenticated:
if profile.username and profile.password:
self.authenticate(profile.username, profile.password)
(...)
def authenticate(self, username, password, save_profile=True) -> dict:
"""
Authenticate current instance with given user
:param: save_profile: whether to save profile locally
"""
auth = {
'username': username,
'password': password
}
resp = self.con.post(AUTHENTICATE_URL, json=auth)
self.user = resp.json()
if self.user['status'] == 'failed':
raise AuthenticationError("Couldn't log in with the ...")
self.guid = self.user['user']['guid']
if save_profile:
Profile(
username=username,
password=password,
cookie=self.get_cookies(),
proxy=self.con.proxies.get('https'),
).save()
return resp.json()
The python API doesn't seem to be maintained, but I think minds.com newly uses jsonwebtokens for authentication. Is the something missing from the api to be jwt-enabled?
After compare to browser request again and again. The reason why occurred error is you gave a wrong content-type, but actually you need to send as json data (it seems website is making joke). So you need to specify headers["content-type"] = "text/plain". Otherwise website will response 500.
Note: To avoid broad discussion, i can only answer this error.
Change the code to below, but there is only one line differs from source code :).
def authenticate(self, username, password, save_profile=True) -> dict:
"""
Authenticate current instance with given user
:param: save_profile: whether to save profile locally
"""
auth = {
'username': username,
'password': password
}
self.con.headers["content-type"] = "text/plain" ####
resp = self.con.post(AUTHENTICATE_URL, json=auth)
self.user = resp.json()
if self.user['status'] == 'failed':
raise AuthenticationError("Couldn't log in with the given credentials")
self.guid = self.user['user']['guid']
if save_profile:
Profile(
username=username,
password=password,
cookie=self.get_cookies(),
proxy=self.con.proxies.get('https'),
).save()
return resp.json()
Related
Got one shared link from OneDrive hosted by someone that's not me - https://1drv.ms/v/s!AjonToUPWqXmgjO3RqDbhRaSMrOM?e=69dccx
While investigating a solution that would enable to download that file from OneDrive programatically with Python, stumbled accross Office365-REST-Python-Client.
In their GitHub page they've got a specific section dedicated to OneDrive and also some examples of what one can do with it where one closely matches the desired goal.
Installed it using
pip install Office365-REST-Python-Client
but after comparing the code between the example that closely matched my goal realized that had to run instead
pip install office365-rest-client
Changed the initial part to
import os
import tempfile
from office365.graph.graph_client import GraphClient
from office365.settings import settings
which required to create a file in root with the name settings.py
import os
secure_vars = os.environ['office365_python_sdk_securevars'].split(';')
tenant = os.environ.get('office365_python_sdk_tenant', 'mediadev8')
settings = {
'url': 'https://{tenant}.sharepoint.com/'.format(tenant=tenant),
'tenant': '{tenant}.onmicrosoft.com'.format(tenant=tenant),
'redirect_url': 'https://github.com/vgrem/Office365-REST-Python-Client/',
'user_credentials': {
'username': secure_vars[0],
'password': secure_vars[1]
},
'client_credentials': {
'client_id': secure_vars[2],
'client_secret': secure_vars[3],
}
}
Then, adapted the function get_token() to
def get_token(auth_ctx):
"""Acquire token via client credential flow (ADAL Python library is utilized)
:type auth_ctx: adal.AuthenticationContext
"""
url = 'https://1drv.ms/v/s!AjonToUPWqXmgjO3RqDbhRaSMrOM?e=69dccx'
username = 'email'
password = 'password'
token = auth_ctx.acquire_token_for_user(url, username, password)
return token
So, this is the current code
import os
import tempfile
from office365.graph.graph_client import GraphClient
from office365.settings import settings
def get_token(auth_ctx):
"""Acquire token via client credential flow (ADAL Python library is utilized)
:type auth_ctx: adal.AuthenticationContext
"""
url = 'https://1drv.ms/v/s!AjonToUPWqXmgjO3RqDbhRaSMrOM?e=69dccx'
username = 'email' #with my email
password = 'password' #with my password
token = auth_ctx.acquire_token_for_user(url, username, password)
return token
def download_files(remote_folder, local_path):
drive_items = remote_folder.children
client.load(drive_items)
client.execute_query()
for drive_item in drive_items:
if not drive_item.file.is_server_object_null: # is file?
# download file content
with open(os.path.join(local_path, drive_item.name), 'wb') as local_file:
drive_item.download(local_file)
client.execute_query()
print("File '{0}' has been downloaded".format(local_file.name))
# example demonstrates how to export OneDrive files into local file system
# connect
client = GraphClient(settings['tenant'], get_token)
# load drive properties
drive = client.users["tiagomartinsperes#gmail.com"].drive
client.load(drive)
client.execute_query()
# download files from OneDrive
with tempfile.TemporaryDirectory() as path:
download_files(drive.root, path)
print("Done")
Even though these adjustments fixed various problems, still didn't manage to make it work.
When running the code, this is the error I'm getting
File
"C:\Users\tiago\Anaconda3\lib\site-packages\office365\settings.py",
line 3, in
secure_vars = os.environ['office365_python_sdk_securevars'].split(';')
File "C:\Users\tiago\Anaconda3\lib\os.py", line 678, in getitem
raise KeyError(key) from None
KeyError: 'office365_python_sdk_securevars'
Having some trouble using this plugin https://github.com/agile4you/bottle-jwt/
It seems to not work as I expected, down below my code:
import bottle
from Py.engine import *
from bottle_jwt import (JWTProviderPlugin, jwt_auth_required)
class AuthBackend(object):
user = {'id': 1237832, 'username': 'pav', 'password': '123', 'data': {'sex': 'male', 'active': True}}
def authenticate_user(self, username, password):
"""Authenticate User by username and password.
Returns:
A dict representing User Record or None.
"""
if username == self.user['username'] and password == self.user['password']:
return self.user
return None
def get_user(self, user_id):
"""Retrieve User By ID.
Returns:
A dict representing User Record or None.
"""
if user_id == self.user['id']:
return {k: self.user[k] for k in self.user if k != 'password'}
return None
app = bottle.Bottle()
server_secret = 'secret'
provider_plugin = JWTProviderPlugin(
keyword='jwt',
auth_endpoint='/login',
backend=AuthBackend(),
fields=('username', 'password'),
secret=server_secret,
ttl=30
)
app.install(provider_plugin)
#app.route('/')
#jwt_auth_required
def index():
return open('Html/index.html', 'r').read()
#app.post('/login')
def login():
return open('Html/login.html', 'r').read()
#app.get('/login')
def login():
return open('Html/login.html', 'r').read()
def run_server():
bottle.run(app=app, host='localhost', port=8080, debug=True, reloader=True)
# Main
if __name__ == '__main__':
run_server()
Once running, if I open browser On 127.0.0.1/8080 i get back a blank page with the string "{"AuthError": ["Cannot access this resource!"]}"
Which is Fine, it means that I'm not allowed to open index.html file (Cool: #jwt_auth_required worked)
Digging in source file I found a function named validate_token() with:
if not token:
logger.debug("Forbidden access")
raise JWTForbiddenError('Cannot access this resource!')
Here is the exception
except JWTForbiddenError as error:
bottle.response.content_type = b('application/json')
bottle.response._status_line = b('403 Forbidden')
return {"AuthError": error.args}
So, is there any way to redirect me on my login.html page if token does not match or is absent?
Plugin includes some way to do that or is just an API pckg?
That's not how JWT concept is supposed to be used. JWT are for RESTFul.
You need to make the server as REST API and on the client use JS
libraries such as AngularJs / Vue.js etc.,
Coming to the question about the plugin:
provider_plugin = JWTProviderPlugin(
keyword='jwt',
auth_endpoint='/login',
backend=AuthBackend(),
fields=('username', 'password'),
secret=server_secret,
ttl=30
)
auth_endpoint='/login' is to give a custom endpoint for authorization where the Bottle_JWT methods are looking for credentials to validate and generate JWT for.
I created a mock just to construct a response and this is how it should be used.
Once you pass the correct credential, the plugin responds with the JWT and expire which you have to intercept in authorized calls and add as request headers
Hope this helps.
I'm using the Python social auth with a custom backend. Here is my code so far:
from requests import HTTPError
from social.backends.oauth import BaseOAuth2
from social.exceptions import AuthFailed
class MyOAuth2(BaseOAuth2):
#testing OAuth authentication backend
name = 'testing'
AUTHORIZATION_URL = 'https://<my_drupal_oauth2_server>/oauth2/authorize'
ACCESS_TOKEN_URL = 'https://<my_drupal_oauth2_server>/oauth2/token'
SCOPE_SEPARATOR = ','
STATE_PARAMETER = 'False'
REDIRECT_STATE = 'False'
EXTRA_DATA = [
('id', 'id'),
('expires', 'expires'),
]
def get_user_details(self, response):
#Return user details from test account
return {'username': response.get('login'),
'email': response.get('email') or '',
'first_name': response.get('name')}
#def user_data(self, access_token, *args, **kwargs):
# #Loads user data from service
This is my first attempt to implement an OAuth2 setup using a custom back end -- first attempt to write one.
The OAuth2 server works with Google's OAuth2 playground. My django app (which is Open edX) however is throwing the error.
If I manually add ?state=xyz to the url, I get another error:
Session value state missing
I've searched for good information on both of these errors and I'm coming up short on what a root cause might be, and a solution.
I can't seem to change a users password using the ldap3 python module against an OpenLDAP server. A similar question has been asked before but that's specific to Active Directory.
What I've tried:
from ldap3.extend.standard.modifyPassword import ModifyPassword
from ldap3.utils.hashed import hashed
password = hashed(HASHED_SALTED_SHA, password)
# or..
password = '{SASL}theuser#domain.com'
modify = ModifyPassword(
connection, user.entry_get_dn(), new_password=password)
resp = modify.send()
print(modify.result)
{'referrals': None, 'result': 0, 'description': 'success', 'type': 'extendedResp', 'message': '', 'responseName': None, 'new_password': None, 'dn': '', 'responseValue': None}
The description says success, but the password isn't actually changed.
I've also tried to send a modify replace message:
def modify_user_password(self, user, password):
dn = user.entry_get_dn()
hashed_password = hashed(HASHED_SALTED_SHA, 'MyStupidPassword')
changes = {
'userPassword': [(MODIFY_REPLACE, [hashed_password])]
}
logger.debug('dn: ' + dn)
logger.debug('changes: ' + str(changes))
success = self.engage_conn.modify(dn, changes=changes)
if success:
logger.debug('Changed password for: %s', dn)
print(self.engage_conn.result)
else:
logger.warn('Unable to change password for %s', dn)
logger.debug(str(self.engage_conn.result))
raise ValueError('stop')
The connection is not an SSL connection. The answer to the AD question requires that the connection be over SSL. Is this also a requirement for OpenLDAP?
Edit:
After changing the dn to user.entry_get_dn() the code seemed to work about 90% of the time. After running these tests again today it appears that it now works consistently. I'm going to chalk this up to not viewing fresh data in my directory browser.
Changing the password seems to work as described in the docs and shown in the edit of my question above. For future reference, this code seems to work:
from ldap3 import (
HASHED_SALTED_SHA, MODIFY_REPLACE
)
from ldap3.utils.hashed import hashed
def modify_user_password(self, user, password):
dn = user.entry_get_dn()
hashed_password = hashed(HASHED_SALTED_SHA, password)
changes = {
'userPassword': [(MODIFY_REPLACE, [hashed_password])]
}
success = self.connection.modify(dn, changes=changes)
if not success:
print('Unable to change password for %s' % dn)
print(self.connection.result)
raise ValueError('Unable to change password')
To clarify a few things:
This is connecting to an OpenLDAP server (with multiple databases)
There is NO SSL here. We plan on implementing SSL but this works without it.
I'm trying to implement twisted cred with HTTP Digest Authentication, and I'm having some difficulty. I was able to get it to work with checkPassword, but I don't want to store my passwords in the clear in the database, obviously.
I'm storing the MD5 of username:realm:password in my password database, and I'm calculating it using:
from twisted.cred._digest import calcHA1
def calc_ha1(self, password, username=None):
if username is None:
username = self.avatarId
realm = self.digest_factory.digest.authenticationRealm
return calcHA1('md5', username, realm, password, None, None)
My password checker looks like this:
def requestAvatarId(self, credentials):
username = credentials.username
try:
user = self.session.query(models.User).filter_by(username=username).one()
except NoResultFound as e:
return defer.fail(credError.UnauthorizedLogin("No such administrator"))
if credentials.checkHash(user.password_hash):
return defer.succeed(username)
else:
return defer.fail(credError.UnauthorizedLogin("Bad password"))
However, when checkHash computes HA2 and combines it with the HA1 that I have in the DB, it does not match what the browser is sending. I stepped through the checkHash code with a debugger and everything is operating as I would expect. Does anyone have ideas?
Thanks
-s