Skip to content

Commit

Permalink
bug fixes while writing test cases, e.g. ensuring blocked accounts ca…
Browse files Browse the repository at this point in the history
…nnot log in, adding dev key to unlink accounts
  • Loading branch information
mekarpeles authored and root committed Feb 1, 2017
1 parent 198c4bb commit 931291d
Show file tree
Hide file tree
Showing 7 changed files with 702 additions and 110 deletions.
2 changes: 2 additions & 0 deletions conf/openlibrary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,5 @@ affiliate_ids:
# Amazon is a book source, not just an affiliate, so we make its affiliate
# tag generally available
amazon: internetarchi-20

internal_api_key: '8oPd1tx747YH374ohs48ZO5s2Nt1r9yD'
139 changes: 56 additions & 83 deletions openlibrary/accounts/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,17 +295,17 @@ def get_linked_ia_account(self):
class OpenLibraryAccount(Account):

@classmethod
def create(cls, username, email, password, test=False, verify=True):
if test:
return cls(email="[email protected]", itemname="test",
screenname="test")
def create(cls, username, email, password, verify=False, test=False):
if cls.get(email=email):
raise ValueError('email_registered')
if cls.get(username=username):
raise ValueError('username_registered')

if test:
return cls(**{'itemname': username, 'email': email})

raise NotImplementedError('account_creation_not_implemented')
# XXX Create account here

account = web.ctx.site.register(
username=username,
email=email,
Expand Down Expand Up @@ -371,11 +371,13 @@ def unlink(self):
_ol_account = web.ctx.site.store.get(self._key)
_ol_account['internetarchive_itemname'] = None
web.ctx.site.store[self._key] = _ol_account
self.internetarchive_itemname = None

def link(self, itemname):
_ol_account = web.ctx.site.store.get(self._key)
_ol_account['internetarchive_itemname'] = itemname
web.ctx.site.store[self._key] = _ol_account
self.internetarchive_itemname = itemname

@classmethod
def authenticate(cls, email, password, test=False):
Expand All @@ -399,73 +401,57 @@ def __init__(self, **kwargs):
setattr(self, k, kwargs[k])

@classmethod
def create(cls, screenname, email, password, test=False):
def create(cls, screenname, email, password, verified=False, test=False):
screenname = screenname.replace('@', '') # remove IA @
if test:
return cls(email="[email protected]", itemname="test",
screenname="test")
if cls.get(email=email):
raise ValueError('email_registered')
if cls.get(screenname=screenname):
raise ValueError('username_registered')

raise NotImplementedError('account_creation_not_implemented')
response = cls._post_ia_user_api(
service='createUser', email=email, password=password,
username=username, test=test)
response = cls.xauth( # XXX test
'create', test='oltest', email=email, password=password,
screenname=screenname, verified=verified)

@classmethod
def xauth(cls, **data):
return cls._post_ia_xauth_api(**data)

@classmethod
def _post_ia_xauth_api(cls, test=None, **data):
service = data.pop('service', u'')
def xauth(cls, service, test=None, **data):
url = "%s?op=%s" % (lending.IA_XAUTH_API_URL, service)
data.update({
'client_access': lending.config_ia_ol_xauth_s3.get('s3_key'),
'client_secret': lending.config_ia_ol_xauth_s3.get('s3_secret')
})
payload = urllib.urlencode(data)
payload = simplejson.dumps(data)
if test:
url += "&developer=%s" % test
try:
response = urllib2.urlopen(url, payload).read()
req = urllib2.Request(url, payload, {
'Content-Type': 'application/json'})
f = urllib2.urlopen(req)
response = f.read()
f.close()
except urllib2.HTTPError as e:
if e.code == 403:
return {'error': e.read(), 'code': 403}
else:
response = e.read()
return simplejson.loads(response)

@classmethod
def _post_ia_user_api(cls, test=False, **data):
token = lending.config_ia_ol_auth_key
if 'token' not in data and token:
data['token'] = token
if test or not token: # ?
data['test'] = "true"
payload = urllib.urlencode(data)
response = simplejson.loads(urllib2.urlopen(
lending.IA_USER_API_URL, payload).read())
return response

@classmethod
def get(cls, email, test=False, _json=False):
response = cls._post_ia_xauth_api(email=email, test=test, service="info")
response = cls.xauth(email=email, test=test, service="info")
if 'success' in response:
values = response.get('values', {})
if values:
values['email'] = email
return values if _json else cls(**values)

@classmethod
def authenticate(cls, email, password, test=False):
return cls._post_ia_user_api(test=test, **{
response = cls.xauth('authenticate', test=test, **{
"email": email,
"password": password,
"service": "authUser",
"password": password
})
return ("ok" if response.get('success') is True else
response.get('values', {}).get('reason'))


def audit_accounts(email, password, test=False):
Expand All @@ -491,6 +477,7 @@ def audit_accounts(email, password, test=False):

elif ia_login == "ok":
ia_account = InternetArchiveAccount.get(email=email, test=test)

audit['authenticated'] = 'ia'
audit['has_ia'] = email

Expand Down Expand Up @@ -518,12 +505,12 @@ def audit_accounts(email, password, test=False):
audit['link'] = link
audit['has_ol'] = ol_account.email

# Kludge so if a user logs in with IA credentials, we
# can fetch the linked OL account and set an
# auth_token even when we don't have the OL user's
# password in order to perform web.ctx.site.login.
# Their auth checks out via IA, set their auth_token for OL
web.ctx.conn.set_auth_token(ol_account.generate_login_code())
# Kludge so if a user logs in with IA credentials, we
# can fetch the linked OL account and set an
# auth_token even when we don't have the OL user's
# password in order to perform web.ctx.site.login.
# Their auth checks out via IA, set their auth_token for OL
web.ctx.conn.set_auth_token(ol_account.generate_login_code())

elif ol_login == "ok":
ol_account = OpenLibraryAccount.get(email=email, test=test)
Expand Down Expand Up @@ -565,24 +552,20 @@ def link_accounts(email, password, bridgeEmail="", bridgePassword="",

audit = audit_accounts(email, password)

if 'error' in audit:
if 'error' in audit or (audit['link'] and audit['authenticated']):
return audit

ia_account = (InternetArchiveAccount.get(
itemname=audit['has_ia'], test=test) if
audit.get('has_ia', False) else None)
ol_account = (OpenLibraryAccount.get(email=email, test=test) if
audit.get('has_ol', False) else None)
ia_account = (InternetArchiveAccount.get(email=audit['has_ia'], test=test)
if audit.get('has_ia', False) else None)
ol_account = (OpenLibraryAccount.get(email=audit['has_ol'], test=test)
if audit.get('has_ol', False) else None)

if ia_account and ol_account:
if not audit['link']:
if ia_account.locked or ol_account.blocked():
return {'error': 'account_blocked'}
audit['link'] = ia_account.itemname

# avoids Document Update conflict
_ol_account = web.ctx.site.store.get(ol_account._key)
_ol_account['internetarchive_itemname'] = ia_account.itemname
web.ctx.site.store[ol_account._key] = _ol_account

ol_account.link(ia_account.itemname)
return audit
elif not (ia_account or ol_account):
return {'error': 'account_not_found'}
Expand All @@ -600,64 +583,54 @@ def link_accounts(email, password, bridgeEmail="", bridgePassword="",
if OpenLibraryAccount.get_by_link(ia_account.itemname):
return {'error': 'account_already_linked'}

# avoids Document Update conflict
_ol_account = web.ctx.site.store.get(ol_account._key)
_ol_account['internetarchive_itemname'] = ia_account.itemname
web.ctx.site.store[ol_account._key] = _ol_account

ol_account.link(ia_account.itemname)
audit['link'] = ia_account.itemname
audit['has_ia'] = ia_account.itemname
aduit['ia_email'] = ia_account.email
audit['has_ia'] = ia_account.email
audit['has_ol'] = ol_account.email
return audit
return {'error': _res}
elif ia_account:
_resp = OpenLibraryAccount.authenticate(bridgeEmail, bridgePassword)
_resp = OpenLibraryAccount.authenticate(
bridgeEmail, bridgePassword)
if _resp == "ok":
ol_account = OpenLibraryAccount.get(
email=bridgeEmail, test=test)
if ol_account.itemname:
return {'error': 'account_already_linked'}

# avoids Document Update conflict
_ol_account = web.ctx.site.store.get(ol_account._key)
_ol_account['internetarchive_itemname'] = ia_account.itemname
web.ctx.site.store[ol_account._key] = _ol_account

audit['has_ia'] = ia_account.email
audit['has_ol'] = ol_account.email
audit['ol_email'] = ol_account.username
audit['link'] = ia_account.itemname
ol_account.link(ia_account.itemname)
return audit
return {'error': _resp}
# Create and link new account
elif email and password and username:
if ol_account:
try:
ol_account_username = username or ol_account.username
ia_account = InternetArchiveAccount.create(
username, email, password, test=test)
return {'error': 'account_creation_not_implemented'}
username, email, password, test=True) # XXX True

audit['link'] = ia_account.itemname
audit['has_ia'] = ia_account.itemname
aduit['ia_email'] = ia_account.email
audit['has_ia'] = ia_account.email
audit['has_ol'] = ol_account.email
ol_account.link(ia_account.itemname)
return audit

except (ValueError, NotImplementedError) as e:
return {'error': str(e)}
elif ia_account:
try:
ia_account_username = username or ia_account.screenname
ol_account = OpenLibraryAccount.create(
username, email, password, test=test, verify=False)

# avoics Document Update conflict
_ol_account = web.ctx.site.store.get(ol_account._key)
_ol_account['internetarchive_itemname'] = ia_account.itemname
web.ctx.site.store[ol_account._key] = _ol_account

username, email, password, ia_account_username,
verify=False, test=True) # XXX
audit['has_ol'] = ol_account.email
audit['ol_email'] = ol_account.username
audit['has_ia'] = ia_account.email
audit['link'] = ia_account.itemname
ol_account.link(ia_account.itemname)
return audit
except (ValueError, NotImplementedError) as e:
return {'error': str(e)}
return {'error': 'no_valid_accounts'}
return {'error': 'missing_fields'}

11 changes: 7 additions & 4 deletions openlibrary/core/lending.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,20 @@

config_content_server = None
config_loanstatus_url = None
config_ia_access_secret = None
config_bookreader_host = None
config_ia_access_secret = None
config_ia_ol_shared_key = None
config_ia_ol_auth_key = None
config_ia_ol_xauth_s3 = None
config_internal_api_key = None

def setup(config):
"""Initializes this module from openlibrary config.
"""
global config_content_server, config_loanstatus_url, \
config_ia_access_secret, config_bookreader_host, config_ia_ol_shared_key, \
config_ia_ol_auth_key, config_ia_ol_xauth_s3
config_ia_access_secret, config_bookreader_host, \
config_ia_ol_shared_key, config_ia_ol_auth_key, \
config_ia_ol_xauth_s3, config_internal_api_key

if config.get("content_server"):
try:
Expand All @@ -70,6 +72,7 @@ def setup(config):
config_ia_ol_shared_key = config.get('ia_ol_shared_key')
config_ia_ol_auth_key = config.get('ia_ol_auth_key')
config_ia_ol_xauth_s3 = config.get('ia_ol_xauth_s3')
config_internal_api_key = config.get('internal_api_key')


def is_borrowable(identifiers, acs=False, restricted=False):
Expand Down Expand Up @@ -396,7 +399,7 @@ def is_yet_to_be_fulfilled(self):
"""Returns True if the loan is not yet fulfilled and fulfillment time
is not expired.
"""
return (self['expiry'] is None and
return (self['expiry'] is None and
(time.time() - self['loaned_at']) < LOAN_FULFILLMENT_TIMEOUT_SECONDS)

def return_loan(self):
Expand Down
29 changes: 19 additions & 10 deletions openlibrary/plugins/upstream/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
import infogami.core.code as core

from openlibrary.i18n import gettext as _
from openlibrary.core import helpers as h
from openlibrary.core import helpers as h, lending
from openlibrary.plugins.recaptcha import recaptcha

from openlibrary import accounts
from openlibrary.accounts import link_accounts, audit_accounts, Account
import forms
Expand All @@ -34,20 +35,28 @@
sendmail = accounts.sendmail


class xauth(delegate.page):
path = "/xauth"
class unlink(delegate.page):
path = "/internal/account/unlink"

def GET(self):
from openlibrary.accounts import InternetArchiveAccount, OpenLibraryAccount
i = web.input(service='')
#result=InternetArchiveAccount.xauth(service=i.service, email=i.email)
ol = OpenLibraryAccount.get(email='[email protected]')
ol.unlink()
result = ol
#result = InternetArchiveAccount.get(email='[email protected]')
from openlibrary.accounts import OpenLibraryAccount
i = web.input(email='', itemname='', key='')
if i.key != lending.config_internal_api_key:
result = {'error': 'Authentication failed for private API'}
else:
try:
result = OpenLibraryAccount.get(email=i.email, link=i.itemname)
if result is None:
raise ValueError('Invalid Open Library account email ' \
'or itemname')
result.unlink()
except ValueError as e:
result = {'error': str(e)}

return delegate.RawText(simplejson.dumps(result),
content_type="application/json")


class account(delegate.page):
"""Account preferences.
"""
Expand Down
Loading

0 comments on commit 931291d

Please sign in to comment.