-
Notifications
You must be signed in to change notification settings - Fork 963
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement an Admin Flag model and some Emergency Circuit breakers (#2967
) * AdminFlag: Implement an Admin Flag model * implement flags for disallowing user and new project registration * improve and display error message on admin intervention * /help content for Administrator Intervention * fix up errors in refactoring AdminFlag.is_enabled to a classmethod, and a bad merge * address review feedback * lint
- Loading branch information
Showing
9 changed files
with
253 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import factory.fuzzy | ||
|
||
from warehouse.utils.admin_flags import AdminFlag | ||
|
||
from .base import WarehouseFactory | ||
|
||
|
||
class AdminFlagFactory(WarehouseFactory): | ||
class Meta: | ||
model = AdminFlag | ||
|
||
id = factory.fuzzy.FuzzyText(length=12) | ||
description = factory.fuzzy.FuzzyText(length=24) | ||
enabled = True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,14 +17,15 @@ | |
import pretend | ||
import pytest | ||
|
||
from pyramid.httpexceptions import HTTPMovedPermanently, HTTPSeeOther | ||
from pyramid.httpexceptions import (HTTPMovedPermanently, HTTPSeeOther) | ||
from sqlalchemy.orm.exc import NoResultFound | ||
|
||
from warehouse.accounts import views | ||
from warehouse.accounts.interfaces import ( | ||
IUserService, ITokenService, TokenExpired, TokenInvalid, TokenMissing, | ||
TooManyFailedLogins | ||
) | ||
from warehouse.utils.admin_flags import AdminFlag | ||
|
||
from ...common.db.accounts import EmailFactory, UserFactory | ||
|
||
|
@@ -288,32 +289,32 @@ def test_post_redirects_user(self, pyramid_request, expected_next_url, | |
|
||
class TestRegister: | ||
|
||
def test_get(self, pyramid_request): | ||
def test_get(self, db_request): | ||
form_inst = pretend.stub() | ||
form = pretend.call_recorder(lambda *args, **kwargs: form_inst) | ||
pyramid_request.find_service = pretend.call_recorder( | ||
db_request.find_service = pretend.call_recorder( | ||
lambda *args, **kwargs: pretend.stub( | ||
enabled=False, | ||
csp_policy=pretend.stub(), | ||
merge=lambda _: None, | ||
) | ||
) | ||
result = views.register(pyramid_request, _form_class=form) | ||
result = views.register(db_request, _form_class=form) | ||
assert result["form"] is form_inst | ||
|
||
def test_redirect_authenticated_user(self): | ||
result = views.register(pretend.stub(authenticated_userid=1)) | ||
assert isinstance(result, HTTPSeeOther) | ||
assert result.headers["Location"] == "/" | ||
|
||
def test_register_redirect(self, pyramid_request, monkeypatch): | ||
pyramid_request.method = "POST" | ||
def test_register_redirect(self, db_request, monkeypatch): | ||
db_request.method = "POST" | ||
|
||
user = pretend.stub(id=pretend.stub()) | ||
email = pretend.stub() | ||
create_user = pretend.call_recorder(lambda *args, **kwargs: user) | ||
add_email = pretend.call_recorder(lambda *args, **kwargs: email) | ||
pyramid_request.find_service = pretend.call_recorder( | ||
db_request.find_service = pretend.call_recorder( | ||
lambda *args, **kwargs: pretend.stub( | ||
csp_policy={}, | ||
merge=lambda _: {}, | ||
|
@@ -326,8 +327,8 @@ def test_register_redirect(self, pyramid_request, monkeypatch): | |
add_email=add_email, | ||
) | ||
) | ||
pyramid_request.route_path = pretend.call_recorder(lambda name: "/") | ||
pyramid_request.POST.update({ | ||
db_request.route_path = pretend.call_recorder(lambda name: "/") | ||
db_request.POST.update({ | ||
"username": "username_value", | ||
"password": "MyStr0ng!shP455w0rd", | ||
"password_confirm": "MyStr0ng!shP455w0rd", | ||
|
@@ -337,7 +338,7 @@ def test_register_redirect(self, pyramid_request, monkeypatch): | |
send_email = pretend.call_recorder(lambda *a: None) | ||
monkeypatch.setattr(views, 'send_email_verification_email', send_email) | ||
|
||
result = views.register(pyramid_request) | ||
result = views.register(db_request) | ||
|
||
assert isinstance(result, HTTPSeeOther) | ||
assert result.headers["Location"] == "/" | ||
|
@@ -347,7 +348,40 @@ def test_register_redirect(self, pyramid_request, monkeypatch): | |
assert add_email.calls == [ | ||
pretend.call(user.id, '[email protected]', primary=True), | ||
] | ||
assert send_email.calls == [pretend.call(pyramid_request, email)] | ||
assert send_email.calls == [pretend.call(db_request, email)] | ||
|
||
def test_register_fails_with_admin_flag_set(self, db_request): | ||
admin_flag = (db_request.db.query(AdminFlag) | ||
.filter( | ||
AdminFlag.id == 'disallow-new-user-registration') | ||
.first()) | ||
admin_flag.enabled = True | ||
db_request.method = "POST" | ||
|
||
db_request.POST.update({ | ||
"username": "username_value", | ||
"password": "MyStr0ng!shP455w0rd", | ||
"password_confirm": "MyStr0ng!shP455w0rd", | ||
"email": "[email protected]", | ||
"full_name": "full_name", | ||
}) | ||
|
||
db_request.session.flash = pretend.call_recorder( | ||
lambda *a, **kw: None | ||
) | ||
|
||
db_request.route_path = pretend.call_recorder(lambda name: "/") | ||
|
||
result = views.register(db_request) | ||
|
||
assert isinstance(result, HTTPSeeOther) | ||
assert db_request.session.flash.calls == [ | ||
pretend.call( | ||
("New User Registration Temporarily Disabled " | ||
"See https://pypi.org/help#admin-intervention for details"), | ||
queue="error" | ||
), | ||
] | ||
|
||
|
||
class TestRequestPasswordReset: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
from warehouse.utils.admin_flags import AdminFlag | ||
|
||
from ...common.db.utils import AdminFlagFactory as DBAdminFlagFactory | ||
|
||
|
||
class TestAdminFlag: | ||
|
||
def test_default(self, db_session): | ||
assert not AdminFlag.is_enabled(db_session, 'not-a-real-flag') | ||
|
||
def test_enabled(self, db_session): | ||
DBAdminFlagFactory.create(id='this-flag-is-enabled', enabled=True) | ||
assert AdminFlag.is_enabled(db_session, 'this-flag-is-enabled') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
56 changes: 56 additions & 0 deletions
56
warehouse/migrations/versions/7165e957cddc_create_table_for_warehouse_.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
""" | ||
create table for warehouse administration flags | ||
Revision ID: 7165e957cddc | ||
Revises: 1e2ccd34f539 | ||
Create Date: 2018-02-17 18:42:18.209572 | ||
""" | ||
|
||
from alembic import op | ||
import sqlalchemy as sa | ||
|
||
|
||
revision = '7165e957cddc' | ||
down_revision = '1e2ccd34f539' | ||
|
||
|
||
def upgrade(): | ||
op.create_table( | ||
'warehouse_admin_flag', | ||
sa.Column('id', sa.Text(), nullable=False), | ||
sa.Column('description', sa.Text(), nullable=False), | ||
sa.Column('enabled', sa.Boolean(), nullable=False), | ||
sa.PrimaryKeyConstraint('id') | ||
) | ||
# Insert our initial flags. | ||
op.execute(""" | ||
INSERT INTO warehouse_admin_flag(id, description, enabled) | ||
VALUES ( | ||
'disallow-new-user-registration', | ||
'Disallow ALL new User registrations', | ||
FALSE | ||
) | ||
""") | ||
op.execute(""" | ||
INSERT INTO warehouse_admin_flag(id, description, enabled) | ||
VALUES ( | ||
'disallow-new-project-registration', | ||
'Disallow ALL new Project registrations', | ||
FALSE | ||
) | ||
""") | ||
|
||
|
||
def downgrade(): | ||
op.drop_table('warehouse_admin_flag') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.