Skip to content

Commit

Permalink
Redirected to 404 if user goes to a missing user page (#629)
Browse files Browse the repository at this point in the history
  • Loading branch information
George Schneeloch authored Jun 29, 2016
1 parent cb7a5c0 commit 076f789
Show file tree
Hide file tree
Showing 7 changed files with 259 additions and 107 deletions.
43 changes: 25 additions & 18 deletions static/js/containers/LoginButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,32 @@ class LoginButton extends React.Component {
{getPreferredName(profile)}
</span>;

return (
<LinkContainer to={{ pathname: '/dashboard' }} active={false}>
<SplitButton
title={title}
bsStyle="danger"
id="logout-button">
<LinkContainer to={{ pathname: '/settings' }} active={false}>
<MenuItem>
Settings
if (SETTINGS.authenticated) {
return (
<LinkContainer to={{ pathname: '/dashboard' }} active={false}>
<SplitButton
title={title}
bsStyle="danger"
id="logout-button">
<LinkContainer to={{ pathname: '/settings' }} active={false}>
<MenuItem>
Settings
</MenuItem>
</LinkContainer>
<MenuItem
href="/logout"
eventKey="logout">
Logout
</MenuItem>
</LinkContainer>
<MenuItem
href="/logout"
eventKey="logout">
Logout
</MenuItem>
</SplitButton>
</LinkContainer>
);
</SplitButton>
</LinkContainer>
);
} else {
return <div className="pull-right">
<span>Get Started Today!</span>
<a className="btn btn-danger" href="/login/edxorg/">Sign in with edX.org</a>
</div>;
}
}
}

Expand Down
53 changes: 45 additions & 8 deletions static/js/containers/UserPage_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ describe("UserPage", function() {
helper.cleanup();
});

it('should have a logout button', () => {
return renderComponent(`/users/${SETTINGS.username}`, userActions).then(([, div]) => {
let button = div.querySelector("#logout-button");
assert.ok(button);
});
});

describe("Education History", () => {
let deleteButton = div => {
return div.getElementsByClassName('profile-tab-card')[1].
Expand Down Expand Up @@ -502,29 +509,59 @@ describe("UserPage", function() {
});
});
});

it("should show all edit, delete icons for an authenticated user's own page" , () => {
return renderComponent(`/users/${SETTINGS.username}`, userActions).then(([, div]) => {
let count = div.getElementsByClassName('mdl-button--icon').length;
assert.equal(count,
1 + USER_PROFILE_RESPONSE.work_history.length * 2 + USER_PROFILE_RESPONSE.education.length * 2
);
});
});

it("should not show any edit, delete icons for other user pages" , () => {
let otherProfile = Object.assign({}, USER_PROFILE_RESPONSE, {
username: 'other'
});
helper.profileGetStub.withArgs('other').returns(Promise.resolve(otherProfile));
return renderComponent(`/users/other`, userActions).then(([, div]) => {
let count = div.getElementsByClassName('mdl-button--icon').length;
assert.equal(count, 0);
});
});
});

describe("Unauthenticated user page", () => {
let settingsBackup;

beforeEach(() => {
helper = new IntegrationTestHelper();
listenForActions = helper.listenForActions.bind(helper);
renderComponent = helper.renderComponent.bind(helper);
patchUserProfileStub = helper.sandbox.stub(api, 'patchUserProfile');
renderComponent = helper.renderComponent.bind(helper);
helper.profileGetStub.
withArgs(SETTINGS.username).
returns (
Promise.resolve(Object.assign({}, USER_PROFILE_RESPONSE))
);
returns(Promise.resolve(USER_PROFILE_RESPONSE));
settingsBackup = SETTINGS;
SETTINGS = Object.assign({}, SETTINGS, {
authenticated: false
});
});

afterEach(() => {
helper.cleanup();
SETTINGS = settingsBackup;
});

it('should hide all edit, delete icons', () => {
return renderComponent(`/users/${SETTINGS.username}`, userActions).then(() => {
let icons = [...document.getElementsByClassName('mdl-button--icons')];
assert.deepEqual(icons, []);
return renderComponent(`/users/${SETTINGS.username}`, userActions).then(([, div]) => {
assert.equal(0, div.getElementsByClassName('mdl-button--icon').length);
});
});

it('should show sign in button with valid link', () => {
return renderComponent(`/users/${SETTINGS.username}`, userActions).then(([, div]) => {
let button = div.querySelector("a[href='/login/edxorg/']");
assert.equal(button.textContent.trim(), "Sign in with edX.org");
});
});
});
Expand Down
2 changes: 1 addition & 1 deletion static/js/global_init.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Define globals we would usually get from Django
global.SETTINGS = {
isAuthenticated: true,
authenticated: true,
name: "full name",
username: "jane",
edx_base_url: "/edx/"
Expand Down
1 change: 0 additions & 1 deletion ui/url_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@
DASHBOARD_URL = '/dashboard/'
PROFILE_URL = '/profile/'
TERMS_OF_SERVICE_URL = '/terms_of_service/'
USERS_URL = '/users/'
SETTINGS_URL = "/settings/"
8 changes: 4 additions & 4 deletions ui/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,21 @@
DASHBOARD_URL,
PROFILE_URL,
TERMS_OF_SERVICE_URL,
USERS_URL,
SETTINGS_URL,
)
from ui.views import (
dashboard,
DashboardView,
UsersView,
page_404,
page_500,
)

dashboard_urlpatterns = [
url(r'^{}'.format(dashboard_url.lstrip("/")), dashboard, name='ui-dashboard')
url(r'^{}'.format(dashboard_url.lstrip("/")), DashboardView.as_view(), name='ui-dashboard')
for dashboard_url in [
DASHBOARD_URL,
PROFILE_URL,
TERMS_OF_SERVICE_URL,
USERS_URL,
SETTINGS_URL,
]
]
Expand All @@ -31,4 +30,5 @@
url(r'^logout/$', 'django.contrib.auth.views.logout', {'next_page': '/'}),
url(r'^404/$', page_404, name='ui-404'),
url(r'^500/$', page_500, name='ui-500'),
url(r'^users/(?P<user>[-\w]+)?/?', UsersView.as_view(), name='ui-users'),
] + dashboard_urlpatterns
91 changes: 65 additions & 26 deletions ui/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.staticfiles.templatetags.staticfiles import static
from django.views.generic import View
from django.shortcuts import (
render,
Http404,
)
from django.utils.decorators import method_decorator

from backends.edxorg import EdxOrgOAuth2
from micromasters.utils import webpack_dev_server_host, webpack_dev_server_url
from profiles.permissions import CanSeeIfNotPrivate
from ui.decorators import (
require_mandatory_urls,
)
Expand All @@ -33,35 +37,70 @@ def get_bundle_url(request, bundle_name):
return static("bundles/{bundle}".format(bundle=bundle_name))


@require_mandatory_urls
@login_required()
def dashboard(request, *args): # pylint: disable=unused-argument
class ReactView(View): # pylint: disable=unused-argument
"""
The app dashboard view
Abstract view for templates using React
"""
name = ""
if not request.user.is_anonymous():
name = request.user.profile.preferred_name

js_settings = {
"gaTrackingID": settings.GA_TRACKING_ID,
"reactGaDebug": settings.REACT_GA_DEBUG,
"authenticated": not request.user.is_anonymous(),
"name": name,
"username": request.user.social_auth.get(provider=EdxOrgOAuth2.name).uid,
"host": webpack_dev_server_host(request),
"edx_base_url": settings.EDXORG_BASE_URL
}

return render(
request,
"dashboard.html",
context={
"style_src": get_bundle_url(request, "style.js"),
"dashboard_src": get_bundle_url(request, "dashboard.js"),
"js_settings_json": json.dumps(js_settings),
def get(self, request, *args, **kwargs):
"""
Handle GET requests to templates using React
"""
username = None
name = ""
if not request.user.is_anonymous():
name = request.user.profile.preferred_name
social_auths = request.user.social_auth.filter(
provider=EdxOrgOAuth2.name)
if social_auths.exists():
username = social_auths.first().uid

js_settings = {
"gaTrackingID": settings.GA_TRACKING_ID,
"reactGaDebug": settings.REACT_GA_DEBUG,
"authenticated": not request.user.is_anonymous(),
"name": name,
"username": username,
"host": webpack_dev_server_host(request),
"edx_base_url": settings.EDXORG_BASE_URL
}
)

return render(
request,
"dashboard.html",
context={
"style_src": get_bundle_url(request, "style.js"),
"dashboard_src": get_bundle_url(request, "dashboard.js"),
"js_settings_json": json.dumps(js_settings),
}
)


@method_decorator(require_mandatory_urls, name='dispatch')
@method_decorator(login_required, name='dispatch')
class DashboardView(ReactView):
"""
Wrapper for dashboard view which asserts certain logged in requirements
"""


class UsersView(ReactView):
"""
View for users pages. This gets handled by the dashboard view like all other
React handled views, but we also want to return a 404 if the user does not exist.
"""
def get(self, request, *args, **kwargs):
"""
Handle GET requests
"""
user = kwargs.pop('user')
if user is not None:
if not CanSeeIfNotPrivate().has_permission(request, self):
raise Http404
elif request.user.is_anonymous():
# /users/ redirects to logged in user's page, but user is not logged in here
raise Http404

return super(UsersView, self).get(request, *args, **kwargs)


def page_404(request):
Expand Down
Loading

0 comments on commit 076f789

Please sign in to comment.