diff --git a/.env.example b/.env.example index e602846..ef6bbe0 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,7 @@ # Environment variables # Copy to a file named .env and add your values +FLASK_APP=verify +FLASK_ENV=development # Secret key SECRET_KEY=replace-me-in-production diff --git a/.github/workflows/flask.yml b/.github/workflows/flask.yml new file mode 100644 index 0000000..9b41993 --- /dev/null +++ b/.github/workflows/flask.yml @@ -0,0 +1,37 @@ +name: Flask + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + max-parallel: 4 + matrix: + python-version: [3.6, 3.7, 3.8] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + - name: Test + run: | + python -m pytest + env: + FLASK_APP: verify + FLASK_ENV: development + SECRET_KEY: some_secret + TWILIO_ACCOUNT_SID: sid + TWILIO_AUTH_TOKEN: token + VERIFICATION_SID: vesid diff --git a/.mergify.yml b/.mergify.yml new file mode 100644 index 0000000..0433495 --- /dev/null +++ b/.mergify.yml @@ -0,0 +1,13 @@ +pull_request_rules: + - name: automatic merge for Dependabot pull requests + conditions: + - author=dependabot-preview[bot] + - status-success=build (macos-latest, 10) + - status-success=build (macos-latest, 12) + - status-success=build (windows-latest, 10) + - status-success=build (windows-latest, 12) + - status-success=build (ubuntu-latest, 10) + - status-success=build (ubuntu-latest, 12) + actions: + merge: + method: squash diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..2f0727e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,73 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at open-source@twilio.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ad3257e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contributing to Twilio + +All third party contributors acknowledge that any contributions they provide will be made under the same open source license that the open source project is provided under. diff --git a/LICENSE b/LICENSE index 8f9252f..2c3cf9d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,31 +1,21 @@ -Copyright © 2010 by the Pallets team. +MIT License -Some rights reserved. +Copyright (c) 2020 Twilio Inc. -Redistribution and use in source and binary forms of the software as -well as documentation, with or without modification, are permitted -provided that the following conditions are met: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -* Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -* Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -* Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND -CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF -SUCH DAMAGE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d845466 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +UNAME := $(shell uname) +install: +ifeq ($(UNAME), Windows) + py -3 -m venv venv; venv\Scripts\activate.bat; +else + virtualenv venv; source ./venv/bin/activate; +endif + pip3 install -r requirements.txt + +serve-setup: + flask init-db; flask run; +open-browser: + python3 -m webbrowser "http://127.0.0.1:5000"; +serve: open-browser serve-setup diff --git a/README.md b/README.md index bfd1350..73b358d 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,44 @@ + + Twilio + + # Verify Quickstart +![](https://github.com/TwilioDevEd/verify-v2-quickstart-python/workflows/Flask/badge.svg) + > We are currently in the process of updating this sample template. If you are encountering any issues with the sample, please open an issue at [github.com/twilio-labs/code-exchange/issues](https://github.com/twilio-labs/code-exchange/issues) and we'll try to help you. -Simple phone verification with Python, Flask, and Twilio Verify. +Simple phone verification with Python, Flask, and Twilio Verify. -Full Quickstart instructions available at https://www.twilio.com/docs/verify/api-beta/quickstarts/python-flask +[Read the full quickstart here](https://www.twilio.com/docs/verify/api-beta/quickstarts/python-flask)! ## Install - python3 -m venv venv - . venv/bin/activate - -Or on Windows cmd: + ``` + make install + ``` - py -3 -m venv venv - venv\Scripts\activate.bat +Copy `.env.example` to `.env` to setup you environment. -Install Verify: + ``` + cp .env.example .env + ``` - pip install -r requirements.txt +Edit `.env` to add your Twilio access keys. You'll need to set your `TWILIO_ACCOUNT_SID` and +`TWILIO_AUTH_TOKEN` from the [Twilio Console](https://www.twilio.com/console). +For the `VERIFICATION_SID` variable you'll need to provision a +[Verification Service](https://www.twilio.com/console/verify/services). ## Run + ``` + make serve + ``` - export FLASK_APP=verify - export FLASK_ENV=development - flask init-db - flask run - -Or on Windows cmd: - - set FLASK_APP=verify - set FLASK_ENV=development - flask init-db - flask run - -Open http://127.0.0.1:5000 in a browser. +Open [http://127.0.0.1:5000](http://127.0.0.1:5000) in a browser. ## Meta * No warranty expressed or implied. Software is as is. Diggity. +* The CodeExchange repository can be found [here](https://github.com/twilio-labs/code-exchange/). * [MIT License](http://www.opensource.org/licenses/mit-license.html) * Lovingly crafted by Twilio Developer Education. diff --git a/requirements.txt b/requirements.txt index e652660..2039e18 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ -flask==1.0.2 -python-dotenv==0.9.1 -twilio==6.27.1 \ No newline at end of file +flask==1.1.1 +python-dotenv==0.12.0 +twilio==6.38.0 +pytest==5.4.1 \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..831d315 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,24 @@ +import os +import tempfile + +import pytest + +from verify import create_app +from verify.db import init_db + + +@pytest.fixture +def app(): + db_fd, db_path = tempfile.mkstemp() + app = create_app({'TESTING': True, 'DATABASE': db_path}) + with app.app_context(): + init_db() + + yield app + + os.close(db_fd) + os.unlink(db_path) + +@pytest.fixture +def client(app): + return app.test_client() diff --git a/tests/test_auth.py b/tests/test_auth.py new file mode 100644 index 0000000..096d411 --- /dev/null +++ b/tests/test_auth.py @@ -0,0 +1,34 @@ + +import re +from unittest.mock import patch + + +sample_user = { + 'username': 'tanjiro', + 'password': 'nezuko', + 'full_phone': '0909090909', + 'channel': 'sms' +} + +@patch('verify.auth.start_verification') +def test_creates_an_user(app, client): + res = client.post('/auth/register', data=sample_user) + assert res.status_code == 302 + assert '/auth/verify' in res.location + +@patch('verify.auth.start_verification') +def test_username_exists_raises_failure(app, client): + client.post('/auth/register', data=sample_user) + res = client.post('/auth/register', data=sample_user) + assert res.status_code == 200 + assert re.search('User tanjiro is already registered.', res.get_data(as_text=True)) + +@patch('verify.auth.start_verification') +def test_create_user_logout_and_log_back_in(app, client): + client.post('/auth/register', data=sample_user) + res = client.get('/auth/logout') + assert res.status_code == 302 + assert '/auth/login' in res.location + + res = client.post('/auth/login', data=dict(username='tanjiro', password='nezuko'), follow_redirects=True) + assert re.search('Congratulations, you have accessed the secret content!', res.get_data(as_text=True)) diff --git a/tests/test_routes.py b/tests/test_routes.py new file mode 100644 index 0000000..399fbfa --- /dev/null +++ b/tests/test_routes.py @@ -0,0 +1,4 @@ +def test_index(app, client): + res = client.get('/') + assert res.status_code == 302 + assert '/auth/login' in res.location