Browse Source

added editing of own account

funding-tag
Holger Frey 5 years ago
parent
commit
f82d5bf746
  1. 3
      ordr3/resources.py
  2. 27
      ordr3/schemas/account.py
  3. 29
      ordr3/templates/account/myaccount.jinja2
  4. 62
      ordr3/views/account.py
  5. 125
      tests/test_repo.py
  6. 14
      tests/test_services.py

3
ordr3/resources.py

@ -2,7 +2,7 @@
import abc import abc
from pyramid.security import Allow, Everyone from pyramid.security import Allow, Everyone, Authenticated
from sqlalchemy.inspection import inspect from sqlalchemy.inspection import inspect
@ -61,6 +61,7 @@ class Root(BaseResource):
(Allow, Everyone, "logout"), (Allow, Everyone, "logout"),
(Allow, Everyone, "registration"), (Allow, Everyone, "registration"),
(Allow, Everyone, "view"), (Allow, Everyone, "view"),
(Allow, Authenticated, "account"),
] ]

27
ordr3/schemas/account.py

@ -114,3 +114,30 @@ class ResetPasswordSchema(CSRFSchema):
} }
settings.update(override) settings.update(override)
return super().as_form(request, **settings) return super().as_form(request, **settings)
class MyAccountSchema(CSRFSchema):
""" edit an account """
user_name = colander.SchemaNode(
colander.String(),
widget=deform.widget.TextInputWidget(
template="textinput_disabled.pt", css_class="o3-reg-username"
),
)
first_name = colander.SchemaNode(colander.String(),)
last_name = colander.SchemaNode(colander.String(),)
email = colander.SchemaNode(
colander.String(),
validator=colander.Email(),
widget=deform.widget.TextInputWidget(template="email.pt",),
)
@classmethod
def as_form(cls, request, **override):
settings = {
"buttons": ("Save", "Cancel"),
"css_class": "form-horizontal registration",
}
settings.update(override)
return super().as_form(request, **settings)

29
ordr3/templates/account/myaccount.jinja2

@ -0,0 +1,29 @@
{% extends "ordr3:templates/layout_small.jinja2" %}
{% block subtitle %} My Account {% endblock subtitle %}
{% block content %}
<div class="container">
<div class="row o3-registration-card">
<div class="col"></div>
<div class="col">
<div class="card">
<div class="card-header bg-dark text-light">
<h2 class="card-title text-center pt-1">Ordr</h5>
</div>
<div class="card-body">
<h6 class="card-subtitle mb-2 text-muted text-center mt-1 mb-4">Edit your account</h6>
{{form.render()|safe}}
<hr>
<p class="mt-4">
<a href="{{request.resource_url(request.root, 'mypassword')}}" class="btn btn-outline-secondary">Change Password</a>
</p>
</div>
</div>
</div>
<div class="col"></div>
</div>
</div>
{% endblock content %}

62
ordr3/views/account.py

@ -8,7 +8,7 @@ from pyramid.httpexceptions import HTTPFound
from .. import models, security, services from .. import models, security, services
from ..repo import RepoItemNotFound from ..repo import RepoItemNotFound
from ..events import PasswordResetEmail from ..events import FlashMessage, PasswordResetEmail
from ..schemas import account from ..schemas import account
@ -225,4 +225,62 @@ def reset_password(context, request):
) )
def password_reseted(context, request): def password_reseted(context, request):
return {} return {}
# http://localhost:6543/reset?t=69e24c08-1cb2-4656-987a-4791175f3368
@view_config(
context="ordr3:resources.Root",
name="myaccount",
permission="account",
request_method="GET",
renderer="ordr3:templates/account/myaccount.jinja2",
)
def myaccount(context, request):
form = account.MyAccountSchema.as_form(request)
form_data = {
"user_name": request.user.username,
"first_name": request.user.first_name,
"last_name": request.user.last_name,
"email": request.user.email,
}
form.set_appstruct(form_data)
return {"form": form}
@view_config(
context="ordr3:resources.Root",
name="myaccount",
permission="account",
request_method="POST",
renderer="ordr3:templates/account/myaccount.jinja2",
)
def edit_myaccount(context, request):
if "Cancel" in request.POST:
return HTTPFound(request.resource_path(request.root))
form = account.MyAccountSchema.as_form(request)
data = request.POST.items()
try:
appstruct = form.validate(data)
except deform.ValidationFailure:
return {"form": form}
request.user.first_name = appstruct["first_name"]
request.user.last_name = appstruct["last_name"]
request.user.email = appstruct["email"]
return HTTPFound(request.resource_path(request.root))
@view_config(
context="ordr3:resources.Root", name="mypassword", permission="account",
)
def myaccount_reset_link(context, request):
token = services.create_token_for_user(request.repo, request.user)
request.emit(PasswordResetEmail(request.user, token.token))
request.emit(
FlashMessage.info(
f"A password reset link has been sent to {request.user.email}."
)
)
return HTTPFound(request.resource_path(request.root))

125
tests/test_repo.py

@ -52,6 +52,20 @@ def example_users():
] ]
@pytest.fixture()
def example_tokens():
from ordr3.models import PasswordResetToken
from datetime import datetime, timedelta
valid = datetime.utcnow() + timedelta(days=2)
invalid = datetime.utcnow() - timedelta(days=2)
return [
PasswordResetToken("valid_token", 1, valid),
PasswordResetToken("invalid_token", 2, invalid),
]
def test_sql_repo_add_order(session, example_orders): def test_sql_repo_add_order(session, example_orders):
from ordr3.repo import SqlAlchemyRepository from ordr3.repo import SqlAlchemyRepository
from ordr3.models import OrderItem from ordr3.models import OrderItem
@ -76,6 +90,17 @@ def test_sql_repo_get_order(session, example_orders):
assert example_orders[1] == repo.get_order(2) assert example_orders[1] == repo.get_order(2)
def test_sql_repo_get_order_raises_exception(session, example_orders):
from ordr3.repo import SqlAlchemyRepository, RepoItemNotFound
repo = SqlAlchemyRepository(session)
repo.add_order(example_orders[0])
session.flush()
with pytest.raises(RepoItemNotFound):
repo.get_order(2)
def test_sql_repo_list_orders(session, example_orders): def test_sql_repo_list_orders(session, example_orders):
from ordr3.repo import SqlAlchemyRepository from ordr3.repo import SqlAlchemyRepository
@ -112,6 +137,17 @@ def test_sql_repo_get_user(session, example_users):
assert example_users[1] == repo.get_user(2) assert example_users[1] == repo.get_user(2)
def test_sql_repo_get_user_raises_exception(session, example_users):
from ordr3.repo import SqlAlchemyRepository, RepoItemNotFound
repo = SqlAlchemyRepository(session)
repo.add_user(example_users[0])
session.flush()
with pytest.raises(RepoItemNotFound):
repo.get_user(2)
def test_sql_repo_get_user_by_username(session, example_users): def test_sql_repo_get_user_by_username(session, example_users):
from ordr3.repo import SqlAlchemyRepository from ordr3.repo import SqlAlchemyRepository
@ -123,6 +159,17 @@ def test_sql_repo_get_user_by_username(session, example_users):
assert example_users[1] == repo.get_user_by_username("Me") assert example_users[1] == repo.get_user_by_username("Me")
def test_sql_repo_get_user_by_username_exception(session, example_users):
from ordr3.repo import SqlAlchemyRepository, RepoItemNotFound
repo = SqlAlchemyRepository(session)
repo.add_user(example_users[0])
session.flush()
with pytest.raises(RepoItemNotFound):
repo.get_user_by_username("unknown user name")
def test_sql_repo_get_user_by_email(session, example_users): def test_sql_repo_get_user_by_email(session, example_users):
from ordr3.repo import SqlAlchemyRepository from ordr3.repo import SqlAlchemyRepository
@ -134,6 +181,17 @@ def test_sql_repo_get_user_by_email(session, example_users):
assert example_users[1] == repo.get_user_by_email("jane.doe") assert example_users[1] == repo.get_user_by_email("jane.doe")
def test_sql_repo_get_user_by_email_exception(session, example_users):
from ordr3.repo import SqlAlchemyRepository, RepoItemNotFound
repo = SqlAlchemyRepository(session)
repo.add_user(example_users[0])
session.flush()
with pytest.raises(RepoItemNotFound):
repo.get_user_by_email("unknown email")
def test_sql_repo_list_users(session, example_users): def test_sql_repo_list_users(session, example_users):
from ordr3.repo import SqlAlchemyRepository from ordr3.repo import SqlAlchemyRepository
@ -157,3 +215,70 @@ def test_sql_search_vendor(session, example_users):
assert repo.search_vendor("sa") == "Sigma Aldrich" assert repo.search_vendor("sa") == "Sigma Aldrich"
assert repo.search_vendor("unknown") is None assert repo.search_vendor("unknown") is None
def test_sql_repo_add_reset_token(session, example_tokens):
from ordr3.repo import SqlAlchemyRepository
from ordr3.models import PasswordResetToken
repo = SqlAlchemyRepository(session)
repo.add_reset_token(example_tokens[0])
session.flush()
token = session.query(PasswordResetToken).first()
assert token == example_tokens[0]
def test_sql_repo_delete_reset_token(session, example_tokens):
from ordr3.repo import SqlAlchemyRepository
from ordr3.models import PasswordResetToken
repo = SqlAlchemyRepository(session)
repo.add_reset_token(example_tokens[0])
repo.add_reset_token(example_tokens[1])
session.flush()
repo.delete_reset_token(example_tokens[0])
tokens = session.query(PasswordResetToken).all()
assert tokens == [example_tokens[1]]
def test_sql_repo_get_reset_token(session, example_tokens):
from ordr3.repo import SqlAlchemyRepository
repo = SqlAlchemyRepository(session)
repo.add_reset_token(example_tokens[0])
repo.add_reset_token(example_tokens[1])
session.flush()
token = repo.get_reset_token("valid_token")
assert token == example_tokens[0]
def test_sql_repo_get_reset_token_raises_exception(session, example_tokens):
from ordr3.repo import SqlAlchemyRepository, RepoItemNotFound
repo = SqlAlchemyRepository(session)
repo.add_reset_token(example_tokens[0])
session.flush()
with pytest.raises(RepoItemNotFound):
repo.get_reset_token("unknown token")
def test_clear_stale_reset_tokens(session, example_tokens):
from ordr3.repo import SqlAlchemyRepository
from ordr3.models import PasswordResetToken
repo = SqlAlchemyRepository(session)
repo.add_reset_token(example_tokens[0])
repo.add_reset_token(example_tokens[1])
session.flush()
repo.clear_stale_reset_tokens()
tokens = session.query(PasswordResetToken).all()
assert tokens == [example_tokens[0]]

14
tests/test_services.py

@ -403,3 +403,17 @@ def test_get_user_from_reset_token_unknown_user():
result = services.get_user_from_reset_token(repo, "identifier") result = services.get_user_from_reset_token(repo, "identifier")
assert result is None assert result is None
def test_create_token_for_user():
from ordr3.services import create_token_for_user
from ordr3.models import PasswordResetToken, User
repo = FakeOrderRepository(None)
user = User(*list("ABCDEFG"))
result = create_token_for_user(repo, user)
assert isinstance(result, PasswordResetToken)
assert result.user_id == "A"
assert repo._tokens == {result}

Loading…
Cancel
Save