~n0mn0m/archive

cc4716a86cda409d36941f78b235552707991024 — n0mn0m 5 months ago c39ba18 master
Migrate submodules.

Moved from submodules to local sub directories for archive.
473 files changed, 72151 insertions(+), 32 deletions(-)

D .gitmodules
D BattlenetAuthomatic
A BattlenetAuthomatic/.gitattributes
A BattlenetAuthomatic/.gitignore
A BattlenetAuthomatic/LICENSE.txt
A BattlenetAuthomatic/README.md
A BattlenetAuthomatic/authomatic/__init__.py
A BattlenetAuthomatic/authomatic/adapters.py
A BattlenetAuthomatic/authomatic/core.py
A BattlenetAuthomatic/authomatic/exceptions.py
A BattlenetAuthomatic/authomatic/providers/__init__.py
A BattlenetAuthomatic/authomatic/providers/gaeopenid.py
A BattlenetAuthomatic/authomatic/providers/oauth1.py
A BattlenetAuthomatic/authomatic/providers/oauth2.py
A BattlenetAuthomatic/authomatic/providers/openid.py
A BattlenetAuthomatic/authomatic/providers/persona.py
A BattlenetAuthomatic/authomatic/six.py
A BattlenetAuthomatic/config.py
A BattlenetAuthomatic/main.py
A BattlenetAuthomatic/templates/base.html
A BattlenetAuthomatic/templates/index.html
A BattlenetAuthomatic/templates/login.html
D CodeLouisvilleDjango
A CodeLouisvilleDjango/.DS_Store
A CodeLouisvilleDjango/.gitignore
A CodeLouisvilleDjango/LICENSE
A CodeLouisvilleDjango/LouiePizza/LouiePizza/__init__.py
A CodeLouisvilleDjango/LouiePizza/LouiePizza/settings.py
A CodeLouisvilleDjango/LouiePizza/LouiePizza/urls.py
A CodeLouisvilleDjango/LouiePizza/LouiePizza/views.py
A CodeLouisvilleDjango/LouiePizza/LouiePizza/wsgi.py
A CodeLouisvilleDjango/LouiePizza/Menus/__init__.py
A CodeLouisvilleDjango/LouiePizza/Menus/admin.py
A CodeLouisvilleDjango/LouiePizza/Menus/apps.py
A CodeLouisvilleDjango/LouiePizza/Menus/migrations/0001_initial.py
A CodeLouisvilleDjango/LouiePizza/Menus/migrations/0002_auto_20170824_0051.py
A CodeLouisvilleDjango/LouiePizza/Menus/migrations/0003_menuitem.py
A CodeLouisvilleDjango/LouiePizza/Menus/migrations/0004_auto_20170824_2349.py
A CodeLouisvilleDjango/LouiePizza/Menus/migrations/__init__.py
A CodeLouisvilleDjango/LouiePizza/Menus/models.py
A CodeLouisvilleDjango/LouiePizza/Menus/templates/menus/louiemenu.html
A CodeLouisvilleDjango/LouiePizza/Menus/tests.py
A CodeLouisvilleDjango/LouiePizza/Menus/urls.py
A CodeLouisvilleDjango/LouiePizza/Menus/views.py
A CodeLouisvilleDjango/LouiePizza/Newsletters/__init__.py
A CodeLouisvilleDjango/LouiePizza/Newsletters/admin.py
A CodeLouisvilleDjango/LouiePizza/Newsletters/apps.py
A CodeLouisvilleDjango/LouiePizza/Newsletters/migrations/0001_initial.py
A CodeLouisvilleDjango/LouiePizza/Newsletters/migrations/__init__.py
A CodeLouisvilleDjango/LouiePizza/Newsletters/models.py
A CodeLouisvilleDjango/LouiePizza/Newsletters/templates/newsletters/subscribe.html
A CodeLouisvilleDjango/LouiePizza/Newsletters/tests.py
A CodeLouisvilleDjango/LouiePizza/Newsletters/urls.py
A CodeLouisvilleDjango/LouiePizza/Newsletters/views.py
A CodeLouisvilleDjango/LouiePizza/assets/css/normalize.css
A CodeLouisvilleDjango/LouiePizza/assets/css/style.css
A CodeLouisvilleDjango/LouiePizza/assets/img/hero-bg-pizza.jpg
A CodeLouisvilleDjango/LouiePizza/assets/img/louie-photo.png
A CodeLouisvilleDjango/LouiePizza/assets/img/louies-bg-making-red.jpg
A CodeLouisvilleDjango/LouiePizza/assets/img/louies-logo.svg
A CodeLouisvilleDjango/LouiePizza/assets/js/app.js
A CodeLouisvilleDjango/LouiePizza/db.sqlite3
A CodeLouisvilleDjango/LouiePizza/manage.py
A CodeLouisvilleDjango/LouiePizza/templates/index.html
A CodeLouisvilleDjango/LouiePizza/templates/layout.html
A CodeLouisvilleDjango/README.md
A CodeLouisvilleDjango/requirements.txt
D CommercialLaunchScraper
A CommercialLaunchScraper/.gitignore
A CommercialLaunchScraper/launch_scraper/ORM/comm_sqlite_mapping.py
A CommercialLaunchScraper/launch_scraper/__init__.py
A CommercialLaunchScraper/launch_scraper/__pycache__/__init__.cpython-35.pyc
A CommercialLaunchScraper/launch_scraper/__pycache__/items.cpython-35.pyc
A CommercialLaunchScraper/launch_scraper/__pycache__/launch_dataframe.cpython-35.pyc
A CommercialLaunchScraper/launch_scraper/__pycache__/pipelines.cpython-35.pyc
A CommercialLaunchScraper/launch_scraper/__pycache__/settings.cpython-35.pyc
A CommercialLaunchScraper/launch_scraper/csv/faa_actlnchlic_20161023.csv
A CommercialLaunchScraper/launch_scraper/csv/faa_actlnchsitelic_20161023.csv
A CommercialLaunchScraper/launch_scraper/csv/faa_actpermit_20161023.csv
A CommercialLaunchScraper/launch_scraper/csv/faa_licensedlaunch_20161023.csv
A CommercialLaunchScraper/launch_scraper/csv/faa_permlaunch_20161023.csv
A CommercialLaunchScraper/launch_scraper/csv/faa_reentry_20161023.csv
A CommercialLaunchScraper/launch_scraper/csv/faa_safetyappr_20161023.csv
A CommercialLaunchScraper/launch_scraper/items.py
A CommercialLaunchScraper/launch_scraper/json/faa_actlnchlic_20161023.json
A CommercialLaunchScraper/launch_scraper/json/faa_actlnchsitelic_20161023.json
A CommercialLaunchScraper/launch_scraper/json/faa_actpermit_20161023.json
A CommercialLaunchScraper/launch_scraper/json/faa_licensedlaunch_20161023.json
A CommercialLaunchScraper/launch_scraper/json/faa_permlaunch_20161023.json
A CommercialLaunchScraper/launch_scraper/json/faa_reentry_20161023.json
A CommercialLaunchScraper/launch_scraper/json/faa_safetyappr_20161023.json
A CommercialLaunchScraper/launch_scraper/launch_dataframe.py
A CommercialLaunchScraper/launch_scraper/main.py
A CommercialLaunchScraper/launch_scraper/pipelines.py
A CommercialLaunchScraper/launch_scraper/scratch.py
A CommercialLaunchScraper/launch_scraper/settings.py
A CommercialLaunchScraper/launch_scraper/spiders/__init__.py
A CommercialLaunchScraper/launch_scraper/spiders/__pycache__/__init__.cpython-35.pyc
A CommercialLaunchScraper/launch_scraper/spiders/__pycache__/commspiders.cpython-35.pyc
A CommercialLaunchScraper/launch_scraper/spiders/commspiders.py
A CommercialLaunchScraper/readme.md
A CommercialLaunchScraper/scrapy.cfg
D CppStatsCli
A CppStatsCli/.idea/codeStyles/Project.xml
A CppStatsCli/.idea/misc.xml
A CppStatsCli/.idea/modules.xml
A CppStatsCli/.idea/prework.iml
A CppStatsCli/CMakeLists.txt
A CppStatsCli/LICENSE
A CppStatsCli/README.md
A CppStatsCli/cmake-build-debug/CMakeCache.txt
A CppStatsCli/cmake-build-debug/CMakeFiles/3.10.3/CMakeCCompiler.cmake
A CppStatsCli/cmake-build-debug/CMakeFiles/3.10.3/CMakeCXXCompiler.cmake
A CppStatsCli/cmake-build-debug/CMakeFiles/3.10.3/CMakeDetermineCompilerABI_C.bin
A CppStatsCli/cmake-build-debug/CMakeFiles/3.10.3/CMakeDetermineCompilerABI_CXX.bin
A CppStatsCli/cmake-build-debug/CMakeFiles/3.10.3/CMakeSystem.cmake
A CppStatsCli/cmake-build-debug/CMakeFiles/3.10.3/CompilerIdC/CMakeCCompilerId.c
A CppStatsCli/cmake-build-debug/CMakeFiles/3.10.3/CompilerIdC/a.out
A CppStatsCli/cmake-build-debug/CMakeFiles/3.10.3/CompilerIdCXX/CMakeCXXCompilerId.cpp
A CppStatsCli/cmake-build-debug/CMakeFiles/3.10.3/CompilerIdCXX/a.out
A CppStatsCli/cmake-build-debug/CMakeFiles/CMakeDirectoryInformation.cmake
A CppStatsCli/cmake-build-debug/CMakeFiles/CMakeOutput.log
A CppStatsCli/cmake-build-debug/CMakeFiles/Makefile.cmake
A CppStatsCli/cmake-build-debug/CMakeFiles/Makefile2
A CppStatsCli/cmake-build-debug/CMakeFiles/TargetDirectories.txt
A CppStatsCli/cmake-build-debug/CMakeFiles/clion-environment.txt
A CppStatsCli/cmake-build-debug/CMakeFiles/clion-log.txt
A CppStatsCli/cmake-build-debug/CMakeFiles/cmake.check_cache
A CppStatsCli/cmake-build-debug/CMakeFiles/feature_tests.bin
A CppStatsCli/cmake-build-debug/CMakeFiles/feature_tests.c
A CppStatsCli/cmake-build-debug/CMakeFiles/feature_tests.cxx
A CppStatsCli/cmake-build-debug/CMakeFiles/prework.dir/DependInfo.cmake
A CppStatsCli/cmake-build-debug/CMakeFiles/prework.dir/build.make
A CppStatsCli/cmake-build-debug/CMakeFiles/prework.dir/cmake_clean.cmake
A CppStatsCli/cmake-build-debug/CMakeFiles/prework.dir/depend.make
A CppStatsCli/cmake-build-debug/CMakeFiles/prework.dir/flags.make
A CppStatsCli/cmake-build-debug/CMakeFiles/prework.dir/link.txt
A CppStatsCli/cmake-build-debug/CMakeFiles/prework.dir/progress.make
A CppStatsCli/cmake-build-debug/CMakeFiles/progress.marks
A CppStatsCli/cmake-build-debug/Makefile
A CppStatsCli/cmake-build-debug/cmake_install.cmake
A CppStatsCli/cmake-build-debug/prework
A CppStatsCli/cmake-build-debug/prework.cbp
A CppStatsCli/inputs.cpp
A CppStatsCli/inputs.h
A CppStatsCli/main.cpp
A CppStatsCli/sort.cpp
A CppStatsCli/sort.h
A CppStatsCli/stats.cpp
A CppStatsCli/stats.h
D Doppler
A Doppler/.gitignore
A Doppler/MANIFEST.in
A Doppler/README.md
A Doppler/assembler/__init__.py
A Doppler/assembler/parse.py
A Doppler/crawler/__init__.py
A Doppler/crawler/extract.py
A Doppler/data/game_content.db
A Doppler/doppler.png
A Doppler/doppler/__init__.py
A Doppler/doppler/doppler.py
A Doppler/doppler/utilities.py
A Doppler/eclipse_phase_cc_license
A Doppler/environment.yml
A Doppler/fate_srd_cc_license
A Doppler/license
A Doppler/main.py
A Doppler/setup.py
A Doppler/tests/__init__.py
A Doppler/tests/conftest.py
A Doppler/tests/unit/__init__.py
A Doppler/tests/unit/test_doppler.py
D brutale
A brutale/.gitignore
A brutale/LICENSE
A brutale/Makefile
A brutale/README.md
A brutale/VERSION
A brutale/content/a-simple-post.md
A brutale/content/example category/post-with-code.md
A brutale/content/extra-post.md
A brutale/content/images/pelly.png
A brutale/content/images/site-cover.jpg
A brutale/content/images/wilderness.jpg
A brutale/content/pages/example-page.md
A brutale/content/post-with-images.md
A brutale/content/releases/version-one.md
A brutale/pelicanconf.py
A brutale/plugins/gzip_cache/Readme.rst
A brutale/plugins/gzip_cache/__init__.py
A brutale/plugins/gzip_cache/gzip_cache.py
A brutale/plugins/gzip_cache/test_gzip_cache.py
A brutale/plugins/ipynb/LICENSE
A brutale/plugins/ipynb/README.md
A brutale/plugins/ipynb/__init__.py
A brutale/plugins/ipynb/core.py
A brutale/plugins/ipynb/ipynb.py
A brutale/plugins/ipynb/liquid.py
A brutale/plugins/ipynb/markup.py
A brutale/plugins/ipynb/requirements.txt
A brutale/plugins/ipynb/tests/__init__.py
A brutale/plugins/ipynb/tests/pelican/__init__.py
A brutale/plugins/ipynb/tests/pelican/content/with-liquid-tag.ipynb
A brutale/plugins/ipynb/tests/pelican/content/with-liquid-tag.md
A brutale/plugins/ipynb/tests/pelican/content/with-meta-file.ipynb
A brutale/plugins/ipynb/tests/pelican/content/with-meta-file.ipynb-meta
A brutale/plugins/ipynb/tests/pelican/content/with-metacell.ipynb
A brutale/plugins/ipynb/tests/pelican/pelicanconf_liquid.py
A brutale/plugins/ipynb/tests/pelican/pelicanconf_markup.py
A brutale/plugins/ipynb/tests/pelican/theme/templates/base.html
A brutale/plugins/optimize_images/Readme.md
A brutale/plugins/optimize_images/__init__.py
A brutale/plugins/optimize_images/optimize_images.py
A brutale/plugins/sitemap/Readme.rst
A brutale/plugins/sitemap/__init__.py
A brutale/plugins/sitemap/sitemap.py
A brutale/plugins/w3c_validate/README.md
A brutale/plugins/w3c_validate/__init__.py
A brutale/plugins/w3c_validate/wc3_validate.py
A brutale/publishconf.py
A brutale/requirements.txt
A brutale/themes/brutale/static/css/data-ttf.css
A brutale/themes/brutale/static/css/data-woff.css
A brutale/themes/brutale/static/css/data-woff2.css
A brutale/themes/brutale/static/css/icons.data.png.css
A brutale/themes/brutale/static/css/icons.data.svg.css
A brutale/themes/brutale/static/css/icons.fallback.css
A brutale/themes/brutale/static/css/main.css
A brutale/themes/brutale/static/css/main.css.map
A brutale/themes/brutale/static/css/png/email.png
A brutale/themes/brutale/static/css/png/facebook.png
A brutale/themes/brutale/static/css/png/foursquare.png
A brutale/themes/brutale/static/css/png/github.png
A brutale/themes/brutale/static/css/png/gitlab.png
A brutale/themes/brutale/static/css/png/goodreads.png
A brutale/themes/brutale/static/css/png/hackaday.png
A brutale/themes/brutale/static/css/png/instagram.png
A brutale/themes/brutale/static/css/png/keybase.png
A brutale/themes/brutale/static/css/png/linkedin.png
A brutale/themes/brutale/static/css/png/mastadon.png
A brutale/themes/brutale/static/css/png/medium.png
A brutale/themes/brutale/static/css/png/pgp.png
A brutale/themes/brutale/static/css/png/rss.png
A brutale/themes/brutale/static/css/png/stackexchange.png
A brutale/themes/brutale/static/css/png/stackoverflow.png
A brutale/themes/brutale/static/css/png/strava.png
A brutale/themes/brutale/static/css/png/telegram.png
A brutale/themes/brutale/static/css/png/twitter.png
A brutale/themes/brutale/static/css/png/untappd.png
A brutale/themes/brutale/static/css/png/youtube.png
A brutale/themes/brutale/static/css/sass/_mixins.sass
A brutale/themes/brutale/static/css/sass/_pygment.sass
A brutale/themes/brutale/static/css/sass/_reset.sass
A brutale/themes/brutale/static/css/sass/main.sass
A brutale/themes/brutale/static/css/svg/email.svg
A brutale/themes/brutale/static/css/svg/github.svg
A brutale/themes/brutale/static/css/svg/gitlab.svg
A brutale/themes/brutale/static/css/svg/hackaday.svg
A brutale/themes/brutale/static/css/svg/keybase.svg
A brutale/themes/brutale/static/css/svg/linkedin.svg
A brutale/themes/brutale/static/css/svg/mastadon.svg
A brutale/themes/brutale/static/css/svg/pgp.svg
A brutale/themes/brutale/static/css/svg/rss.svg
A brutale/themes/brutale/static/css/svg/stackexchange.svg
A brutale/themes/brutale/static/css/svg/stackoverflow.svg
A brutale/themes/brutale/static/css/svg/youtube.svg
A brutale/themes/brutale/static/fonts/lato-black-webfont.ttf
A brutale/themes/brutale/static/fonts/lato-black-webfont.woff
A brutale/themes/brutale/static/fonts/lato-black-webfont.woff2
A brutale/themes/brutale/static/fonts/lato-light-webfont.ttf
A brutale/themes/brutale/static/fonts/lato-light-webfont.woff
A brutale/themes/brutale/static/fonts/lato-light-webfont.woff2
A brutale/themes/brutale/static/fonts/lato-regular-webfont.ttf
A brutale/themes/brutale/static/fonts/lato-regular-webfont.woff
A brutale/themes/brutale/static/fonts/lato-regular-webfont.woff2
A brutale/themes/brutale/static/js/moment.js
A brutale/themes/brutale/templates/analytics.html
A brutale/themes/brutale/templates/archives.html
A brutale/themes/brutale/templates/article.html
A brutale/themes/brutale/templates/article_list.html
A brutale/themes/brutale/templates/author.html
A brutale/themes/brutale/templates/authors.html
A brutale/themes/brutale/templates/base.html
A brutale/themes/brutale/templates/categories.html
A brutale/themes/brutale/templates/category.html
A brutale/themes/brutale/templates/github.html
A brutale/themes/brutale/templates/index.html
A brutale/themes/brutale/templates/page.html
A brutale/themes/brutale/templates/pagination.html
A brutale/themes/brutale/templates/period_archives.html
A brutale/themes/brutale/templates/tag.html
A brutale/themes/brutale/templates/tags.html
D librooma
A librooma/.gitignore
A librooma/README.md
A librooma/baud.ino
A librooma/docs/circuit_playground_Adafruit_Circuit_Playground_Express_Pinout.png
A librooma/docs/iRobot_Roomba_600_Open_Interface_Spec.pdf
A librooma/img/roomba_connectors.jpeg
A librooma/img/roomba_oi_one.jpeg
A librooma/pulse.ino
A librooma/roomba.ino
A librooma/run.ino
D n0mn0m.gitlab.io
A n0mn0m.gitlab.io/.gitignore
A n0mn0m.gitlab.io/.gitlab-ci.yml
A n0mn0m.gitlab.io/.well-known/acme-challenge/8M4AOctLwHjGldKRUbdJaY1VqNpbgtiEM20wVSfEJWI
A n0mn0m.gitlab.io/.well-known/acme-challenge/RfQo-FuGZxJ6YsuuJVW4t895K81ZL9qMLzeDsJsHtpM
A n0mn0m.gitlab.io/CNAME
A n0mn0m.gitlab.io/LICENSE
A n0mn0m.gitlab.io/Makefile
A n0mn0m.gitlab.io/README.md
A n0mn0m.gitlab.io/content/circuitpython/gen-con-pyportal.md
A n0mn0m.gitlab.io/content/database/what-is-odbc-pt2.md
A n0mn0m.gitlab.io/content/database/what-is-odbc-pt3.md
A n0mn0m.gitlab.io/content/database/what-is-odbc.md
A n0mn0m.gitlab.io/content/hardware/connected-roomba-lora.md
A n0mn0m.gitlab.io/content/hardware/connected-roomba-prototype.md
A n0mn0m.gitlab.io/content/hardware/connected-roomba-remote-commands.md
A n0mn0m.gitlab.io/content/hardware/connected-roomba-wrap-up.md
A n0mn0m.gitlab.io/content/hardware/hackaday-connected-world.md
A n0mn0m.gitlab.io/content/images/add-dhcp.png
A n0mn0m.gitlab.io/content/images/con-portal-with-case-badge.jpg
A n0mn0m.gitlab.io/content/images/con-portal-with-case-countdown.jpg
A n0mn0m.gitlab.io/content/images/con-portal-with-case-d20.jpg
A n0mn0m.gitlab.io/content/images/con-portal-with-case-home.jpg
A n0mn0m.gitlab.io/content/images/con-portal.jpg
A n0mn0m.gitlab.io/content/images/connected-roomba-connections.jpg
A n0mn0m.gitlab.io/content/images/connected-roomba-demo.mp4
A n0mn0m.gitlab.io/content/images/connected-roomba-feather-connections.jpg
A n0mn0m.gitlab.io/content/images/connected-roomba-front.jpg
A n0mn0m.gitlab.io/content/images/connected-roomba-full.jpg
A n0mn0m.gitlab.io/content/images/connected-roomba-pi-boot.jpg
A n0mn0m.gitlab.io/content/images/connected-roomba-pi-start.jpg
A n0mn0m.gitlab.io/content/images/connected-roomba-pi-stop.jpg
A n0mn0m.gitlab.io/content/images/connected-roomba-proto.jpg
A n0mn0m.gitlab.io/content/images/cypy.png
A n0mn0m.gitlab.io/content/images/dhcp-config.png
A n0mn0m.gitlab.io/content/images/dhcp-dns.png
A n0mn0m.gitlab.io/content/images/favicon.ico
A n0mn0m.gitlab.io/content/images/name-server.png
A n0mn0m.gitlab.io/content/images/profile.jpg
A n0mn0m.gitlab.io/content/images/profile_round.png
A n0mn0m.gitlab.io/content/images/ubq-action.png
A n0mn0m.gitlab.io/content/images/ubq-services.png
A n0mn0m.gitlab.io/content/images/ubq-system.png
A n0mn0m.gitlab.io/content/pages/about.md
A n0mn0m.gitlab.io/content/pages/book-shelf.md
A n0mn0m.gitlab.io/content/pages/podcast.md
A n0mn0m.gitlab.io/content/pages/resume.md
A n0mn0m.gitlab.io/content/pages/tools.md
A n0mn0m.gitlab.io/content/postgres/async-postgres-locks.md
A n0mn0m.gitlab.io/content/python/dataclasses-as-config-objects.md
A n0mn0m.gitlab.io/content/python/dealing-with-null-transformations-in-pyspark.ipynb
A n0mn0m.gitlab.io/content/python/dealing-with-null-transformations-in-pyspark.nbdata
A n0mn0m.gitlab.io/content/python/modules-and-packages.ipynb
A n0mn0m.gitlab.io/content/python/modules-and-packages.nbdata
A n0mn0m.gitlab.io/content/python/pelican_on_windows.md
A n0mn0m.gitlab.io/content/python/pyspark-intro.md
A n0mn0m.gitlab.io/content/python/recurse.md
A n0mn0m.gitlab.io/content/sysadmin/edgerouter-x-ip-reporting.md
A n0mn0m.gitlab.io/content/sysadmin/edgerouter-x-pihole-setup.md
A n0mn0m.gitlab.io/content/sysadmin/edgerouter-x-vpn-setup-prt-one.md
A n0mn0m.gitlab.io/content/sysadmin/edgerouter-x-vpn-setup-prt-two.md
A n0mn0m.gitlab.io/content/tools/docker-airflow.md
A n0mn0m.gitlab.io/content/tools/vim-anaconda-build.md
A n0mn0m.gitlab.io/content/tools/vim-rust-2019.md
A n0mn0m.gitlab.io/content/web/accessible-static-sites.md
A n0mn0m.gitlab.io/content/web/wildcard-letsencrypt.md
A n0mn0m.gitlab.io/content/writing/first.md
A n0mn0m.gitlab.io/letsencrypt-renewal/letsencrypt_authenticator.sh
A n0mn0m.gitlab.io/letsencrypt-renewal/letsencrypt_cleanup.sh
A n0mn0m.gitlab.io/letsencrypt-renewal/letsencrypt_generate.sh
A n0mn0m.gitlab.io/pelicanconf.py
A n0mn0m.gitlab.io/plugins/category_order/Readme.rst
A n0mn0m.gitlab.io/plugins/category_order/__init__.py
A n0mn0m.gitlab.io/plugins/category_order/category_order.py
A n0mn0m.gitlab.io/plugins/gzip_cache/Readme.rst
A n0mn0m.gitlab.io/plugins/gzip_cache/__init__.py
A n0mn0m.gitlab.io/plugins/gzip_cache/gzip_cache.py
A n0mn0m.gitlab.io/plugins/gzip_cache/test_gzip_cache.py
A n0mn0m.gitlab.io/plugins/ipynb/LICENSE
A n0mn0m.gitlab.io/plugins/ipynb/README.md
A n0mn0m.gitlab.io/plugins/ipynb/__init__.py
A n0mn0m.gitlab.io/plugins/ipynb/core.py
A n0mn0m.gitlab.io/plugins/ipynb/ipynb.py
A n0mn0m.gitlab.io/plugins/ipynb/liquid.py
A n0mn0m.gitlab.io/plugins/ipynb/markup.py
A n0mn0m.gitlab.io/plugins/ipynb/requirements.txt
A n0mn0m.gitlab.io/plugins/ipynb/tests/__init__.py
A n0mn0m.gitlab.io/plugins/ipynb/tests/pelican/__init__.py
A n0mn0m.gitlab.io/plugins/ipynb/tests/pelican/content/with-liquid-tag.ipynb
A n0mn0m.gitlab.io/plugins/ipynb/tests/pelican/content/with-liquid-tag.md
A n0mn0m.gitlab.io/plugins/ipynb/tests/pelican/content/with-meta-file.ipynb
A n0mn0m.gitlab.io/plugins/ipynb/tests/pelican/content/with-meta-file.ipynb-meta
A n0mn0m.gitlab.io/plugins/ipynb/tests/pelican/content/with-metacell.ipynb
A n0mn0m.gitlab.io/plugins/ipynb/tests/pelican/pelicanconf_liquid.py
A n0mn0m.gitlab.io/plugins/ipynb/tests/pelican/pelicanconf_markup.py
A n0mn0m.gitlab.io/plugins/ipynb/tests/pelican/theme/templates/base.html
A n0mn0m.gitlab.io/plugins/optimize_images/Readme.md
A n0mn0m.gitlab.io/plugins/optimize_images/__init__.py
A n0mn0m.gitlab.io/plugins/optimize_images/optimize_images.py
A n0mn0m.gitlab.io/plugins/sitemap/Readme.rst
A n0mn0m.gitlab.io/plugins/sitemap/__init__.py
A n0mn0m.gitlab.io/plugins/sitemap/sitemap.py
A n0mn0m.gitlab.io/plugins/w3c_validate/README.md
A n0mn0m.gitlab.io/plugins/w3c_validate/__init__.py
A n0mn0m.gitlab.io/plugins/w3c_validate/wc3_validate.py
A n0mn0m.gitlab.io/publishconf.py
A n0mn0m.gitlab.io/requirements.txt
A n0mn0m.gitlab.io/themes/brutalist/static/css/data-ttf.css
A n0mn0m.gitlab.io/themes/brutalist/static/css/data-woff.css
A n0mn0m.gitlab.io/themes/brutalist/static/css/data-woff2.css
A n0mn0m.gitlab.io/themes/brutalist/static/css/icons.data.png.css
A n0mn0m.gitlab.io/themes/brutalist/static/css/icons.data.svg.css
A n0mn0m.gitlab.io/themes/brutalist/static/css/icons.fallback.css
A n0mn0m.gitlab.io/themes/brutalist/static/css/main.css
A n0mn0m.gitlab.io/themes/brutalist/static/css/main.css.map
A n0mn0m.gitlab.io/themes/brutalist/static/css/png/email.png
A n0mn0m.gitlab.io/themes/brutalist/static/css/png/github.png
A n0mn0m.gitlab.io/themes/brutalist/static/css/png/gitlab.png
A n0mn0m.gitlab.io/themes/brutalist/static/css/png/hackaday.png
A n0mn0m.gitlab.io/themes/brutalist/static/css/png/keybase.png
A n0mn0m.gitlab.io/themes/brutalist/static/css/png/linkedin.png
A n0mn0m.gitlab.io/themes/brutalist/static/css/png/mastadon.png
A n0mn0m.gitlab.io/themes/brutalist/static/css/png/pgp.png
A n0mn0m.gitlab.io/themes/brutalist/static/css/png/rss.png
A n0mn0m.gitlab.io/themes/brutalist/static/css/png/stackexchange.png
A n0mn0m.gitlab.io/themes/brutalist/static/css/png/stackoverflow.png
A n0mn0m.gitlab.io/themes/brutalist/static/css/png/youtube.png
A n0mn0m.gitlab.io/themes/brutalist/static/css/sass/_mixins.sass
A n0mn0m.gitlab.io/themes/brutalist/static/css/sass/_pygment.sass
A n0mn0m.gitlab.io/themes/brutalist/static/css/sass/_reset.sass
A n0mn0m.gitlab.io/themes/brutalist/static/css/sass/main.sass
A n0mn0m.gitlab.io/themes/brutalist/static/css/svg/email.svg
A n0mn0m.gitlab.io/themes/brutalist/static/css/svg/github.svg
A n0mn0m.gitlab.io/themes/brutalist/static/css/svg/gitlab.svg
A n0mn0m.gitlab.io/themes/brutalist/static/css/svg/hackaday.svg
A n0mn0m.gitlab.io/themes/brutalist/static/css/svg/keybase.svg
A n0mn0m.gitlab.io/themes/brutalist/static/css/svg/linkedin.svg
A n0mn0m.gitlab.io/themes/brutalist/static/css/svg/mastadon.svg
A n0mn0m.gitlab.io/themes/brutalist/static/css/svg/pgp.svg
A n0mn0m.gitlab.io/themes/brutalist/static/css/svg/rss.svg
A n0mn0m.gitlab.io/themes/brutalist/static/css/svg/stackexchange.svg
A n0mn0m.gitlab.io/themes/brutalist/static/css/svg/stackoverflow.svg
A n0mn0m.gitlab.io/themes/brutalist/static/css/svg/youtube.svg
A n0mn0m.gitlab.io/themes/brutalist/static/fonts/lato-black-webfont.ttf
A n0mn0m.gitlab.io/themes/brutalist/static/fonts/lato-black-webfont.woff
A n0mn0m.gitlab.io/themes/brutalist/static/fonts/lato-black-webfont.woff2
A n0mn0m.gitlab.io/themes/brutalist/static/fonts/lato-light-webfont.ttf
A n0mn0m.gitlab.io/themes/brutalist/static/fonts/lato-light-webfont.woff
A n0mn0m.gitlab.io/themes/brutalist/static/fonts/lato-light-webfont.woff2
A n0mn0m.gitlab.io/themes/brutalist/static/fonts/lato-regular-webfont.ttf
A n0mn0m.gitlab.io/themes/brutalist/static/fonts/lato-regular-webfont.woff
A n0mn0m.gitlab.io/themes/brutalist/static/fonts/lato-regular-webfont.woff2
A n0mn0m.gitlab.io/themes/brutalist/static/js/moment.js
A n0mn0m.gitlab.io/themes/brutalist/templates/analytics.html
A n0mn0m.gitlab.io/themes/brutalist/templates/archives.html
A n0mn0m.gitlab.io/themes/brutalist/templates/article.html
A n0mn0m.gitlab.io/themes/brutalist/templates/article_list.html
A n0mn0m.gitlab.io/themes/brutalist/templates/author.html
A n0mn0m.gitlab.io/themes/brutalist/templates/authors.html
A n0mn0m.gitlab.io/themes/brutalist/templates/base.html
A n0mn0m.gitlab.io/themes/brutalist/templates/categories.html
A n0mn0m.gitlab.io/themes/brutalist/templates/category.html
A n0mn0m.gitlab.io/themes/brutalist/templates/github.html
A n0mn0m.gitlab.io/themes/brutalist/templates/index.html
A n0mn0m.gitlab.io/themes/brutalist/templates/page.html
A n0mn0m.gitlab.io/themes/brutalist/templates/pagination.html
A n0mn0m.gitlab.io/themes/brutalist/templates/period_archives.html
A n0mn0m.gitlab.io/themes/brutalist/templates/tag.html
A n0mn0m.gitlab.io/themes/brutalist/templates/tags.html
D .gitmodules => .gitmodules +0 -24
@@ 1,24 0,0 @@
[submodule "Doppler"]
	path = Doppler
	url = git@gitlab.com:n0mn0m/Doppler.git
[submodule "librooma"]
	path = librooma
	url = git@gitlab.com:n0mn0m/librooma.git
[submodule "CppStatsCli"]
	path = CppStatsCli
	url = git@github.com:n0mn0m/CppStatsCli.git
[submodule "CodeLouisvilleDjango"]
	path = CodeLouisvilleDjango
	url = git@github.com:n0mn0m/CodeLouisvilleDjango.git
[submodule "BattlenetAuthomatic"]
	path = BattlenetAuthomatic
	url = git@github.com:n0mn0m/BattlenetAuthomatic.git
[submodule "CommercialLaunchScraper"]
	path = CommercialLaunchScraper
	url = git@github.com:n0mn0m/CommercialLaunchScraper.git
[submodule "n0mn0m.gitlab.io"]
	path = n0mn0m.gitlab.io
	url = git@gitlab.com:n0mn0m/n0mn0m.gitlab.io.git
[submodule "brutale"]
	path = brutale
	url = git@git.sr.ht:~n0mn0m/brutale

D BattlenetAuthomatic => BattlenetAuthomatic +0 -1
@@ 1,1 0,0 @@
Subproject commit d9f3e8bc57b157891b0f7393e0b90a0e65d32bac

A BattlenetAuthomatic/.gitattributes => BattlenetAuthomatic/.gitattributes +17 -0
@@ 0,0 1,17 @@
# Auto detect text files and perform LF normalization
* text=auto

# Custom for Visual Studio
*.cs     diff=csharp

# Standard to msysgit
*.doc	 diff=astextplain
*.DOC	 diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot  diff=astextplain
*.DOT  diff=astextplain
*.pdf  diff=astextplain
*.PDF	 diff=astextplain
*.rtf	 diff=astextplain
*.RTF	 diff=astextplain

A BattlenetAuthomatic/.gitignore => BattlenetAuthomatic/.gitignore +47 -0
@@ 0,0 1,47 @@
# Windows image file caches
Thumbs.db
ehthumbs.db

# Folder config file
Desktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Windows Installer files
*.cab
*.msi
*.msm
*.msp

# Windows shortcuts
*.lnk

# =========================
# Operating System Files
# =========================

# OSX
# =========================

.DS_Store
.AppleDouble
.LSOverride

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

A BattlenetAuthomatic/LICENSE.txt => BattlenetAuthomatic/LICENSE.txt +24 -0
@@ 0,0 1,24 @@
The MIT License (MIT)
Copyright (c) 2012 Peter Hudec

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:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions of
the Software.

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

A BattlenetAuthomatic/README.md => BattlenetAuthomatic/README.md +5 -0
@@ 0,0 1,5 @@
## Flask Blizzard OAuth

This project extends the [authomatic project](http://peterhudec.com/authomatic/) with a basic Blizzard OAuth 2 provider.

Make sure to plug in your key/secret in the config.py file
\ No newline at end of file

A BattlenetAuthomatic/authomatic/__init__.py => BattlenetAuthomatic/authomatic/__init__.py +22 -0
@@ 0,0 1,22 @@
# -*- coding: utf-8 -*-
"""
This is the only interface that you should ever need to get a **user** logged in, get
**his/her** info and credentials, deserialize the credentials
and access **his/her protected resources**.

.. autosummary::
    :nosignatures:
    
    authomatic.setup
    authomatic.login
    authomatic.provider_id
    authomatic.access
    authomatic.async_access
    authomatic.credentials
    authomatic.request_elements
    authomatic.backend

"""

from . import six
from .core import Authomatic, setup, login, provider_id, access, async_access, credentials, request_elements, backend

A BattlenetAuthomatic/authomatic/adapters.py => BattlenetAuthomatic/authomatic/adapters.py +273 -0
@@ 0,0 1,273 @@
# -*- coding: utf-8 -*-
"""
Adapters
--------

.. contents::
   :backlinks: none

The :func:`authomatic.login` function needs access to functionality like
getting the **URL** of the handler where it is being called, getting the **request params** and **cookies** and
**writing the body**, **headers** and **status** to the response.

Since implementation of these features varies across Python web frameworks,
the Authomatic library uses **adapters** to unify these differences into a single interface.

Available Adapters
^^^^^^^^^^^^^^^^^^

If you are missing an adapter for the framework of your choice,
please open an `enhancement issue <https://github.com/peterhudec/authomatic/issues>`_
or consider a contribution to this module by :ref:`implementing <implement_adapters>` one by yourself.
Its very easy and shouldn't take you more than a few minutes.

.. autoclass:: DjangoAdapter
    :members:

.. autoclass:: Webapp2Adapter
    :members:

.. autoclass:: WebObAdapter
    :members:

.. autoclass:: WerkzeugAdapter
    :members:

.. _implement_adapters:

Implementing an Adapter
^^^^^^^^^^^^^^^^^^^^^^^

Implementing an adapter for a Python web framework is pretty easy.

Do it by subclassing the :class:`.BaseAdapter` abstract class.
There are only **six** members that you need to implement.

Moreover if your framework is based on the |webob|_ or |werkzeug|_ package
you can subclass the :class:`.WebObAdapter` or :class:`.WerkzeugAdapter` respectively.

.. autoclass:: BaseAdapter
    :members:

"""

import abc
from authomatic.core import Response


class BaseAdapter(object):
    """
    Base class for platform adapters

    Defines common interface for WSGI framework specific functionality.
    """

    __metaclass__ = abc.ABCMeta

    @abc.abstractproperty
    def params(self):
        """
        Must return a :class:`dict` of all request parameters of any HTTP method.

        :returns:
            :class:`dict`
        """


    @abc.abstractproperty
    def url(self):
        """
        Must return the url of the actual request including path but without query and fragment

        :returns:
            :class:`str`
        """


    @abc.abstractproperty
    def cookies(self):
        """
        Must return cookies as a :class:`dict`.

        :returns:
            :class:`dict`
        """


    @abc.abstractmethod
    def write(self, value):
        """
        Must write specified value to response.

        :param str value:
            String to be written to response.
        """


    @abc.abstractmethod
    def set_header(self, key, value):
        """
        Must set response headers to ``Key: value``.

        :param str key:
            Header name.

        :param str value:
            Header value.
        """


    @abc.abstractmethod
    def set_status(self, status):
        """
        Must set the response status e.g. ``'302 Found'``.

        :param str status:
            The HTTP response status.
        """


class DjangoAdapter(BaseAdapter):
    """
    Adapter for the |django|_ framework.
    """
    
    def __init__(self, request, response):
        """                
        :param request:
            An instance of the :class:`django.http.HttpRequest` class.
            
        :param response:
            An instance of the :class:`django.http.HttpResponse` class.
        """
        self.request = request
        self.response = response
    
    @property
    def params(self):
        return dict(self.request.REQUEST)
    
    @property
    def url(self):
        return self.request.build_absolute_uri(self.request.path)
    
    @property
    def cookies(self):
        return dict(self.request.COOKIES)
    
    def write(self, value):
        self.response.write(value)
    
    def set_header(self, key, value):
        self.response[key] = value
        
    def set_status(self, status):
        status_code, reason = status.split(' ', 1)
        self.response.status_code = int(status_code)


class WebObAdapter(BaseAdapter):
    """Adapter for the |webob|_ package."""
    
    def __init__(self, request, response):
        """
        :param request:
            A |webob|_ :class:`Request` instance.
            
        :param response:
            A |webob|_ :class:`Response` instance.
        """
        self.request = request
        self.response = response
        

    #===========================================================================
    # Request
    #===========================================================================

    @property
    def url(self):
        return self.request.path_url


    @property
    def params(self):
        return dict(self.request.params)


    @property
    def cookies(self):
        return dict(self.request.cookies)


    #===========================================================================
    # Response
    #===========================================================================

    def write(self, value):
        self.response.write(value)


    def set_header(self, key, value):
        self.response.headers[key] = str(value)


    def set_status(self, status):
        self.response.status = status


class Webapp2Adapter(WebObAdapter):
    """
    Adapter for the |webapp2|_ framework.
    
    Inherits from the :class:`.WebObAdapter`.
    """

    def __init__(self, handler):
        """
        :param handler:
            A :class:`webapp2.RequestHandler` instance.
        """
        self.request = handler.request
        self.response = handler.response


class WerkzeugAdapter(BaseAdapter):
    """
    Adapter for |flask|_ and other |werkzeug|_ based frameworks.
    
    Thanks to `Mark Steve Samson <http://marksteve.com>`_.
    """

    @property
    def params(self):
        return self.request.args

    @property
    def url(self):
        return self.request.base_url

    @property
    def cookies(self):
        return self.request.cookies

    def __init__(self, request, response):
        """
        :param request:
            Instance of the :class:`werkzeug.wrappers.Request` class.
            
        :param response:
            Instance of the :class:`werkzeug.wrappers.Response` class.
        """
        
        self.request = request
        self.response = response

    def write(self, value):
        self.response.data = self.response.data.decode('utf-8') + value

    def set_header(self, key, value):
        self.response.headers[key] = value

    def set_status(self, status):
        self.response.status = status

A BattlenetAuthomatic/authomatic/core.py => BattlenetAuthomatic/authomatic/core.py +1796 -0
@@ 0,0 1,1796 @@
# -*- coding: utf-8 -*-

import collections
import copy
import datetime
from . import exceptions
import hashlib
import hmac
import json
import logging
try:
    import cPickle as pickle
except ImportError:
    import pickle
import sys
import threading
import time
from xml.etree import ElementTree

from authomatic.exceptions import (
    ConfigError,
    CredentialsError,
    ImportStringError,
    RequestElementsError,
    SessionError,
)
from authomatic import six
from authomatic.six.moves import urllib_parse as parse


#===============================================================================
# Global variables !!!
#===============================================================================

_logger = logging.getLogger(__name__)
_logger.addHandler(logging.StreamHandler(sys.stdout))

_counter = None


def normalize_dict(dict_):
    """
    Replaces all values that are single-item iterables with the value of its index 0.

    :param dict dict_:
        Dictionary to normalize.

    :returns:
        Normalized dictionary.
    """

    return dict([(k, v[0] if not isinstance(v, str) and len(v) == 1 else v)
                 for k, v in list(dict_.items())])


def items_to_dict(items):
    """
    Converts list of tuples to dictionary with duplicate keys converted to lists.

    :param list items:
        List of tuples.

    :returns:
        :class:`dict`
    """

    res = collections.defaultdict(list)

    for k, v in items:
        res[k].append(v)

    return normalize_dict(dict(res))


class Counter(object):
    """
    A simple counter to be used in the config to generate unique `id` values.
    """

    def __init__(self, start=0):
        self._count = start

    def count(self):
        self._count += 1
        return self._count

_counter = Counter()

def provider_id():
    """
    A simple counter to be used in the config to generate unique `IDs`.

    :returns:
        :class:`int`.

    Use it in the :doc:`config` like this:
    ::

        import authomatic

        CONFIG = {
            'facebook': {
                 'class_': authomatic.providers.oauth2.Facebook,
                 'id': authomatic.provider_id(), # returns 1
                 'consumer_key': '##########',
                 'consumer_secret': '##########',
                 'scope': ['user_about_me', 'email']
            },
            'google': {
                 'class_': 'authomatic.providers.oauth2.Google',
                 'id': authomatic.provider_id(), # returns 2
                 'consumer_key': '##########',
                 'consumer_secret': '##########',
                 'scope': ['https://www.googleapis.com/auth/userinfo.profile',
                           'https://www.googleapis.com/auth/userinfo.email']
            },
            'windows_live': {
                 'class_': 'oauth2.WindowsLive',
                 'id': authomatic.provider_id(), # returns 3
                 'consumer_key': '##########',
                 'consumer_secret': '##########',
                 'scope': ['wl.basic', 'wl.emails', 'wl.photos']
            },
        }
    """

    return _counter.count()


def escape(s):
    """Escape a URL including any /."""
    return parse.quote(s.encode('utf-8'), safe='~')

def json_qs_parser(body):
    """
    Parses response body from JSON, XML or query string.

    :param body:
        string

    :returns:
        :class:`dict`, :class:`list` if input is JSON or query string,
        :class:`xml.etree.ElementTree.Element` if XML.
    """
    try:
        # Try JSON first.
        return json.loads(body)
    except:
        pass

    try:
        # Then XML.
        return ElementTree.fromstring(body)
    except:
        pass

    # Finally query string.
    return dict(parse.parse_qsl(body))


def import_string(import_name, silent=False):
    """
    Imports an object by string in dotted notation.

    taken `from webapp2.import_string() <http://webapp-improved.appspot.com/api/webapp2.html#webapp2.import_string>`_
    """

    try:
        if '.' in import_name:
            module, obj = import_name.rsplit('.', 1)
            return getattr(__import__(module, None, None, [obj]), obj)
        else:
            return __import__(import_name)
    except (ImportError, AttributeError) as e:
        if not silent:
            raise ImportStringError('Import from string failed for path {0}'
                                    .format(import_name), str(e))


def resolve_provider_class(class_):
    """
    Returns a provider class.

    :param class_name: :class:`string` or :class:`authomatic.providers.BaseProvider` subclass.
    """

    if isinstance(class_, str):
        # prepare path for authomatic.providers package
        path = '.'.join([__package__, 'providers', class_])

        # try to import class by string from providers module or by fully qualified path
        return import_string(class_, True) or import_string(path)
    else:
        return class_


def id_to_name(config, short_name):
    """
    Returns the provider :doc:`config` key based on it's
    ``id`` value.

    :param dict config:
        :doc:`config`.
    :param id:
        Value of the id parameter in the :ref:`config` to search for.
    """

    for k, v in list(config.items()):
        if v.get('id') == short_name:
            return k
            break
    else:
        raise Exception('No provider with id={0} found in the config!'.format(short_name))


class ReprMixin(object):
    """
    Provides __repr__() method with output *ClassName(arg1=value, arg2=value)*.

    Ignored are attributes

    * which values are considered false.
    * with leading underscore.
    * listed in _repr_ignore.

    Values of attributes listed in _repr_sensitive will be replaced by *###*.
    Values which repr() string is longer than _repr_length_limit will be represented as *ClassName(...)*
    """

    #: Iterable of attributes to be ignored.
    _repr_ignore = []
    #: Iterable of attributes which value should not be visible.
    _repr_sensitive = []
    #: `int` Values longer than this will be truncated to *ClassName(...)*.
    _repr_length_limit = 20


    def __repr__(self):

        # get class name
        name = self.__class__.__name__

        # construct keyword arguments
        args = []

        for k, v in list(self.__dict__.items()):

            # ignore attributes with leading underscores and those listed in _repr_ignore
            if v and not k.startswith('_') and not k in self._repr_ignore:

                # replace sensitive values
                if k in self._repr_sensitive:
                    v = '###'

                # if repr is too long
                if len(repr(v)) > self._repr_length_limit:
                    # Truncate to ClassName(...)
                    v = '{0}(...)'.format(v.__class__.__name__)
                else:
                    v = repr(v)

                args.append('{0}={1}'.format(k, v))

        args = ', '.join(args)

        return '{0}({1})'.format(name, args)


class Future(threading.Thread):
    """
    Represents an activity run in a separate thread.
    Subclasses the standard library :class:`threading.Thread` and adds :attr:`.get_result` method.

    .. warning::

        |async|

    """

    def __init__(self, func, *args, **kwargs):
        """
        :param callable func:
            The function to be run in separate thread.

        Calls :data:`func` in separate thread and returns immediately.
        Accepts arbitrary positional and keyword arguments which will be passed to :data:`func`.
        """

        super(Future, self).__init__()
        self._func = func
        self._args = args
        self._kwargs = kwargs
        self._result = None

        self.start()


    def run(self):
        self._result = self._func(*self._args, **self._kwargs)


    def get_result(self, timeout=None):
        """
        Waits for the wrapped :data:`func` to finish and returns its result.

        .. note::

            This will block the **calling thread** until the :data:`func` returns.

        :param timeout:
            :class:`float` or ``None`` A timeout for the :data:`func` to return in seconds.

        :returns:
            The result of the wrapped :data:`func`.
        """

        self.join(timeout)
        return self._result


class Session(object):
    """A dictionary-like secure cookie session implementation."""
    def __init__(self, adapter, secret, name='authomatic', max_age=600,
                 secure=False):
        """
        :param str secret:
            Session secret used to sign the session cookie.
        :param str name:
            Session cookie name.
        :param int max_age:
            Maximum allowed age of session cookie nonce in seconds.
        :param bool secure:
            If ``True`` the session cookie will be saved with ``Secure``
            attribute.
        """

        self.adapter = adapter
        self.name = name
        self.secret = secret
        self.max_age = max_age
        self.secure = secure
        self._data = {}

    def create_cookie(self, delete=None):
        """
        Creates the value for ``Set-Cookie`` HTTP header.

        :param bool delete:
            If ``True`` the cookie value will be ``deleted`` and the
            Expires value will be ``Thu, 01-Jan-1970 00:00:01 GMT``.
        """
        value = 'deleted' if delete else self._serialize(self.data)
        split_url = parse.urlsplit(self.adapter.url)
        domain = split_url.netloc.split(':')[0]

        # Work-around for issue #11, failure of WebKit-based browsers to accept
        # cookies set as part of a redirect response in some circumstances.
        if '.' not in domain:
            template = '{name}={value}; Path={path}; HttpOnly{secure}{expires}'
        else:
            template = ('{name}={value}; Domain={domain}; Path={path}; '
                        'HttpOnly{secure}{expires}')

        return template.format(
            name=self.name,
            value=value,
            domain=domain,
            path=split_url.path,
            secure='; Secure' if self.secure else '',
            expires='; Expires=Thu, 01-Jan-1970 00:00:01 GMT' if delete else ''
        )

    def save(self):
        """Adds the session cookie to headers."""
        if self.data:
            cookie = self.create_cookie()
            cookie_len = len(cookie)

            if cookie_len > 4093:
                raise SessionError('Cookie too long! The cookie size {0} '
                                   'is more than 4093 bytes.'
                                   .format(cookie_len))

            self.adapter.set_header('Set-Cookie', cookie)

            # Reset data
            self._data = {}

    def delete(self):
        self.adapter.set_header('Set-Cookie', self.create_cookie(delete=True))

    def _get_data(self):
        """Extracts the session data from cookie."""
        cookie = self.adapter.cookies.get(self.name)
        return self._deserialize(cookie) if cookie else {}

    @property
    def data(self):
        """Gets session data lazily."""
        if not self._data:
            self._data = self._get_data()
        # Always return a dict, even if deserialization returned nothing
        if self._data is None:
            self._data = {}
        return self._data

    def _signature(self, *parts):
        """Creates signature for the session."""
        signature = hmac.new(six.b(self.secret), digestmod=hashlib.sha1)
        signature.update(six.b('|'.join(parts)))
        return signature.hexdigest()

    def _serialize(self, value):
        """
        Converts the value to a signed string with timestamp.

        :param value:
            Object to be serialized.

        :returns:
            Serialized value.
        """

        # data = copy.deepcopy(value)
        data = value

        # 1. Serialize
        serialized = pickle.dumps(data).decode('latin-1')

        # 2. Encode
        # Percent encoding produces smaller result then urlsafe base64.
        encoded = parse.quote(serialized, '')

        # 3. Concatenate
        timestamp = str(int(time.time()))
        signature = self._signature(self.name, encoded, timestamp)
        concatenated = '|'.join([encoded, timestamp, signature])

        return concatenated


    def _deserialize(self, value):
        """
        Deserializes and verifies the value created by :meth:`._serialize`.

        :param str value:
            The serialized value.

        :returns:
            Deserialized object.
        """

        # 3. Split
        encoded, timestamp, signature = value.split('|')

        # Verify signature
        if not signature == self._signature(self.name, encoded, timestamp):
            raise SessionError('Invalid signature "{0}"!'.format(signature))

        # Verify timestamp
        if int(timestamp) < int(time.time()) - self.max_age:
            return None

        # 2. Decode
        decoded = parse.unquote(encoded)

        # 1. Deserialize
        deserialized = pickle.loads(decoded.encode('latin-1'))

        return deserialized

    def __setitem__(self, key, value):
        self._data[key] = value

    def __getitem__(self, key):
        return self.data.__getitem__(key)

    def __delitem__(self, key):
        return self._data.__delitem__(key)

    def get(self, key, default=None):
        return self.data.get(key, default)


class User(ReprMixin):
    """
    Provides unified interface to selected **user** info returned by different **providers**.

    .. note:: The value format may vary across providers.
    """

    def __init__(self, provider, **kwargs):
        #: A :doc:`provider <providers>` instance.
        self.provider = provider

        #: An :class:`.Credentials` instance.
        self.credentials = kwargs.get('credentials')

        #: A :class:`dict` containing all the **user** information returned by the **provider**.
        #: The structure differs across **providers**.
        self.data = kwargs.get('data')

        #: The :attr:`.Response.content` of the request made to update the user.
        self.content = kwargs.get('content')

        #: :class:`str` ID assigned to the **user** by the **provider**.
        self.id = kwargs.get('id')
        #: :class:`str` User name e.g. *andrewpipkin*.
        self.username = kwargs.get('username')
        #: :class:`str` Name e.g. *Andrew Pipkin*.
        self.name = kwargs.get('name')
        #: :class:`str` First name e.g. *Andrew*.
        self.first_name = kwargs.get('first_name')
        #: :class:`str` Last name e.g. *Pipkin*.
        self.last_name = kwargs.get('last_name')
        #: :class:`str` Nickname e.g. *Andy*.
        self.nickname = kwargs.get('nickname')
        #: :class:`str` Link URL.
        self.link = kwargs.get('link')
        #: :class:`str` Gender.
        self.gender = kwargs.get('gender')
        #: :class:`str` Timezone.
        self.timezone = kwargs.get('timezone')
        #: :class:`str` Locale.
        self.locale = kwargs.get('locale')
        #: :class:`str` E-mail.
        self.email = kwargs.get('email')
        #: :class:`str` phone.
        self.phone = kwargs.get('phone')
        #: :class:`str` Picture URL.
        self.picture = kwargs.get('picture')
        #: Birth date as :class:`datetime.datetime()` or :class:`str` if parsing failed or ``None``.
        self.birth_date = kwargs.get('birth_date')
        #: :class:`str` Country.
        self.country = kwargs.get('country')
        #: :class:`str` City.
        self.city = kwargs.get('city')
        #: :class:`str` Geographical location.
        self.location = kwargs.get('location')
        #: :class:`str` Postal code.
        self.postal_code = kwargs.get('postal_code')
        #: Instance of the Google App Engine Users API
        #: `User <https://developers.google.com/appengine/docs/python/users/userclass>`_ class.
        #: Only present when using the :class:`authomatic.providers.gaeopenid.GAEOpenID` provider.
        self.gae_user = kwargs.get('gae_user')


    def update(self):
        """
        Updates the user info by fetching the **provider's** user info URL.

        :returns:
            Updated instance of this class.
        """

        return self.provider.update_user()


    def async_update(self):
        """
        Same as :meth:`.update` but runs asynchronously in a separate thread.

        .. warning::

            |async|

        :returns:
            :class:`.Future` instance representing the separate thread.
        """

        return Future(self.update)

    def to_dict(self):
        """
        Converts the :class:`.User` instance to a :class:`dict`.

        :returns:
            :class:`dict`
        """

        # copy the dictionary
        d = copy.copy(self.__dict__)

        # Keep only the provider name to avoid circular reference
        d['provider'] = self.provider.name
        d['credentials'] = self.credentials.serialize() if self.credentials else None
        d['birth_date'] = str(d['birth_date'])

        # Remove content
        d.pop('content')

        if isinstance(self.data, ElementTree.Element):
            d['data'] = None

        return d


SupportedUserAttributesNT = collections.namedtuple(
    typename='SupportedUserAttributesNT',
    field_names=['birth_date', 'city', 'country', 'email', 'first_name',
                 'gender', 'id', 'last_name', 'link', 'locale', 'location',
                 'name', 'nickname', 'phone', 'picture', 'postal_code',
                 'timezone', 'username',]
)


class SupportedUserAttributes(SupportedUserAttributesNT):
    def __new__(cls, **kwargs):
        defaults = dict((i, False) for i in SupportedUserAttributes._fields)
        defaults.update(**kwargs)
        return super(SupportedUserAttributes, cls).__new__(cls, **defaults)


class Credentials(ReprMixin):
    """Contains all necessary information to fetch **user's protected resources**."""

    _repr_sensitive = ('token', 'refresh_token', 'token_secret', 'consumer_key', 'consumer_secret')

    def __init__(self, config, **kwargs):
        
        #: :class:`dict` :doc:`config`.
        self.config = config

        #: :class:`str` User **access token**.
        self.token = kwargs.get('token', '')

        #: :class:`str` Access token type.
        self.token_type = kwargs.get('token_type', '')

        #: :class:`str` Refresh token.
        self.refresh_token = kwargs.get('refresh_token', '')

        #: :class:`str` Access token secret.
        self.token_secret = kwargs.get('token_secret', '')

        #: :class:`int` Expiration date as UNIX timestamp.
        self.expiration_time = int(kwargs.get('expiration_time', 0))

        #: A :doc:`Provider <providers>` instance**.
        provider = kwargs.get('provider')

        self.expire_in = int(kwargs.get('expire_in', 0))

        if provider:
            #: :class:`str` Provider name specified in the :doc:`config`.
            self.provider_name = provider.name

            #: :class:`str` Provider type e.g. ``"authomatic.providers.oauth2.OAuth2"``.
            self.provider_type = provider.get_type()

            #: :class:`str` Provider type e.g. ``"authomatic.providers.oauth2.OAuth2"``.
            self.provider_type_id = provider.type_id

            #: :class:`str` Provider short name specified in the :doc:`config`.
            self.provider_id = int(provider.id) if provider.id else None

            #: :class:`class` Provider class.
            self.provider_class = provider.__class__

            #: :class:`str` Consumer key specified in the :doc:`config`.
            self.consumer_key = provider.consumer_key

            #: :class:`str` Consumer secret specified in the :doc:`config`.
            self.consumer_secret = provider.consumer_secret

        else:
            self.provider_name = kwargs.get('provider_name', '')
            self.provider_type = kwargs.get('provider_type', '')
            self.provider_type_id = kwargs.get('provider_type_id')
            self.provider_id = kwargs.get('provider_id')
            self.provider_class = kwargs.get('provider_class')

            self.consumer_key = kwargs.get('consumer_key', '')
            self.consumer_secret = kwargs.get('consumer_secret', '')


    @property
    def expire_in(self):
        """

        """

        return self._expire_in

    @expire_in.setter
    def expire_in(self, value):
        """
        Computes :attr:`.expiration_time` when the value is set.
        """

        if value:
            self._expiration_time = int(time.time()) + int(value)
            self._expire_in = value


    @property
    def expiration_time(self):
        return self._expiration_time

    @expiration_time.setter
    def expiration_time(self, value):
        self._expiration_time = int(value)
        self._expire_in = self._expiration_time - int(time.time())


    @property
    def expiration_date(self):
        """
        Expiration date as :class:`datetime.datetime` or ``None`` if credentials never expire.
        """

        if self.expire_in < 0:
            return None
        else:
            return datetime.datetime.fromtimestamp(self.expiration_time)


    @property
    def valid(self):
        """
        ``True`` if credentials are valid, ``False`` if expired.
        """

        if self.expiration_time:
            return self.expiration_time > int(time.time())
        else:
            return True


    def expire_soon(self, seconds):
        """
        Returns ``True`` if credentials expire sooner than specified.

        :param int seconds:
            Number of seconds.

        :returns:
            ``True`` if credentials expire sooner than specified, else ``False``.
        """

        if self.expiration_time:
            return self.expiration_time < int(time.time()) + int(seconds)
        else:
            return False


    def refresh(self, force=False, soon=86400):
        """
        Refreshes the credentials only if the **provider** supports it and
        if it will expire in less than one day.
        It does nothing in other cases.

        .. note::

            The credentials will be refreshed only if it gives sense
            i.e. only |oauth2|_ has the notion of credentials *refreshment/extension*.
            And there are also differences across providers
            e.g. Google supports refreshment only if there is a ``refresh_token`` in the credentials and
            that in turn is present only if the ``access_type`` parameter was set to ``offline``
            in the **user authorization request**.

        :param bool force:
            If ``True`` the credentials will be refreshed even if they won't expire soon.

        :param int soon:
            Number of seconds specifying what means *soon*.
        """

        if hasattr(self.provider_class, 'refresh_credentials'):
            if force or self.expire_soon(soon):
                logging.info('PROVIDER NAME: {0}'.format(self.provider_name))
                return self.provider_class(self, None, self.provider_name).refresh_credentials(self)


    def async_refresh(self, *args, **kwargs):
        """
        Same as :meth:`.refresh` but runs asynchronously in a separate thread.

        .. warning::

            |async|

        :returns:
            :class:`.Future` instance representing the separate thread.
        """

        return Future(self.refresh, *args, **kwargs)


    def provider_type_class(self):
        """
        Returns the :doc:`provider <providers>` class specified in the :doc:`config`.

        :returns:
            :class:`authomatic.providers.BaseProvider` subclass.
        """

        return resolve_provider_class(self.provider_type)


    def serialize(self):
        """
        Converts the credentials to a percent encoded string to be stored for later use.

        :returns:
            :class:`string`
        """

        if self.provider_id is None:
            raise ConfigError('To serialize credentials you need to specify a '
                              'unique integer under the "id" key in the config '
                              'for each provider!')

        # Get the provider type specific items.
        rest = self.provider_type_class().to_tuple(self)

        # Provider ID and provider type ID are always the first two items.
        result = (self.provider_id, self.provider_type_id) + rest

        # Make sure that all items are strings.
        stringified = [str(i) for i in result]

        # Concatenate by newline.
        concatenated = '\n'.join(stringified)

        # Percent encode.
        return parse.quote(concatenated, '')


    @classmethod
    def deserialize(cls, config, credentials):
        """
        A *class method* which reconstructs credentials created by :meth:`serialize`.
        You can also pass it a :class:`.Credentials` instance.

        :param dict config:
            The same :doc:`config` used in the :func:`.login` to get the credentials.
        :param str credentials:
            :class:`string` The serialized credentials or :class:`.Credentials` instance.

        :returns:
            :class:`.Credentials`
        """

        # Accept both serialized and normal.
        if isinstance(credentials, Credentials):
            return credentials

        decoded = parse.unquote(credentials)

        split = decoded.split('\n')

        # We need the provider ID to move forward.
        if split[0] is None:
            raise CredentialsError('To deserialize credentials you need to specify a unique ' + \
                                   'integer under the "id" key in the config for each provider!')

        provider_id = int(split[0])

        # Get provider config by short name.
        provider_name = id_to_name(config, provider_id)
        cfg = config.get(provider_name)

        # Get the provider class.
        ProviderClass = resolve_provider_class(cfg.get('class_'))

        deserialized = Credentials(config)

        deserialized.provider_id = provider_id
        deserialized.provider_type = ProviderClass.get_type()
        deserialized.provider_type_id = split[1]
        deserialized.provider_class = ProviderClass
        deserialized.provider_name = provider_name
        deserialized.provider_class = ProviderClass

        # Add provider type specific properties.
        return ProviderClass.reconstruct(split[2:], deserialized, cfg)


class LoginResult(ReprMixin):
    """
    Result of the :func:`authomatic.login` function.
    """

    def __init__(self, provider):
        #: A :doc:`provider <providers>` instance.
        self.provider = provider

        #: An instance of the :exc:`authomatic.exceptions.BaseError` subclass.
        self.error = None


    def popup_js(self, callback_name=None, indent=None, custom=None, stay_open=False):
        """
        Returns JavaScript that:

        #.  Triggers the ``options.onLoginComplete(result, closer)`` handler set with the
            :ref:`authomatic.setup() <js_setup>` function of :ref:`javascript.js <js>`.
        #.  Calls the JavasScript callback specified by :data:`callback_name`
            on the opener of the *login handler popup* and passes it the
            *login result* JSON object as first argument and the `closer` function which
            you should call in your callback to close the popup.

        :param str callback_name:
            The name of the javascript callback e.g ``foo.bar.loginCallback``
            will result in ``window.opener.foo.bar.loginCallback(result);`` in the HTML.

        :param int indent:
            The number of spaces to indent the JSON result object.
            If ``0`` or negative, only newlines are added.
            If ``None``, no newlines are added.

        :param custom:
            Any JSON serializable object that will be passed to the ``result.custom`` attribute.

        :param str stay_open:
            If ``True``, the popup will stay open.

        :returns:
            :class:`str` with JavaScript.
        """

        custom_callback = """
        try {{ window.opener.{cb}(result, closer); }} catch(e) {{}}
        """.format(cb=callback_name) if callback_name else ''

        # TODO: Move the window.close() to the opener
        return """
        (function(){{

            closer = function(){{
                window.close();
            }};

            var result = {result};
            result.custom = {custom};

            {custom_callback}

            try {{ window.opener.authomatic.loginComplete(result, closer); }} catch(e) {{}}

        }})();

        """.format(result=self.to_json(indent),
                   custom=json.dumps(custom),
                   custom_callback=custom_callback,
                   stay_open='// ' if stay_open else '')


    def popup_html(self, callback_name=None, indent=None, title='Login | {0}', custom=None, stay_open=False):
        """
        Returns a HTML with JavaScript that:

        #.  Triggers the ``options.onLoginComplete(result, closer)`` handler set with the
            :ref:`authomatic.setup() <js_setup>` function of :ref:`javascript.js <js>`.
        #.  Calls the JavasScript callback specified by :data:`callback_name`
            on the opener of the *login handler popup* and passes it the
            *login result* JSON object as first argument and the `closer` function which
            you should call in your callback to close the popup.

        :param str callback_name:
            The name of the javascript callback e.g ``foo.bar.loginCallback``
            will result in ``window.opener.foo.bar.loginCallback(result);`` in the HTML.

        :param int indent:
            The number of spaces to indent the JSON result object.
            If ``0`` or negative, only newlines are added.
            If ``None``, no newlines are added.

        :param str title:
            The text of the HTML title. You can use ``{0}`` tag inside,
            which will be replaced by the provider name.

        :param custom:
            Any JSON serializable object that will be passed to the ``result.custom`` attribute.

        :param str stay_open:
            If ``True``, the popup will stay open.

        :returns:
            :class:`str` with HTML.
        """

        return """
        <!DOCTYPE html>
        <html>
            <head><title>{title}</title></head>
            <body>
            <script type="text/javascript">
                {js}
            </script>
            </body>
        </html>
        """.format(title=title.format(self.provider.name if self.provider else ''),
                   js=self.popup_js(callback_name, indent, custom, stay_open))

    @property
    def user(self):
        """
        A :class:`.User` instance.
        """

        return self.provider.user if self.provider else None


    def to_dict(self):
        return dict(provider=self.provider, user=self.user, error=self.error)


    def to_json(self, indent=4):
        return json.dumps(self, default=lambda obj: obj.to_dict() if hasattr(obj, 'to_dict') else '', indent=indent)


class Response(ReprMixin):
    """
    Wraps :class:`httplib.HTTPResponse` and adds
    :attr:`.content` and :attr:`.data` attributes.
    """

    def __init__(self, httplib_response, content_parser=None):
        """
        :param httplib_response:
            The wrapped :class:`httplib.HTTPResponse` instance.

        :param function content_parser:
            Callable which accepts :attr:`.content` as argument,
            parses it and returns the parsed data as :class:`dict`.
        """

        self.httplib_response = httplib_response
        self.content_parser = content_parser or json_qs_parser
        self._data = None
        self._content = None

        #: Same as :attr:`httplib.HTTPResponse.msg`.
        self.msg = httplib_response.msg
        #: Same as :attr:`httplib.HTTPResponse.version`.
        self.version = httplib_response.version
        #: Same as :attr:`httplib.HTTPResponse.status`.
        self.status = httplib_response.status
        #: Same as :attr:`httplib.HTTPResponse.reason`.
        self.reason = httplib_response.reason


    def read(self, amt=None):
        """
        Same as :meth:`httplib.HTTPResponse.read`.

        :param amt:
        """

        return self.httplib_response.read(amt)


    def getheader(self, name, default=None):
        """
        Same as :meth:`httplib.HTTPResponse.getheader`.

        :param name:
        :param default:
        """

        return self.httplib_response.getheader(name, default)


    def fileno(self):
        """
        Same as :meth:`httplib.HTTPResponse.fileno`.
        """
        return self.httplib_response.fileno()


    def getheaders(self):
        """
        Same as :meth:`httplib.HTTPResponse.getheaders`.
        """
        return self.httplib_response.getheaders()


    def is_binary_string(self, content):
        """
        Return true if string is binary data
        """

        textchars = (bytearray([7, 8, 9, 10, 12, 13, 27]) +
                     bytearray(range(0x20, 0x100)))
        return bool(content.translate(None, textchars))


    @property
    def content(self):
        """
        The whole response content.
        """

        if not self._content:
            content = self.httplib_response.read()
            if self.is_binary_string(content):
                self._content = content
            else:
                self._content = content.decode('utf-8')
        return self._content


    @property
    def data(self):
        """
        A :class:`dict` of data parsed from :attr:`.content`.
        """

        if not self._data:
            self._data = self.content_parser(self.content)
        return self._data


class UserInfoResponse(Response):
    """
    Inherits from :class:`.Response`, adds  :attr:`~UserInfoResponse.user` attribute.
    """

    def __init__(self, user, *args, **kwargs):
        super(UserInfoResponse, self).__init__(*args, **kwargs)

        #: :class:`.User` instance.
        self.user = user


class RequestElements(tuple):
    """
    A tuple of ``(url, method, params, headers, body)`` request elements.
    With some additional properties.
    """

    def __new__(cls, url, method, params, headers, body):
        return tuple.__new__(cls, (url, method, params, headers, body))

    @property
    def url(self):
        """
        Request URL.
        """

        return self[0]

    @property
    def method(self):
        """
        HTTP method of the request.
        """

        return self[1]

    @property
    def params(self):
        """
        Dictionary of request parameters.
        """

        return self[2]

    @property
    def headers(self):
        """
        Dictionary of request headers.
        """

        return self[3]

    @property
    def body(self):
        """
        :class:`str` Body of ``POST``, ``PUT`` and ``PATCH`` requests.
        """

        return self[4]

    @property
    def query_string(self):
        """
        Query string of the request.
        """

        return parse.urlencode(self.params)

    @property
    def full_url(self):
        """
        URL with query string.
        """

        return self.url + '?' + self.query_string

    def to_json(self):
        return json.dumps(dict(url=self.url,
                          method=self.method,
                          params=self.params,
                          headers=self.headers,
                          body=self.body))


class Authomatic(object):
    def __init__(self, config, secret, session_max_age=600, secure_cookie=False,
                 session=None, session_save_method=None, report_errors=True,
                 debug=False, logging_level=logging.INFO, prefix='authomatic',
                 logger=None):
        """
        Encapsulates all the functionality of this package.
        
        :param dict config:
            :doc:`config`

        :param str secret:
            A secret string that will be used as the key for signing :class:`.Session` cookie and
            as a salt by *CSRF* token generation.
    
        :param session_max_age:
            Maximum allowed age of :class:`.Session` cookie nonce in seconds.
    
        :param bool secure_cookie:
            If ``True`` the :class:`.Session` cookie will be saved wit ``Secure`` attribute.
    
        :param session:
            Custom dictionary-like session implementation.
    
        :param callable session_save_method:
            A method of the supplied session or any mechanism that saves the session data and cookie.
    
        :param bool report_errors:
            If ``True`` exceptions encountered during the **login procedure**
            will be caught and reported in the :attr:`.LoginResult.error` attribute.
            Default is ``True``.
    
        :param bool debug:
            If ``True`` traceback of exceptions will be written to response.
            Default is ``False``.
    
        :param int logging_level:
            The logging level threshold for the default logger as specified in
            the standard Python
            `logging library <http://docs.python.org/2/library/logging.html>`_.
            This setting is ignored when :data:`logger` is set.
            Default is ``logging.INFO``.
    
        :param str prefix:
            Prefix used as the :class:`.Session` cookie name.

        :param logger:
            A :class:`logging.logger` instance.
        """
        
        self.config = config
        self.secret = secret
        self.session_max_age = session_max_age
        self.secure_cookie = secure_cookie
        self.session = session
        self.session_save_method = session_save_method
        self.report_errors = report_errors
        self.debug = debug
        self.logging_level = logging_level
        self.prefix = prefix
        self._logger = logger or logging.getLogger(str(id(self)))
        
        # Set logging level.
        if logger is None:
            self._logger.setLevel(logging_level)
    
    
    def login(self, adapter, provider_name, callback=None, session=None, session_saver=None, **kwargs):
        """
        If :data:`provider_name` specified, launches the login procedure
        for corresponding :doc:`provider </reference/providers>` and
        returns :class:`.LoginResult`.
    
        If :data:`provider_name` is empty, acts like :meth:`.Authomatic.backend`.
    
        .. warning::

            The method redirects the **user** to the **provider** which in turn redirects
            **him/her** back to the *request handler* where it has been called.
    
        :param str provider_name:
            Name of the provider as specified in the keys of the :doc:`config`.
    
        :param callable callback:
            If specified the method will call the callback with :class:`.LoginResult`
            passed as argument and will return nothing.
    
        :param bool report_errors:
    
        .. note::
    
            Accepts additional keyword arguments that will be passed to :doc:`provider <providers>` constructor.
    
        :returns:
            :class:`.LoginResult`
        """
        
        if provider_name:
            # retrieve required settings for current provider and raise exceptions if missing
            provider_settings = self.config.get(provider_name)
            if not provider_settings:
                raise ConfigError('Provider name "{0}" not specified!'
                                  .format(provider_name))
    
            if not (session is None or session_saver is None):
                session = session
                session_saver = session_saver
            else:
                session = Session(adapter=adapter,
                                   secret=self.secret,
                                   max_age=self.session_max_age,
                                   name=self.prefix,
                                   secure=self.secure_cookie)
    
                session_saver = session.save
    
            # Resolve provider class.
            class_ = provider_settings.get('class_')
            if not class_:
                raise ConfigError('The "class_" key not specified in the config'
                                  ' for provider {0}!'.format(provider_name))
            ProviderClass = resolve_provider_class(class_)

            # FIXME: Find a nicer solution
            ProviderClass._logger = self._logger
    
            # instantiate provider class
            provider = ProviderClass(self,
                                     adapter=adapter,
                                     provider_name=provider_name,
                                     callback=callback,
                                     session=session,
                                     session_saver=session_saver,
                                     **kwargs)
    
            # return login result
            return provider.login()
    
        else:
            # Act like backend.
            self.backend(adapter)

 
    def credentials(self, credentials):
        """
        Deserializes credentials.
    
        :param credentials:
            Credentials serialized with :meth:`.Credentials.serialize` or :class:`.Credentials` instance.
    
        :returns:
            :class:`.Credentials`
        """
    
        return Credentials.deserialize(self.config, credentials)
    
    
    def access(self, credentials, url, params=None, method='GET', headers=None, body='', max_redirects=5, content_parser=None):
        """
        Accesses **protected resource** on behalf of the **user**.
    
        :param credentials:
            The **user's** :class:`.Credentials` (serialized or normal).
    
        :param str url:
            The **protected resource** URL.
    
        :param str method:
            HTTP method of the request.
    
        :param dict headers:
            HTTP headers of the request.
    
        :param str body:
            Body of ``POST``, ``PUT`` and ``PATCH`` requests.
    
        :param int max_redirects:
            Maximum number of HTTP redirects to follow.
    
        :param function content_parser:
            A function to be used to parse the :attr:`.Response.data` from :attr:`.Response.content`.
    
        :returns:
            :class:`.Response`
        """
    
        # Deserialize credentials.
        credentials = Credentials.deserialize(self.config, credentials)
    
        # Resolve provider class.
        ProviderClass = credentials.provider_class
        logging.info('ACCESS HEADERS: {0}'.format(headers))
        # Access resource and return response.
        
        provider = ProviderClass(self, adapter=None, provider_name=credentials.provider_name)
        provider.credentials = credentials
        
        return provider.access(url=url,
                               params=params,
                               method=method,
                               headers=headers,
                               body=body,
                               max_redirects=max_redirects,
                               content_parser=content_parser)
    
    
    def async_access(self, *args, **kwargs):
        """
        Same as :meth:`.Authomatic.access` but runs asynchronously in a separate thread.
    
        .. warning::

            |async|
    
        :returns:
            :class:`.Future` instance representing the separate thread.
        """
    
        return Future(self.access, *args, **kwargs)
    
    
    def request_elements(self, credentials=None, url=None, method='GET', params=None,
                         headers=None, body='', json_input=None, return_json=False):
        """
        Creates request elements for accessing **protected resource of a user**.
        Required arguments are :data:`credentials` and :data:`url`.
        You can pass :data:`credentials`, :data:`url`, :data:`method`, and :data:`params`
        as a JSON object.
    
        :param credentials:
            The **user's** credentials (can be serialized).
    
        :param str url:
            The url of the protected resource.
    
        :param str method:
            The HTTP method of the request.
    
        :param dict params:
            Dictionary of request parameters.
    
        :param dict headers:
            Dictionary of request headers.
    
        :param str body:
            Body of ``POST``, ``PUT`` and ``PATCH`` requests.
    
        :param str json_input:
            you can pass :data:`credentials`, :data:`url`, :data:`method`, :data:`params` and :data:`headers`
            in a JSON object. Values from arguments will be used for missing properties.
    
            ::
    
                {
                    "credentials": "###",
                    "url": "https://example.com/api",
                    "method": "POST",
                    "params": {
                        "foo": "bar"
                    },
                    "headers": {
                        "baz": "bing",
                        "Authorization": "Bearer ###"
                    },
                    "body": "Foo bar baz bing."
                }
    
        :param bool return_json:
            if ``True`` the function returns a json object.
    
            ::
    
                {
                    "url": "https://example.com/api",
                    "method": "POST",
                    "params": {
                        "access_token": "###",
                        "foo": "bar"
                    },
                    "headers": {
                        "baz": "bing",
                        "Authorization": "Bearer ###"
                    },
                    "body": "Foo bar baz bing."
                }
    
        :returns:
            :class:`.RequestElements` or JSON string.
        """
    
        # Parse values from JSON
        if json_input:
            parsed_input = json.loads(json_input)
    
            credentials = parsed_input.get('credentials', credentials)
            url = parsed_input.get('url', url)
            method = parsed_input.get('method', method)
            params = parsed_input.get('params', params)
            headers = parsed_input.get('headers', headers)
            body = parsed_input.get('body', body)
    
        if not credentials and url:
            raise RequestElementsError('To create request elements, you must provide credentials ' +\
                                        'and URL either as keyword arguments or in the JSON object!')
    
        # Get the provider class
        credentials = Credentials.deserialize(self.config, credentials)
        ProviderClass = credentials.provider_class
    
        # Create request elements
        request_elements = ProviderClass.create_request_elements(ProviderClass.PROTECTED_RESOURCE_REQUEST_TYPE,
                                                                 credentials=credentials,
                                                                 url=url,
                                                                 method=method,
                                                                 params=params,
                                                                 headers=headers,
                                                                 body=body)
    
        if return_json:
            return request_elements.to_json()
    
        else:
            return request_elements
    
    
    def backend(self, adapter):
        """
        Converts a *request handler* to a JSON backend which you can use with :ref:`authomatic.js <js>`.
    
        Just call it inside a *request handler* like this:
    
        ::
    
            class JSONHandler(webapp2.RequestHandler):
                def get(self):
                    authomatic.backend(Webapp2Adapter(self))
    
        :param adapter:
            The only argument is an :doc:`adapter <adapters>`.
    
        The *request handler* will now accept these request parameters:
    
        :param str type:
            Type of the request. Either ``auto``, ``fetch`` or ``elements``. Default is ``auto``.
    
        :param str credentials:
            Serialized :class:`.Credentials`.
    
        :param str url:
            URL of the **protected resource** request.
    
        :param str method:
            HTTP method of the **protected resource** request.
    
        :param str body:
            HTTP body of the **protected resource** request.
    
        :param JSON params:
            HTTP params of the **protected resource** request as a JSON object.
    
        :param JSON headers:
            HTTP headers of the **protected resource** request as a JSON object.
    
        :param JSON json:
            You can pass all of the aforementioned params except ``type`` in a JSON object.
    
            .. code-block:: javascript
    
                {
                    "credentials": "######",
                    "url": "https://example.com",
                    "method": "POST",
                    "params": {"foo": "bar"},
                    "headers": {"baz": "bing"},
                    "body": "the body of the request"
                }
    
        Depending on the ``type`` param, the handler will either write
        a JSON object with *request elements* to the response,
        and add an ``Authomatic-Response-To: elements`` response header, ...
    
        .. code-block:: javascript
    
            {
                "url": "https://example.com/api",
                "method": "POST",
                "params": {
                    "access_token": "###",
                    "foo": "bar"
                },
                "headers": {
                    "baz": "bing",
                    "Authorization": "Bearer ###"
                }
            }
    
        ... or make a fetch to the **protected resource** and forward it's response
        content, status and headers with an additional ``Authomatic-Response-To: fetch`` header
        to the response.
    
        .. warning::

            The backend will not work if you write anything to the response in the handler!

        """
    
        AUTHOMATIC_HEADER = 'Authomatic-Response-To'
    
        # Collect request params
        request_type = adapter.params.get('type', 'auto')
        json_input = adapter.params.get('json')
        credentials = adapter.params.get('credentials')
        url = adapter.params.get('url')
        method = adapter.params.get('method', 'GET')
        body = adapter.params.get('body', '')
    
    
        params = adapter.params.get('params')
        params = json.loads(params) if params else {}
    
        headers = adapter.params.get('headers')
        headers = json.loads(headers) if headers else {}
    
        ProviderClass = Credentials.deserialize(self.config, credentials).provider_class
    
        if request_type == 'auto':
            # If there is a "callback" param, it's a JSONP request.
            jsonp = params.get('callback')
    
            # JSONP is possible only with GET method.
            if ProviderClass.supports_jsonp and method is 'GET':
                request_type = 'elements'
            else:
                # Remove the JSONP callback
                if params.get('callback'):
                    params.pop('callback')
                request_type = 'fetch'
    
        if request_type == 'fetch':
            # Access protected resource
            response = self.access(credentials, url, params, method, headers, body)
            result = response.content
    
            # Forward status
            adapter.status = str(response.status) + ' ' + str(response.reason)
    
            # Forward headers
            for k, v in response.getheaders():
                logging.info('    {0}: {1}'.format(k, v))
                adapter.set_header(k, v)
    
        elif request_type == 'elements':
            # Create request elements
            if json_input:
                result = self.request_elements(json_input=json_input, return_json=True)
            else:
                result = self.request_elements(credentials=credentials,
                                          url=url,
                                          method=method,
                                          params=params,
                                          headers=headers,
                                          body=body,
                                          return_json=True)
    
            adapter.set_header('Content-Type', 'application/json')
        else:
            result = '{"error": "Bad Request!"}'
    
    
        # Add the authomatic header
        adapter.set_header(AUTHOMATIC_HEADER, request_type)
    
        # Write result to response
        adapter.write(result)
    

#===============================================================================
# Deprecated
#===============================================================================

def setup(*args, **kwargs):
    """
     
    .. warning::

        This function is **deprecated** and will be removed in version 0.1.0!
        Use instance of :class:`.Authomatic` class instead.

    """
     
    logging.warn('The authomatic.setup function is deprecated and will be removed in version 0.1.0! ' + \
                 'Use an instance of the Authomatic class instead.')
     
    global global_authomatic_instance
    global_authomatic_instance = Authomatic(*args, **kwargs)
 
 
def login(*args, **kwargs):
    """
     
    .. warning::

        This function is **deprecated** and will be removed in version 0.1.0!
        Use the :meth:`.Authomatic.login` method instead.

    """
     
    logging.warn('The authomatic.login function is deprecated and will be removed in version 0.1.0! ' + \
                 'Use the "login" method of the "Authomatic" class instead.')
     
    return global_authomatic_instance.login(*args, **kwargs)
 
 
def credentials(*args, **kwargs):
    """
     
    .. warning::

        This function is **deprecated** and will be removed in version 0.1.0!
        Use the :meth:`.Authomatic.credentials` method instead.

    """
     
    logging.warn('The authomatic.credentials function is deprecated and will be removed in version 0.1.0! ' + \
                 'Use the "credentials" method of the "Authomatic" class instead.')
     
    return global_authomatic_instance.credentials(*args, **kwargs)
 
 
def access(*args, **kwargs):
    """
     
    .. warning::

        This function is **deprecated** and will be removed in version 0.1.0!
        Use the :meth:`.Authomatic.access` method instead.

    """
     
    logging.warn('The authomatic.access function is deprecated and will be removed in version 0.1.0! ' + \
                 'Use the "access" method of the "Authomatic" class instead.')
     
    return global_authomatic_instance.access(*args, **kwargs)
 
 
def async_access(*args, **kwargs):
    """
     
    .. warning::

        This function is **deprecated** and will be removed in version 0.1.0!
        Use the :meth:`.Authomatic.async_access` method instead.

    """
     
    logging.warn('The authomatic.async_access function is deprecated and will be removed in version 0.1.0! ' + \
                 'Use the "async_access" method of the "Authomatic" class instead.')
     
    return global_authomatic_instance.async_access(*args, **kwargs)
 
 
def request_elements(*args, **kwargs):
    """
     
    .. warning::

        This function is **deprecated** and will be removed in version 0.1.0!
        Use the :meth:`.Authomatic.request_elements` method instead.

    """
     
    logging.warn('The authomatic.request_elements function is deprecated and will be removed in version 0.1.0! ' + \
                 'Use the "request_elements" method of the "Authomatic" class instead.')
     
    return global_authomatic_instance.request_elements(*args, **kwargs)
 
 
def backend(*args, **kwargs):
    """
     
    .. warning::

        This function is **deprecated** and will be removed in version 0.1.0!
        Use the :meth:`.Authomatic.backend` method instead.

    """
     
    logging.warn('The authomatic.backend function is deprecated and will be removed in version 0.1.0! ' + \
                 'Use the "backend" method of the "Authomatic" class instead.')
     
    return global_authomatic_instance.backend(*args, **kwargs)






A BattlenetAuthomatic/authomatic/exceptions.py => BattlenetAuthomatic/authomatic/exceptions.py +85 -0
@@ 0,0 1,85 @@
# -*- coding: utf-8 -*-
"""
Provides various exception types for the library.
"""

class BaseError(Exception):
    """
    Base error for all errors.
    """
    
    def __init__(self, message, original_message='', url='', status=None):
        super(BaseError, self).__init__(message)
        
        #: Error message.
        self.message = message
        
        #: Original message.
        self.original_message = original_message
        
        #: URL related with the error.
        self.url = url
        
        #: HTTP status code related with the error.
        self.status = status
    
    def to_dict(self):
        return self.__dict__


class ConfigError(BaseError):
    pass


class SessionError(BaseError):
    pass


class CredentialsError(BaseError):
    pass


class HTTPError(BaseError):
    pass


class CSRFError(BaseError):
    pass


class ImportStringError(BaseError):
    pass


class AuthenticationError(BaseError):
    pass


class OAuth1Error(BaseError):
    pass


class OAuth2Error(BaseError):
    pass


class OpenIDError(BaseError):
    pass


class CancellationError(BaseError):
    pass


class FailureError(BaseError):
    pass


class FetchError(BaseError):
    pass


class RequestElementsError(BaseError):
    pass



A BattlenetAuthomatic/authomatic/providers/__init__.py => BattlenetAuthomatic/authomatic/providers/__init__.py +940 -0
@@ 0,0 1,940 @@
# -*- coding: utf-8 -*-
"""
Abstract Classes for Providers
------------------------------

Abstract base classes for implementation of protocol specific providers.

.. note::
    
    Attributes prefixed with ``_x_`` serve the purpose of unification
    of differences across providers.

.. autosummary::
    
    login_decorator
    BaseProvider
    AuthorizationProvider
    AuthenticationProvider

"""

import abc
import authomatic.core
import base64
import hashlib
import logging
import random
import sys
import traceback
import uuid

from authomatic.core import Session
from authomatic.exceptions import (
    ConfigError,
    FetchError,
    CredentialsError,
)
from authomatic import six
from authomatic.six.moves import urllib_parse as parse
from authomatic.six.moves import http_client


__all__ = ['BaseProvider', 'AuthorizationProvider', 'AuthenticationProvider', 'login_decorator']


def _error_traceback_html(exc_info, traceback):
    """
    Generates error traceback HTML.
    
    :param tuple exc_info:
        Output of :func:`sys.exc_info` function.
        
    :param traceback:
        Output of :func:`traceback.format_exc` function.
    """
    
    html = """
    <html>
        <head>
            <title>ERROR: {error}</title>
        </head>
        <body style="font-family: sans-serif">
            <h4>The Authomatic library encountered an error!</h4>
            <h1>{error}</h1>
            <pre>{traceback}</pre>
        </body>
    </html>
    """
    
    return html.format(error=exc_info[1], traceback=traceback)


def login_decorator(func):
    """
    Decorate the :meth:`.BaseProvider.login` implementations with this decorator.
    
    Provides mechanism for error reporting and returning result
    which makes the :meth:`.BaseProvider.login` implementation cleaner.
    """
    
    def wrap(provider, *args, **kwargs):
        error = None
        result = authomatic.core.LoginResult(provider)
        
        try:
            func(provider, *args, **kwargs)
        except Exception as e:
            if provider.settings.report_errors:
                error = e
                provider._log(logging.ERROR, u'Reported suppressed exception: {0}!'.format(repr(error)))
            else:
                if provider.settings.debug:
                    # TODO: Check whether it actually works without middleware
                    provider.write(_error_traceback_html(sys.exc_info(), traceback.format_exc()))
                raise
        
        # If there is user or error the login procedure has finished
        if provider.user or error:
            result = authomatic.core.LoginResult(provider)
            # Add error to result
            result.error = error
            
            # delete session cookie
            if isinstance(provider.session, authomatic.core.Session):
                provider.session.delete()
            
            provider._log(logging.INFO, u'Procedure finished.')
            
            if provider.callback:
                provider.callback(result)
            return result
        else:
            # Save session
            provider.save_session()
        
    return wrap


class BaseProvider(object):
    """
    Abstract base class for all providers.
    """
    
    PROVIDER_TYPE_ID = 0
    
    _repr_ignore = ('user',)

    __metaclass__ = abc.ABCMeta

    supported_user_attributes = authomatic.core.SupportedUserAttributes()
    
    def __init__(self, settings, adapter, provider_name, session=None, session_saver=None, callback=None, js_callback=None,
                 prefix='authomatic', **kwargs):
        
        self.settings = settings
        self.adapter = adapter
        
        self.session = session
        self.save_session = session_saver
        
        
        #: :class:`str` The provider name as specified in the :doc:`config`.
        self.name = provider_name
        
        #: :class:`callable` An optional callback called when the login procedure
        #: is finished with :class:`.core.LoginResult` passed as argument.
        self.callback = callback
        
        #: :class:`str` Name of an optional javascript callback.
        self.js_callback = js_callback
                
        #: :class:`.core.User`.
        self.user = None
        
        #: :class:`bool` If ``True``, the :attr:`.BaseProvider.user_authorization_url` will be displayed
        #: in a *popup mode*, if the **provider** supports it.
        self.popup = self._kwarg(kwargs, 'popup')
    
    
    @property
    def url(self):
        return self.adapter.url
    
    
    @property
    def params(self):
        return self.adapter.params
    
    
    def write(self, value):
        self.adapter.write(value)
    
    
    def set_header(self, key, value):
        self.adapter.set_header(key, value)
    
    
    def set_status(self, status):
        self.adapter.set_status(status)
    
    
    def redirect(self, url):
        self.set_status('302 Found')
        self.set_header('Location', url)
    
    
    #===========================================================================
    # Abstract methods
    #===========================================================================
    
    @abc.abstractmethod
    def login(self):
        """
        Launches the *login procedure* to get **user's credentials** from **provider**.
        
        Should be decorated with :func:`.login_decorator`.
        The *login procedure* is considered finished when the :attr:`.user` attribute is
        not empty when the method runs out of it's flow or when there are errors.
        """
    
    #===========================================================================
    # Exposed methods
    #===========================================================================
    
    def to_dict(self):
        """
        Converts the provider instance to a :class:`dict`.
        
        :returns:
            :class:`dict`
        """
        
        return dict(name=self.name,
                    id=self.id if hasattr(self, 'id') else None,
                    type_id=self.type_id,
                    type=self.get_type(),
                    scope=self.scope if hasattr(self, 'scope') else None,
                    user=self.user.id if self.user else None)
    
    @classmethod
    def get_type(cls):
        """
        Returns the provider type.
        
        :returns:
            :class:`str` The full dotted path to base class e.g. :literal:`"authomatic.providers.oauth2.OAuth2"`. 
        """
        
        return cls.__module__ + '.' + cls.__bases__[0].__name__
    
    
    def update_user(self):
        """
        Updates and returns :attr:`.user`.
        
        :returns:
            :class:`.User`
        """
    
    
    #===========================================================================
    # Internal methods
    #===========================================================================
    
    @property
    def type_id(self):
        pass
    
    
    def _kwarg(self, kwargs, kwname, default=None):
        """
        Resolves keyword arguments from constructor or :doc:`config`.
        
        .. note::
        
            The keyword arguments take this order of precedence:
            
            1. Arguments passed to constructor through the :func:`authomatic.login`.
            2. Provider specific arguments from :doc:`config`.
            3. Arguments from :doc:`config` set in the ``__defaults__`` key.
            2. The value from :data:`default` argument.
        
        :param dict kwargs:
            Keyword arguments dictionary.
        :param str kwname:
            Name of the desired keyword argument.
        """
        
        return kwargs.get(kwname) or \
               self.settings.config.get(self.name, {}).get(kwname) or \
               self.settings.config.get('__defaults__', {}).get(kwname) or \
               default
    
    
    def _session_key(self, key):
        """
        Generates session key string.
        
        :param str key:
            e.g. ``"authomatic:facebook:key"``
        """
        
        return '{0}:{1}:{2}'.format(self.settings.prefix, self.name, key)
    
    
    def _session_set(self, key, value):
        """Saves a value to session."""
        
        self.session[self._session_key(key)] = value
    
    
    def _session_get(self, key):
        """Retrieves a value from session."""
        
        return self.session.get(self._session_key(key))
    
    
    @staticmethod
    def csrf_generator(secret):
        """
        Generates CSRF token.
        
        Inspired by this article: http://blog.ptsecurity.com/2012/10/random-number-security-in-python.html
                
        :returns:
            :class:`str` Random unguessable string.
        """
        
        # Create hash from random string plus salt.
        hashed = hashlib.md5(uuid.uuid4().bytes + six.b(secret)).hexdigest()
        
        # Each time return random portion of the hash.
        span = 5
        shift = random.randint(0, span)     
        return hashed[shift:shift - span - 1]
    
    
    @classmethod
    def _log(cls, level, msg):
        """
        Logs a message with pre-formatted prefix.
        
        :param int level:
            Logging level as specified in the
            `login module <http://docs.python.org/2/library/logging.html>`_ of
            Python standard library.
        
        :param str msg:
            The actual message.
        """

        logger = getattr(cls, '_logger', None) or authomatic.core._logger
        logger.log(level, ': '.join(('authomatic', cls.__name__, msg)))

    
    def _fetch(self, url, method='GET', params=None, headers=None, body='', max_redirects=5, content_parser=None):
        """
        Fetches a URL.
        
        :param str url:
            The URL to fetch.
            
        :param str method:
            HTTP method of the request.
            
        :param dict params:
            Dictionary of request parameters.
            
        :param dict headers:
            HTTP headers of the request.
            
        :param str body:
            Body of ``POST``, ``PUT`` and ``PATCH`` requests.
            
        :param int max_redirects:
            Number of maximum HTTP redirects to follow.
            
        :param function content_parser:
            A callable to be used to parse the :attr:`.Response.data` from :attr:`.Response.content`.
        """
        params = params or {}
        params.update(self.access_params)
        
        headers = headers or {}
        headers.update(self.access_headers)
        
        scheme, host, path, query, fragment = parse.urlsplit(url)
        query = parse.urlencode(params)
        
        if method in ('POST', 'PUT', 'PATCH'):
            if not body:
                # Put querystring to body
                body = query
                query = ''
                headers.update({'Content-Type': 'application/x-www-form-urlencoded'})
        request_path = parse.urlunsplit(('', '', path or '', query or '', ''))
        
        self._log(logging.DEBUG, u' \u251C\u2500 host: {0}'.format(host))
        self._log(logging.DEBUG, u' \u251C\u2500 path: {0}'.format(request_path))
        self._log(logging.DEBUG, u' \u251C\u2500 method: {0}'.format(method))
        self._log(logging.DEBUG, u' \u251C\u2500 body: {0}'.format(body))
        self._log(logging.DEBUG, u' \u251C\u2500 params: {0}'.format(params))
        self._log(logging.DEBUG, u' \u2514\u2500 headers: {0}'.format(headers))
        
        # Connect
        if scheme.lower() == 'https':
            connection = http_client.HTTPSConnection(host)
        else:
            connection = http_client.HTTPConnection(host)
            
        try:
            connection.request(method, request_path, body, headers)
        except Exception as e:
            raise FetchError('Could not connect!',
                             original_message=e.message,
                             url=request_path)
        
        response = connection.getresponse()
        location = response.getheader('Location')
        
        if response.status in (300, 301, 302, 303, 307) and location:
            if location == url:
                raise FetchError('Url redirects to itself!',
                                 url=location,
                                 status=response.status)
                
            elif max_redirects > 0:
                remaining_redirects = max_redirects - 1
                
                self._log(logging.DEBUG, u'Redirecting to {0}'.format(url))
                self._log(logging.DEBUG, u'Remaining redirects: {0}'
                          .format(remaining_redirects))
                
                # Call this method again.
                response = self._fetch(url=location,
                                      params=params,
                                      method=method,
                                      headers=headers,
                                      max_redirects=remaining_redirects)
                
            else:
                raise FetchError('Max redirects reached!',
                                 url=location,
                                 status=response.status)
        else:
            self._log(logging.DEBUG, u'Got response:')
            self._log(logging.DEBUG, u' \u251C\u2500 url: {0}'.format(url))
            self._log(logging.DEBUG, u' \u251C\u2500 status: {0}'.format(response.status))
            self._log(logging.DEBUG, u' \u2514\u2500 headers: {0}'.format(response.getheaders()))
                
        return authomatic.core.Response(response, content_parser)
    
    
    def _update_or_create_user(self, data, credentials=None, content=None):
        """
        Updates or creates :attr:`.user`.
        
        :returns:
            :class:`.User`
        """
        
        if not self.user:
            self.user = authomatic.core.User(self, credentials=credentials)
        
        self.user.content = content
        self.user.data = data
        
        # Update.
        for key in list(self.user.__dict__.keys()):
            # Exclude data.
            if key not in ('data', 'content'):
                # Extract every data item whose key matches the user property name,
                # but only if it has a value.
                value = data.get(key)
                if value:
                    setattr(self.user, key, value)
        
        # Handle different structure of data by different providers.
        self.user = self._x_user_parser(self.user, data)
        
        if self.user.id:
            self.user.id = str(self.user.id)
        
        # TODO: Move to User
        # If there is no user.name,
        if not self.user.name:
            if self.user.first_name and self.user.last_name:
                # Create it from first name and last name if available.
                self.user.name = ' '.join((self.user.first_name,
                                           self.user.last_name))
            else:
                # Or use one of these.
                self.user.name = (self.user.username or self.user.nickname or
                                  self.user.first_name or self.user.last_name)

        if not self.user.location:
            if self.user.city and self.user.country:
                self.user.location = '{0}, {1}'.format(self.user.city,
                                                       self.user.country)
            else:
                self.user.location = self.user.city or self.user.country

        return self.user    
    
    
    @staticmethod
    def _x_user_parser(user, data):
        """
        Handles different structure of user info data by different providers.
        
        :param user:
            :class:`.User`
        :param dict data:
            User info data returned by provider.
        """
        
        return user


    @staticmethod
    def _http_status_in_category(status, category):
        """Checks whether a HTTP status code is in the category denoted
        by the hundreds digit"""

        assert category < 10, 'HTTP status category must be a one-digit int!'
        cat = category * 100
        return status >= cat and status < cat + 100


class AuthorizationProvider(BaseProvider):
    """
    Base provider for *authorization protocols* i.e. protocols which allow a **provider**
    to authorize a **consumer** to access **protected resources** of a **user**.
    e.g. `OAuth 2.0 <http://oauth.net/2/>`_ or `OAuth 1.0a <http://oauth.net/core/1.0a/>`_.    
    """
        
    USER_AUTHORIZATION_REQUEST_TYPE = 2
    ACCESS_TOKEN_REQUEST_TYPE = 3
    PROTECTED_RESOURCE_REQUEST_TYPE = 4
    REFRESH_TOKEN_REQUEST_TYPE = 5
    
    BEARER = 'Bearer'
    
    _x_term_dict = {}
    
    #: If ``True`` the provider doesn't support Cross-site HTTP requests.
    same_origin = True
    
    #: :class:`bool` Whether the provider supports JSONP requests.
    supports_jsonp = False
    
    # Whether to use the HTTP Authorization header.
    _x_use_authorization_header = True
    
    def __init__(self, *args, **kwargs):
        """
        Accepts additional keyword arguments:
        
        :arg str consumer_key:
            The *key* assigned to our application (**consumer**) by the **provider**.
            
        :arg str consumer_secret:
            The *secret* assigned to our application (**consumer**) by the **provider**.
            
        :arg int id:
            A unique numeric ID used to serialize :class:`.Credentials`.
            
        :arg dict user_authorization_params:
            A dictionary of additional request parameters for **user authorization request**.
            
        :arg dict access_token_params:
            A dictionary of additional request parameters for **access_with_credentials token request**.
            
        :arg dict access_headers:
            A dictionary of default HTTP headers that will be used when
            accessing **user's** protected resources.
            Applied by :meth:`.access()`, :meth:`.update_user()` and :meth:`.User.update()`
            
        :arg dict access_params:
            A dictionary of default query string parameters that will be used when
            accessing **user's** protected resources.
            Applied by :meth:`.access()`, :meth:`.update_user()` and :meth:`.User.update()`
        """

        super(AuthorizationProvider, self).__init__(*args, **kwargs)
        
        self.consumer_key = self._kwarg(kwargs, 'consumer_key')
        self.consumer_secret = self._kwarg(kwargs, 'consumer_secret')
        
        self.user_authorization_params = self._kwarg(kwargs, 'user_authorization_params', {})

        self.access_token_headers = self._kwarg(kwargs, 'user_authorization_headers', {})
        self.access_token_params = self._kwarg(kwargs, 'access_token_params', {})

        self.id = self._kwarg(kwargs, 'id')

        self.access_headers = self._kwarg(kwargs, 'access_headers', {})
        self.access_params = self._kwarg(kwargs, 'access_params', {})
        
        #: :class:`.Credentials` to access **user's protected resources**.
        self.credentials = authomatic.core.Credentials(self.settings.config, provider=self)

        #: Response of the *access token request*.
        self.access_token_response = None
    
    #===========================================================================
    # Abstract properties
    #===========================================================================
        
    @abc.abstractproperty
    def user_authorization_url(self):
        """
        :class:`str` URL to which we redirect the **user** to grant our app i.e. the **consumer**
        an **authorization** to access his **protected resources**.
        see http://tools.ietf.org/html/rfc6749#section-4.1.1 and
        http://oauth.net/core/1.0a/#auth_step2.
        """
    
    
    @abc.abstractproperty
    def access_token_url(self):
        """
        :class:`str` URL where we can get the *access token* to access **protected resources** of a **user**.
        see http://tools.ietf.org/html/rfc6749#section-4.1.3 and
        http://oauth.net/core/1.0a/#auth_step3.
        """
    
    
    @abc.abstractproperty
    def user_info_url(self):
        """        
        :class:`str` URL where we can get the **user** info.
        see http://tools.ietf.org/html/rfc6749#section-7 and
        http://oauth.net/core/1.0a/#anchor12.
        """
    
    
    #===========================================================================
    # Abstract methods
    #===========================================================================
    
    @abc.abstractmethod
    def to_tuple(self, credentials):
        """
        Must convert :data:`credentials` to a :class:`tuple` to be used by :meth:`.Credentials.serialize`.
        
        .. warning::

            |classmethod|

        :param credentials:
            :class:`.Credentials`
            
        :returns:
            :class:`tuple`
        """
    
    
    @abc.abstractmethod
    def reconstruct(self, deserialized_tuple, credentials, cfg):
        """
        Must convert the :data:`deserialized_tuple` back to :class:`.Credentials`.
        
        .. warning::

            |classmethod|

        :param tuple deserialized_tuple:
            A tuple whose first index is the :attr:`.id` and the rest
            are all the items of the :class:`tuple` created by :meth:`.to_tuple`.
        
        :param credentials:
            A :class:`.Credentials` instance.
            
        :param dict cfg:
            Provider configuration from :doc:`config`. 
        """
    
    
    @abc.abstractmethod
    def create_request_elements(self, request_type, credentials, url, method='GET', params=None, headers=None, body=''):
        """
        Must return :class:`.RequestElements`.
        
        .. warning::

            |classmethod|

        :param int request_type:
            Type of the request specified by one of the class's constants.
            
        :param credentials:
            :class:`.Credentials` of the **user** whose
            **protected resource** we want to access.
            
        :param str url:
            URL of the request.
            
        :param str method:
            HTTP method of the request.
            
        :param dict params:
            Dictionary of request parameters.
            
        :param dict headers:
            Dictionary of request headers.
            
        :param str body:
            Body of ``POST``, ``PUT`` and ``PATCH`` requests.
        
        :returns:
            :class:`.RequestElements`
        """
    
    
    #===========================================================================
    # Exposed methods
    #===========================================================================
    
    @property
    def type_id(self):
        """
        A short string representing the provider implementation id used for
        serialization of :class:`.Credentials` and to identify the type of provider in JavaScript.
        The part before hyphen denotes the type of the provider, the part after hyphen denotes the class id
        e.g. ``oauth2.Facebook.type_id = '2-5'``, ``oauth1.Twitter.type_id = '1-5'``.
        """
        
        cls = self.__class__
        mod = sys.modules.get(cls.__module__)
        
        return str(self.PROVIDER_TYPE_ID) + '-' + str(mod.PROVIDER_ID_MAP.index(cls))
    
    
    def access(self, url, params=None, method='GET', headers=None,
               body='', max_redirects=5, content_parser=None):
        """
        Fetches the **protected resource** of an authenticated **user**.
        
        :param credentials:
            The **user's** :class:`.Credentials` (serialized or normal).
            
        :param str url:
            The URL of the **protected resource**.
            
        :param str method:
            HTTP method of the request.
            
        :param dict headers:
            HTTP headers of the request.
            
        :param str body:
            Body of ``POST``, ``PUT`` and ``PATCH`` requests.
            
        :param int max_redirects:
            Maximum number of HTTP redirects to follow.
            
        :param function content_parser:
            A function to be used to parse the :attr:`.Response.data` from :attr:`.Response.content`.
        
        :returns:
            :class:`.Response`
        """
        
        if not self.user and not self.credentials:
            raise CredentialsError(u'There is no authenticated user!')
        
        headers = headers or {}
        
        self._log(logging.INFO, u'Accessing protected resource {0}.'.format(url))
        
        request_elements = self.create_request_elements(request_type=self.PROTECTED_RESOURCE_REQUEST_TYPE,
                                                        credentials=self.credentials,
                                                        url=url,
                                                        body=body,
                                                        params=params,
                                                        headers=headers,
                                                        method=method)
        
        response = self._fetch(*request_elements,
                              max_redirects=max_redirects,
                              content_parser=content_parser)
        
        self._log(logging.INFO, u'Got response. HTTP status = {0}.'.format(response.status))
        return response


    def async_access(self, *args, **kwargs):
        """
        Same as :meth:`.access` but runs asynchronously in a separate thread.
        
        .. warning::

            |async|

        :returns:
            :class:`.Future` instance representing the separate thread.
        """
        
        return authomatic.core.Future(self.access, *args, **kwargs)
    
    
    def update_user(self):
        """
        Updates the :attr:`.BaseProvider.user`.
        
        .. warning::
            Fetches the :attr:`.user_info_url`!

        :returns:
            :class:`.UserInfoResponse`
        """
        if self.user_info_url:
            response = self._access_user_info()
            self.user = self._update_or_create_user(response.data,
                                                    content=response.content)
            return authomatic.core.UserInfoResponse(self.user,
                                                    response.httplib_response)
    
    
    #===========================================================================
    # Internal methods
    #===========================================================================
    
    @classmethod
    def _authorization_header(cls, credentials):
        """
        Creates authorization headers if the provider supports it.
        See: http://en.wikipedia.org/wiki/Basic_access_authentication.
        
        :param credentials:
            :class:`.Credentials`
        
        :returns:
            Headers as :class:`dict`.
        """
        
        if cls._x_use_authorization_header:
            res = ':'.join((credentials.consumer_key, credentials.consumer_secret))
            res = base64.b64encode(six.b(res)).decode()
            return {'Authorization': 'Basic {0}'.format(res)}
        else:
            return {}
    
    
    def _check_consumer(self):
        """
        Validates the :attr:`.consumer`.
        """
        
        if not self.consumer.key:
            raise ConfigError('Consumer key not specified for provider {0}!'.format(self.name))
        
        if not self.consumer.secret:
            raise ConfigError('Consumer secret not specified for provider {0}!'.format(self.name))
    
    
    @staticmethod
    def _split_url(url):
        "Splits given url to url base and params converted to list of tuples"
        
        split = parse.urlsplit(url)
        base = parse.urlunsplit((split.scheme, split.netloc, split.path, 0, 0))
        params = parse.parse_qsl(split.query, True)
        
        return base, params
    
    
    @classmethod
    def _x_request_elements_filter(cls, request_type, request_elements, credentials):
        """
        Override this to handle special request requirements of zealous providers.
        
        .. warning::

            |classmethod|
        
        :param int request_type:
            Type of request.
            
        :param request_elements:
            :class:`.RequestElements`
            
        :param credentials:
            :class:`.Credentials`
            
        :returns:
            :class:`.RequestElements`
        """
        
        return request_elements
    
    
    @staticmethod
    def _x_credentials_parser(credentials, data):
        """
        Override this to handle differences in naming conventions across providers.
        
        :param credentials:
            :class:`.Credentials`
            
        :param dict data:
            Response data dictionary.
            
        :returns:
            :class:`.Credentials`
        """
        return credentials
    
    
    def _access_user_info(self):
        """
        Accesses the :attr:`.user_info_url`.
        
        :returns:
            :class:`.UserInfoResponse`
        """
        url = self.user_info_url.format(**self.user.__dict__)
        return self.access(url)
    

class AuthenticationProvider(BaseProvider):
    """
    Base provider for *authentication protocols* i.e. protocols which allow a **provider** to
    authenticate a *claimed identity* of a **user**. e.g. `OpenID <http://openid.net/>`_.
    """
    
    #: Indicates whether the **provider** supports access_with_credentials to
    #: **user's** protected resources.
    # TODO: Useless
    has_protected_resources = False
    
    def __init__(self, *args, **kwargs):   
        super(AuthenticationProvider, self).__init__(*args, **kwargs)
        
        # Lookup default identifier, if available in provider
        default_identifier = getattr(self, 'identifier', None)

        # Allow for custom name for the "id" querystring parameter.
        self.identifier_param = kwargs.get('identifier_param', 'id')
        
        # Get the identifier from request params, or use default as fallback.
        self.identifier = self.params.get(
            self.identifier_param, default_identifier)
        

PROVIDER_ID_MAP = [BaseProvider, AuthorizationProvider, AuthenticationProvider]
        













A BattlenetAuthomatic/authomatic/providers/gaeopenid.py => BattlenetAuthomatic/authomatic/providers/gaeopenid.py +98 -0
@@ 0,0 1,98 @@
# -*- coding: utf-8 -*-
"""
Google App Engine OpenID Providers
----------------------------------

|openid|_ provider implementations based on the |gae_users_api|_.

.. note::

    When using the :class:`GAEOpenID` provider, the :class:`.User` object
    will always have only the
    :attr:`.User.user_id`,
    :attr:`.User.email`,
    :attr:`.User.gae_user`
    attributes populated with data.
    Moreover the :attr:`.User.user_id` will always be empty on the
    `GAE Development Server <https://developers.google.com/appengine/docs/python/tools/devserver>`_.

.. autosummary::
    
    GAEOpenID
    Yahoo
    Google 

"""

import logging

from google.appengine.api import users

import authomatic.core as core
from authomatic import providers
from authomatic.exceptions import FailureError


__all__ = ['GAEOpenID', 'Yahoo', 'Google']


class GAEOpenID(providers.AuthenticationProvider):
    """
    |openid|_ provider based on the |gae_users_api|_.
    
    Accepts additional keyword arguments inherited from :class:`.AuthenticationProvider`.
    """
    
    @providers.login_decorator
    def login(self):
        """Launches the OpenID authentication procedure."""
        
        if self.params.get(self.identifier_param):
            #===================================================================
            # Phase 1 before redirect.
            #===================================================================
            self._log(logging.INFO, u'Starting OpenID authentication procedure.')
            
            url = users.create_login_url(dest_url=self.url, federated_identity=self.identifier)
            
            self._log(logging.INFO, u'Redirecting user to {0}.'.format(url))
            
            self.redirect(url)
        else:
            #===================================================================
            # Phase 2 after redirect.
            #===================================================================
            
            self._log(logging.INFO, u'Continuing OpenID authentication procedure after redirect.')
            
            user = users.get_current_user()
            
            if user:
                self._log(logging.INFO, u'Authentication successful.')
                self._log(logging.INFO, u'Creating user.')
                self.user = core.User(self,
                                     id=user.federated_identity(),
                                     email=user.email(),
                                     gae_user=user)
                
                #===============================================================
                # We're done
                #===============================================================
            else:
                raise FailureError('Unable to authenticate identifier "{0}"!'.format(self.identifier))


class Yahoo(GAEOpenID):
    """
    :class:`.GAEOpenID` provider with the :attr:`.identifier` set to ``"me.yahoo.com"``.
    """
    
    identifier = 'me.yahoo.com'


class Google(GAEOpenID):
    """
    :class:`.GAEOpenID` provider with the :attr:`.identifier` set to ``"https://www.google.com/accounts/o8/id"``.
    """
    
    identifier = 'https://www.google.com/accounts/o8/id'

A BattlenetAuthomatic/authomatic/providers/oauth1.py => BattlenetAuthomatic/authomatic/providers/oauth1.py +1259 -0
@@ 0,0 1,1259 @@
# -*- coding: utf-8 -*-
"""
|oauth1| Providers
--------------------

Providers which implement the |oauth1|_ protocol.

.. autosummary::
    
    OAuth1
    Bitbucket
    Flickr
    Meetup
    Plurk
    Twitter
    Tumblr
    UbuntuOne
    Vimeo
    Xero
    Xing
    Yahoo
    
"""

import abc
import authomatic.core as core
import binascii
import datetime
import hashlib
import hmac
import logging
import time
import uuid

from authomatic import providers
from authomatic.exceptions import (
    CancellationError,
    FailureError,
    OAuth1Error,
)
from authomatic import six
from authomatic.six.moves import urllib_parse as parse


__all__ = ['OAuth1', 'Bitbucket', 'Flickr', 'Meetup', 'Plurk', 'Twitter', 'Tumblr', 'UbuntuOne',
           'Vimeo', 'Xero', 'Xing', 'Yahoo']


def _normalize_params(params):
    """
    Returns a normalized query string sorted first by key, then by value
    excluding the ``realm`` and ``oauth_signature`` parameters
    as specified here: http://oauth.net/core/1.0a/#rfc.section.9.1.1
    
    :param params:
        :class:`dict` or :class:`list` of tuples.
    """
    
    if type(params) == dict:
        params = list(params.items())
    
    # remove "realm" and "oauth_signature"
    params = [(k, v) for k, v in params if k not in ('oauth_signature', 'realm')]
    # sort
    params.sort()
    # convert to query string
    qs = parse.urlencode(params)
    # replace "+" to "%20"
    qs = qs.replace('+', '%20')
    # replace "%7E" to "%20"
    qs = qs.replace('%7E', '~')
    
    return qs


def _join_by_ampersand(*args):
    return '&'.join([core.escape(i) for i in args])


def _create_base_string(method, base, params):
    """
    Returns base string for HMAC-SHA1 signature
    as specified in: http://oauth.net/core/1.0a/#rfc.section.9.1.3
    """
    
    normalized_qs = _normalize_params(params)
    return _join_by_ampersand(method, base, normalized_qs)


class BaseSignatureGenerator(object):
    """
    Abstract base class for all signature generators.
    """

    __metaclass__ = abc.ABCMeta

    #: :class:`str` The name of the signature method.
    method = ''
    
    @abc.abstractmethod
    def create_signature(self, method, base, params, consumer_secret, token_secret=''):
        """
        Must create signature based on the parameters as specified in
        http://oauth.net/core/1.0a/#signing_process.
        
        .. warning::
            
            |classmethod|

        :param str method:
            HTTP method of the request to be signed.
            
        :param str base:
            Base URL of the request without query string an fragment.
            
        :param dict params:
            Dictionary or list of tuples of the request parameters.
            
        :param str consumer_secret:
            :attr:`.core.Consumer.secret`
            
        :param str token_secret:
            Access token secret as specified in http://oauth.net/core/1.0a/#anchor3.
        
        :returns:
            The signature string.
        """


class HMACSHA1SignatureGenerator(BaseSignatureGenerator):
    """
    HMAC-SHA1 signature generator.
    See: http://oauth.net/core/1.0a/#anchor15
    """
    
    method = 'HMAC-SHA1'
    
    @classmethod
    def _create_key(cls, consumer_secret, token_secret=''):
        """
        Returns a key for HMAC-SHA1 signature
        as specified at: http://oauth.net/core/1.0a/#rfc.section.9.2
        
        :param str consumer_secret:
            :attr:`.core.Consumer.secret`
            
        :param str token_secret:
            Access token secret as specified in http://oauth.net/core/1.0a/#anchor3.
        
        :returns:
            Key to sign the request with.
        """
        
        return _join_by_ampersand(consumer_secret, token_secret or '')
    
    
    @classmethod
    def create_signature(cls, method, base, params, consumer_secret, token_secret=''):
        """
        Returns HMAC-SHA1 signature
        as specified at: http://oauth.net/core/1.0a/#rfc.section.9.2
        
        :param str method:
            HTTP method of the request to be signed.
            
        :param str base:
            Base URL of the request without query string an fragment.
            
        :param dict params:
            Dictionary or list of tuples of the request parameters.
            
        :param str consumer_secret:
            :attr:`.core.Consumer.secret`
            
        :param str token_secret:
            Access token secret as specified in http://oauth.net/core/1.0a/#anchor3.
        
        :returns:
            The signature string.
        """
        
        base_string = _create_base_string(method, base, params)
        key = cls._create_key(consumer_secret, token_secret)

        hashed = hmac.new(six.b(key), base_string.encode('utf-8'), hashlib.sha1)


        base64_encoded = binascii.b2a_base64(hashed.digest())[:-1]
        
        return base64_encoded


class PLAINTEXTSignatureGenerator(BaseSignatureGenerator):
    """
    PLAINTEXT signature generator.
    See: http://oauth.net/core/1.0a/#anchor21
    """
    
    method = 'PLAINTEXT'
    
    @classmethod
    def create_signature(cls, method, base, params, consumer_secret, token_secret=''):
        
        consumer_secret = parse.quote(consumer_secret, '')
        token_secret = parse.quote(token_secret, '')
        
        return parse.quote('&'.join((consumer_secret, token_secret)), '')


class OAuth1(providers.AuthorizationProvider):
    """
    Base class for |oauth1|_ providers.    
    """
    
    _signature_generator = HMACSHA1SignatureGenerator
    
    PROVIDER_TYPE_ID = 1
    REQUEST_TOKEN_REQUEST_TYPE = 1
    
    def __init__(self, *args, **kwargs):
        """
        Accepts additional keyword arguments:
        
        :param str consumer_key:
            The *key* assigned to our application (**consumer**) by the **provider**.
            
        :param str consumer_secret:
            The *secret* assigned to our application (**consumer**) by the **provider**.
            
        :param id:
            A unique short name used to serialize :class:`.Credentials`.
            
        :param dict user_authorization_params:
            A dictionary of additional request parameters for **user authorization request**.
            
        :param dict access_token_params:
            A dictionary of additional request parameters for **access token request**.
            
        :param dict request_token_params:
            A dictionary of additional request parameters for **request token request**.
        """
        
        super(OAuth1, self).__init__(*args, **kwargs)
        
        self.request_token_params = self._kwarg(kwargs, 'request_token_params', {})
    
    
    #===========================================================================
    # Abstract properties
    #===========================================================================
    
    @abc.abstractproperty
    def request_token_url(self):
        """
        :class:`str` URL where we can get the |oauth1| request token.
        see http://oauth.net/core/1.0a/#auth_step1.
        """
    
    
    #===========================================================================
    # Internal methods
    #===========================================================================
    
    @classmethod
    def create_request_elements(cls, request_type, credentials, url, params=None, headers=None,
                                body='', method='GET', verifier='', callback=''):
        """
        Creates |oauth1| request elements.
        """
        
        params = params or {}
        headers = headers or {}
        
        consumer_key = credentials.consumer_key or ''
        consumer_secret = credentials.consumer_secret or ''
        token = credentials.token or ''
        token_secret = credentials.token_secret or ''
        
        # separate url base and query parameters
        url, base_params = cls._split_url(url)
        
        # add extracted params to future params
        params.update(dict(base_params))
        
        if request_type == cls.USER_AUTHORIZATION_REQUEST_TYPE:
            # no need for signature
            if token:
                params['oauth_token'] = token
            else:
                raise OAuth1Error('Credentials with valid token are required to create User Authorization URL!')
        else:
            # signature needed
            if request_type == cls.REQUEST_TOKEN_REQUEST_TYPE:
                # Request Token URL
                if consumer_key and consumer_secret and callback:
                    params['oauth_consumer_key'] = consumer_key
                    params['oauth_callback'] = callback
                else:
                    raise OAuth1Error('Credentials with valid consumer_key, consumer_secret and ' +\
                                                             'callback are required to create Request Token URL!')
                
            elif request_type == cls.ACCESS_TOKEN_REQUEST_TYPE:
                # Access Token URL
                if consumer_key and consumer_secret and token and verifier:
                    params['oauth_token'] = token
                    params['oauth_consumer_key'] = consumer_key
                    params['oauth_verifier'] = verifier
                else:
                    raise OAuth1Error('Credentials with valid consumer_key, consumer_secret, token ' +\
                                                             'and argument verifier are required to create Access Token URL!')
                
            elif request_type == cls.PROTECTED_RESOURCE_REQUEST_TYPE:
                # Protected Resources URL
                if consumer_key and consumer_secret and token and token_secret:
                    params['oauth_token'] = token
                    params['oauth_consumer_key'] = consumer_key