2021-09-10 04:30:02 -07:00
<!DOCTYPE html>
<!-- [if IE 8]><html class="no - js lt - ie9" lang="en" > <![endif] -->
<!-- [if gt IE 8]><! --> < html class = "no-js" lang = "en" > <!-- <![endif] -->
< head >
< meta charset = "utf-8" >
< meta http-equiv = "X-UA-Compatible" content = "IE=edge" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< link rel = "canonical" href = "https://blog.trentsonlinedocs.xyz/posts/test-qr-svg-django/" >
< link rel = "shortcut icon" href = "../../img/favicon.ico" >
< title > Test QRCODE Svg in Django - Trent's Blog< / title >
< link rel = "stylesheet" href = "https://fonts.googleapis.com/css?family=Lato:400,700|Roboto+Slab:400,700|Inconsolata:400,700" / >
< link rel = "stylesheet" href = "../../css/theme.css" / >
< link rel = "stylesheet" href = "../../css/theme_extra.css" / >
< link rel = "stylesheet" href = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.5.0/styles/github.min.css" / >
< link href = "../../extra.css" rel = "stylesheet" / >
< script >
// Current page data
var mkdocs_page_name = "Test QRCODE Svg in Django";
var mkdocs_page_input_path = "posts/test-qr-svg-django.md";
var mkdocs_page_url = "/posts/test-qr-svg-django/";
< / script >
< script src = "../../js/jquery-2.1.1.min.js" defer > < / script >
< script src = "../../js/modernizr-2.8.3.min.js" defer > < / script >
< script src = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.5.0/highlight.min.js" > < / script >
< script > hljs . initHighlightingOnLoad ( ) ; < / script >
< / head >
< body class = "wy-body-for-nav" role = "document" >
< div class = "wy-grid-for-nav" >
< nav data-toggle = "wy-nav-shift" class = "wy-nav-side stickynav" >
< div class = "wy-side-scroll" >
< div class = "wy-side-nav-search" >
< a href = "../.." class = "icon icon-home" > Trent's Blog< / a >
< div role = "search" >
< form id = "rtd-search-form" class = "wy-form" action = "../../search.html" method = "get" >
< input type = "text" name = "q" placeholder = "Search docs" title = "Type search term here" / >
< / form >
< / div >
< / div >
2021-04-19 19:26:40 -07:00
2021-09-10 04:30:02 -07:00
< div class = "wy-menu wy-menu-vertical" data-spy = "affix" role = "navigation" aria-label = "main navigation" >
< p class = "caption" > < span class = "caption-text" > RSS< / span > < / p >
< ul >
< li class = "toctree-l1" > < a class = "reference internal" href = "../../rss/" > RSS< / a >
< / li >
< / ul >
< p class = "caption" > < span class = "caption-text" > Links< / span > < / p >
< ul >
< li class = "toctree-l1" > < a class = "reference internal" href = "../../links/" > Links< / a >
< / li >
< li class = "toctree-l1" > < a class = "" href = "https://trentpalmer.org" > TrentReads< / a >
< / li >
< li class = "toctree-l1" > < a class = "" href = "https://blog.trentpalmer.org" > AttentionSpanHistory< / a >
< / li >
< li class = "toctree-l1" > < a class = "" href = "https://github.com/TrentSPalmer" > GitHub< / a >
< / li >
< li class = "toctree-l1" > < a class = "" href = "https://twitter.com/boringtrent" > Twitter< / a >
< / li >
< li class = "toctree-l1" > < a class = "" href = "https://www.facebook.com/trentspalmer" > Facebook< / a >
< / li >
< li class = "toctree-l1" > < a class = "" href = "https://docs.trentsonlinedocs.xyz/" > TrentDocs< / a >
< / li >
< li class = "toctree-l1" > < a class = "" href = "https://trentsonlinedocs.xyz/hugo-themes-report/hugo-themes-report.html" > HugoThemesReport< / a >
< / li >
< li class = "toctree-l1" > < a class = "" href = "https://play.google.com/store/apps/details?id=org.trentpalmer.libre_gps_parser" > LibreGpsParser< / a >
< / li >
< li class = "toctree-l1" > < a class = "" href = "https://concise-pdx.com/" > ConcisePDX< / a >
< / li >
< li class = "toctree-l1" > < a class = "" href = "https://trentspalmer.github.io/fcc-challenges/" > FreeCodeCampChallenges< / a >
< / li >
< li class = "toctree-l1" > < a class = "" href = "https://trentpalmer.work/6a57bbe24d8244289610bf57533d6c6f/" > DeviceLayout< / a >
< / li >
< / ul >
< p class = "caption" > < span class = "caption-text" > Posts< / span > < / p >
< ul class = "current" >
< li class = "toctree-l1" > < a class = "reference internal" href = "../trents-favorite-podcasts/" > Trent's Favorite Podcasts< / a >
< / li >
< li class = "toctree-l1 current" > < a class = "reference internal current" href = "./" > Test QRCODE Svg in Django< / a >
< ul class = "current" >
< li class = "toctree-l2" > < a class = "reference internal" href = "#python-libraries-used" > Python Libraries Used< / a >
2021-09-02 13:03:42 -07:00
< / li >
2021-09-10 04:30:02 -07:00
< / ul >
< / li >
< li class = "toctree-l1" > < a class = "reference internal" href = "../prosody-photo-uploads/" > Prosody Photo Uploads< / a >
< / li >
< li class = "toctree-l1" > < a class = "reference internal" href = "../xmpp-apt-notifications/" > Xmpp Apt Notifications< / a >
< / li >
< li class = "toctree-l1" > < a class = "reference internal" href = "../apache-virtual-hosts/" > Apache Virtual Hosts< / a >
< / li >
< li class = "toctree-l1" > < a class = "reference internal" href = "../sendxmpp-handler-for-python-logging/" > SENDXMPPHandler for Python Logging< / a >
< / li >
< li class = "toctree-l1" > < a class = "reference internal" href = "../instructions-for-tethering-from-phone/" > Instructions For Tethering From Phone< / a >
< / li >
< li class = "toctree-l1" > < a class = "reference internal" href = "../lmde4-custom-partitions-disk-encryption/" > LMDE4 Custom Partitions Disk Encryption< / a >
< / li >
< li class = "toctree-l1" > < a class = "reference internal" href = "../linux-move-cursor-with-keyboard/" > Linux Move Cursor With Keyboard< / a >
< / li >
< li class = "toctree-l1" > < a class = "reference internal" href = "../simplified-raspberry-streaming/" > Simplified Raspberry Streaming< / a >
< / li >
< li class = "toctree-l1" > < a class = "reference internal" href = "../clear-linux-encrypted-xfs-root/" > Clear Linux Encrypted xfs Root< / a >
< / li >
< li class = "toctree-l1" > < a class = "reference internal" href = "../clear-linux-guest-virt-manager/" > Clear Linux Guest Virt Manager< / a >
< / li >
< li class = "toctree-l1" > < a class = "reference internal" href = "../faster-partitioning-with-sgdisk/" > Faster Partitioning With sgdisk< / a >
< / li >
< li class = "toctree-l1" > < a class = "reference internal" href = "../lmde3-xfs-full-disk-encryption/" > LMDE3 xfs Full Disk Encryption< / a >
< / li >
< li class = "toctree-l1" > < a class = "reference internal" href = "../rewrite-hugo-themes-report-in-python/" > Rewrite Hugo Themes Report in Python< / a >
< / li >
< / ul >
< / div >
< / div >
< / nav >
2021-09-02 13:03:42 -07:00
2021-09-10 04:30:02 -07:00
< section data-toggle = "wy-nav-shift" class = "wy-nav-content-wrap" >
2021-09-02 13:03:42 -07:00
2021-09-10 04:30:02 -07:00
< nav class = "wy-nav-top" role = "navigation" aria-label = "top navigation" >
< i data-toggle = "wy-nav-top" class = "fa fa-bars" > < / i >
< a href = "../.." > Trent's Blog< / a >
2021-04-19 19:26:40 -07:00
< / nav >
2021-09-02 14:05:12 -07:00
2021-09-10 04:30:02 -07:00
< div class = "wy-nav-content" >
< div class = "rst-content" >
< div role = "navigation" aria-label = "breadcrumbs navigation" >
< ul class = "wy-breadcrumbs" >
< li > < a href = "../.." > Docs< / a > » < / li >
2021-09-02 14:05:12 -07:00
2021-09-10 04:30:02 -07:00
< li > Posts » < / li >
2021-09-02 14:05:12 -07:00
2021-09-10 04:30:02 -07:00
< li > Test QRCODE Svg in Django< / li >
< li class = "wy-breadcrumbs-aside" >
2021-09-02 14:05:12 -07:00
< / li >
2021-04-19 19:26:40 -07:00
< / ul >
2021-09-10 04:30:02 -07:00
< hr / >
< / div >
< div role = "main" >
< div class = "section" >
2021-04-19 19:26:40 -07:00
< p > date: 2021-04-19< / p >
< h2 id = "introduction" > < strong > Introduction< / strong > < / h2 >
< p > I worked out a solution in django-testing, for testing a view that renders a qrcode
as an svg as an inline svg xml string.< / p >
< p > In case you are not familiar with
< a href = "https://en.wikipedia.org/wiki/Scalable_Vector_Graphics" target = "_blank" > svg< / a > ,
scalable vector graphics, it is an image that is
completely rendered from a
< a href = "https://en.wikipedia.org/wiki/String_(computer_science)" target = "_blank" > string< / a >
of text, or more accurately < a href = "https://en.wikipedia.org/wiki/XML" target = "_blank" > XML< / a >
text, unlike < a href = "https://en.wikipedia.org/wiki/Portable_Network_Graphics" target = "_blank" > PNG< / a >
or < a href = "https://en.wikipedia.org/wiki/JPEG" target = "_blank" > JPG< / a > , which are
< a href = "https://en.wikipedia.org/wiki/Binary_file" target = "_blank" > binary file< / a > s.< / p >
< h3 id = "python-libraries-used" > Python Libraries Used< / h3 >
< ul >
< li > < a href = "https://pypi.org/project/beautifulsoup4/" target = "_blank" > BeautifulSoup< / a >
to consume html< / li >
< li > < a href = "https://pypi.org/project/CairoSVG/" target = "_blank" > CairoSVG< / a >
to convert svg to png< / li >
< li > < a href = "https://pypi.org/project/Pillow/" target = "_blank" > python-pillow< / a >
to convert transparent png background to white< / li >
< li > < a href = "https://pypi.org/project/opencv-python/" target = "_blank" > opencv-python< / a >
to extract data from qrcode< / li >
< li > < a href = "https://pypi.org/project/pyotp/" target = "_blank" > python-pyotp< / a > < / li >
< / ul >
< h2 id = "form-screenshot" > < strong > Form ScreenShot< / strong > < / h2 >
< p > This is the form we will be testing.< / p >
< p > In real life you would confirm that you want to use
< a href = "https://en.wikipedia.org/wiki/Multi-factor_authentication" target = "_blank" > two-factor-authentication< / a >
by scanning the qrcode with an
< a href = "https://play.google.com/store/apps/details?id=org.shadowice.flocke.andotp" target = "_blank" > authentication app< / a >
on your smartphone, and then enter the
< a href = "https://en.wikipedia.org/wiki/Time-based_One-Time_Password" target = "_blank" > time-based one-time password< / a > .< / p >
< p > totp codes are derived from two things and two things only. A secret key and a time such as the current time.
For instance, an authentication application can tell you what your totp code is, but of course
in this testing scenario we use
< a href = "https://pypi.org/project/pyotp/" target = "_blank" > python-pyotp< / a > , after extracting
the secret key from the qrcode.< / p >
< div align = "center" >
< em > enable totp confirmation form< / em >
< / div >
< p > < img alt = ""ScreenShot of a confirmation form for enabling totp (in a django app)"" class = "center" src = "../../photos/IMG_screenshot_enable_totp_confirmation_form.jpg" / > < / p >
< h2 id = "import-python-libraries" > < strong > Import Python Libraries< / strong > < / h2 >
< div class = "highlight" > < pre > < span > < / span > < code > < span class = "c1" > # tp/accounts/tests/test_enable_totp_view.py< / span >
< span class = "c1" > # python manage.py test accounts.tests.test_enable_totp_view< / span >
< span class = "kn" > from< / span > < span class = "nn" > django.test< / span > < span class = "kn" > import< / span > < span class = "n" > TestCase< / span >
< span class = "kn" > from< / span > < span class = "nn" > django.contrib.auth.models< / span > < span class = "kn" > import< / span > < span class = "n" > User< / span >
< span class = "kn" > from< / span > < span class = "nn" > accounts.models< / span > < span class = "kn" > import< / span > < span class = "n" > Account< / span >
< span class = "kn" > from< / span > < span class = "nn" > django.urls< / span > < span class = "kn" > import< / span > < span class = "n" > reverse< / span >
< span class = "kn" > from< / span > < span class = "nn" > bs4< / span > < span class = "kn" > import< / span > < span class = "n" > BeautifulSoup< / span >
< span class = "kn" > import< / span > < span class = "nn" > cv2< / span >
< span class = "kn" > from< / span > < span class = "nn" > cairosvg< / span > < span class = "kn" > import< / span > < span class = "n" > svg2png< / span >
< span class = "kn" > from< / span > < span class = "nn" > PIL< / span > < span class = "kn" > import< / span > < span class = "n" > Image< / span >
< span class = "kn" > import< / span > < span class = "nn" > pyotp< / span >
< span class = "kn" > import< / span > < span class = "nn" > pathlib< / span >
< / code > < / pre > < / div >
< p > The Account model has a one-to-one relationship with the Django built-in
User model. This is where we keep track of the totp secret key and
boolean value for having totp authentication enabled.< / p >
< h2 id = "setup-testcase" > < strong > setUp TestCase< / strong > < / h2 >
< div class = "highlight" > < pre > < span > < / span > < code > < span class = "o" > ...< / span >
< span class = "kn" > import< / span > < span class = "nn" > pathlib< / span >
< span class = "k" > class< / span > < span class = "nc" > TestEnableTOTPViewTestCase< / span > < span class = "p" > (< / span > < span class = "n" > TestCase< / span > < span class = "p" > ):< / span >
< span class = "k" > def< / span > < span class = "nf" > setUp< / span > < span class = "p" > (< / span > < span class = "bp" > self< / span > < span class = "p" > ):< / span >
< span class = "n" > user_a< / span > < span class = "o" > =< / span > < span class = "n" > User< / span > < span class = "o" > .< / span > < span class = "n" > objects< / span > < span class = "o" > .< / span > < span class = "n" > create< / span > < span class = "p" > (< / span >
< span class = "n" > username< / span > < span class = "o" > =< / span > < span class = "s1" > ' user_a' < / span > < span class = "p" > )< / span >
< span class = "n" > user_a< / span > < span class = "o" > .< / span > < span class = "n" > set_password< / span > < span class = "p" > (< / span > < span class = "s1" > ' password_user_a' < / span > < span class = "p" > )< / span >
< span class = "n" > user_a< / span > < span class = "o" > .< / span > < span class = "n" > save< / span > < span class = "p" > ()< / span >
< span class = "n" > Account< / span > < span class = "o" > .< / span > < span class = "n" > objects< / span > < span class = "o" > .< / span > < span class = "n" > create< / span > < span class = "p" > (< / span > < span class = "n" > user< / span > < span class = "o" > =< / span > < span class = "n" > user_a< / span > < span class = "p" > )< / span >
< / code > < / pre > < / div >
< p > We create a test user and save that in the test database.< / p >
< h2 id = "get-form" > < strong > GET Form< / strong > < / h2 >
< p > < div class = "highlight" > < pre > < span > < / span > < code > < span class = "k" > class< / span > < span class = "nc" > TestEnableTOTPViewTestCase< / span > < span class = "p" > (< / span > < span class = "n" > TestCase< / span > < span class = "p" > ):< / span >
< span class = "o" > ...< / span >
< span class = "k" > def< / span > < span class = "nf" > test_enable_totp_view< / span > < span class = "p" > (< / span > < span class = "bp" > self< / span > < span class = "p" > ):< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > client< / span > < span class = "o" > .< / span > < span class = "n" > login< / span > < span class = "p" > (< / span >
< span class = "n" > username< / span > < span class = "o" > =< / span > < span class = "s1" > ' user_a' < / span > < span class = "p" > ,< / span >
< span class = "n" > password< / span > < span class = "o" > =< / span > < span class = "s1" > ' password_user_a' < / span >
< span class = "p" > )< / span >
< span class = "n" > get_response< / span > < span class = "o" > =< / span > < span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > client< / span > < span class = "o" > .< / span > < span class = "n" > get< / span > < span class = "p" > (< / span >
< span class = "n" > reverse< / span > < span class = "p" > (< / span > < span class = "s1" > ' accounts:enable_totp' < / span > < span class = "p" > )< / span >
< span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertEquals< / span > < span class = "p" > (< / span >
< span class = "n" > get_response< / span > < span class = "o" > .< / span > < span class = "n" > status_code< / span > < span class = "p" > ,< / span > < span class = "mi" > 200< / span >
< span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertTemplateUsed< / span > < span class = "p" > (< / span >
< span class = "n" > get_response< / span > < span class = "p" > ,< / span > < span class = "s1" > ' accounts/totp_form.html' < / span >
< span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertEquals< / span > < span class = "p" > (< / span >
< span class = "n" > get_response< / span > < span class = "o" > .< / span > < span class = "n" > request< / span > < span class = "p" > [< / span > < span class = "s1" > ' PATH_INFO' < / span > < span class = "p" > ],< / span >
< span class = "s1" > ' /accounts/enable-totp/' < / span >
< span class = "p" > )< / span >
< / code > < / pre > < / div >
The TestCase requires two requests. In the first request we GET the form
so that we can consume the qrcode.< / p >
< h2 id = "consume-svg-with-beautifulsoup" > < strong > Consume SVG with BeautifulSoup< / strong > < / h2 >
< p > < div class = "highlight" > < pre > < span > < / span > < code > < span class = "k" > class< / span > < span class = "nc" > TestEnableTOTPViewTestCase< / span > < span class = "p" > (< / span > < span class = "n" > TestCase< / span > < span class = "p" > ):< / span >
< span class = "o" > ...< / span >
< span class = "k" > def< / span > < span class = "nf" > test_enable_totp_view< / span > < span class = "p" > (< / span > < span class = "bp" > self< / span > < span class = "p" > ):< / span >
< span class = "o" > ...< / span >
< span class = "n" > soup< / span > < span class = "o" > =< / span > < span class = "n" > BeautifulSoup< / span > < span class = "p" > (< / span >
< span class = "n" > get_response< / span > < span class = "o" > .< / span > < span class = "n" > content< / span > < span class = "p" > ,< / span > < span class = "n" > features< / span > < span class = "o" > =< / span > < span class = "s2" > " lxml" < / span >
< span class = "p" > )< / span >
< span class = "n" > svg_container< / span > < span class = "o" > =< / span > < span class = "n" > soup< / span > < span class = "o" > .< / span > < span class = "n" > find< / span > < span class = "p" > (< / span >
< span class = "s2" > " div" < / span > < span class = "p" > ,< / span > < span class = "p" > {< / span > < span class = "s2" > " id" < / span > < span class = "p" > :< / span > < span class = "s2" > " svgcontainer" < / span > < span class = "p" > }< / span >
< span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertIsNotNone< / span > < span class = "p" > (< / span > < span class = "n" > svg_container< / span > < span class = "p" > )< / span >
< span class = "n" > scsvg< / span > < span class = "o" > =< / span > < span class = "n" > svg_container< / span > < span class = "o" > .< / span > < span class = "n" > findChild< / span > < span class = "p" > (< / span >
< span class = "s2" > " svg" < / span > < span class = "p" > ,< / span > < span class = "n" > recursive< / span > < span class = "o" > =< / span > < span class = "kc" > False< / span >
< span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertIsNotNone< / span > < span class = "p" > (< / span > < span class = "n" > scsvg< / span > < span class = "p" > )< / span >
< span class = "k" > with< / span > < span class = "nb" > open< / span > < span class = "p" > (< / span > < span class = "s1" > ' qr.svg' < / span > < span class = "p" > ,< / span > < span class = "s1" > ' w' < / span > < span class = "p" > )< / span > < span class = "k" > as< / span > < span class = "n" > f< / span > < span class = "p" > :< / span >
< span class = "n" > x_string< / span > < span class = "o" > =< / span > < span class = "s2" > " < ?xml version=' 1.0' " < / span >
< span class = "n" > x_string< / span > < span class = "o" > +=< / span > < span class = "s2" > " encoding=' utf-8' ?> < / span > < span class = "se" > \n< / span > < span class = "s2" > " < / span >
< span class = "n" > f< / span > < span class = "o" > .< / span > < span class = "n" > write< / span > < span class = "p" > (< / span > < span class = "n" > x_string< / span > < span class = "o" > +< / span > < span class = "nb" > str< / span > < span class = "p" > (< / span > < span class = "n" > scsvg< / span > < span class = "p" > ))< / span >
< / code > < / pre > < / div >
The inline xml for the svg comes to us as a child of a
< code > div< / code > with an < code > id< / code > of < em > svgcontainer< / em > . We capture the xml of the svg in the variable
< code > scsvg< / code > , and then write it out to disc.< / p >
< h2 id = "svg2png" > < strong > svg2png< / strong > < / h2 >
< p > < div class = "highlight" > < pre > < span > < / span > < code > < span class = "k" > class< / span > < span class = "nc" > TestEnableTOTPViewTestCase< / span > < span class = "p" > (< / span > < span class = "n" > TestCase< / span > < span class = "p" > ):< / span >
< span class = "o" > ...< / span >
< span class = "k" > def< / span > < span class = "nf" > test_enable_totp_view< / span > < span class = "p" > (< / span > < span class = "bp" > self< / span > < span class = "p" > ):< / span >
< span class = "o" > ...< / span >
< span class = "n" > svg2png< / span > < span class = "p" > (< / span >
< span class = "n" > url< / span > < span class = "o" > =< / span > < span class = "s1" > ' qr.svg' < / span > < span class = "p" > ,< / span > < span class = "n" > write_to< / span > < span class = "o" > =< / span > < span class = "s1" > ' qr.png' < / span > < span class = "p" > ,< / span >
< span class = "n" > scale< / span > < span class = "o" > =< / span > < span class = "mi" > 8< / span >
< span class = "p" > )< / span >
< / code > < / pre > < / div >
With svg2png from CairoSVG, we convert the svg to png format. Opencv seems
unable to consume the qrcode unless you scale it up.< / p >
< h2 id = "add-white-background" > < strong > Add White Background< / strong > < / h2 >
< p > < div class = "highlight" > < pre > < span > < / span > < code > < span class = "k" > class< / span > < span class = "nc" > TestEnableTOTPViewTestCase< / span > < span class = "p" > (< / span > < span class = "n" > TestCase< / span > < span class = "p" > ):< / span >
< span class = "o" > ...< / span >
< span class = "k" > def< / span > < span class = "nf" > test_enable_totp_view< / span > < span class = "p" > (< / span > < span class = "bp" > self< / span > < span class = "p" > ):< / span >
< span class = "o" > ...< / span >
< span class = "n" > t_image< / span > < span class = "o" > =< / span > < span class = "n" > Image< / span > < span class = "o" > .< / span > < span class = "n" > open< / span > < span class = "p" > (< / span > < span class = "s1" > ' qr.png' < / span > < span class = "p" > )< / span >
< span class = "n" > t_image< / span > < span class = "o" > .< / span > < span class = "n" > load< / span > < span class = "p" > ()< / span >
< span class = "n" > background< / span > < span class = "o" > =< / span > < span class = "n" > Image< / span > < span class = "o" > .< / span > < span class = "n" > new< / span > < span class = "p" > (< / span >
< span class = "s2" > " RGB" < / span > < span class = "p" > ,< / span > < span class = "n" > t_image< / span > < span class = "o" > .< / span > < span class = "n" > size< / span > < span class = "p" > ,< / span >
< span class = "p" > (< / span > < span class = "mi" > 255< / span > < span class = "p" > ,< / span > < span class = "mi" > 255< / span > < span class = "p" > ,< / span > < span class = "mi" > 255< / span > < span class = "p" > )< / span >
< span class = "p" > )< / span >
< span class = "n" > background< / span > < span class = "o" > .< / span > < span class = "n" > paste< / span > < span class = "p" > (< / span >
< span class = "n" > t_image< / span > < span class = "p" > ,< / span >
< span class = "n" > mask< / span > < span class = "o" > =< / span > < span class = "n" > t_image< / span > < span class = "o" > .< / span > < span class = "n" > split< / span > < span class = "p" > ()[< / span > < span class = "mi" > 3< / span > < span class = "p" > ]< / span >
< span class = "p" > )< / span >
< span class = "n" > background< / span > < span class = "o" > .< / span > < span class = "n" > save< / span > < span class = "p" > (< / span >
< span class = "s1" > ' qr.jpg' < / span > < span class = "p" > ,< / span > < span class = "s2" > " JPEG" < / span > < span class = "p" > ,< / span >
< span class = "n" > quality< / span > < span class = "o" > =< / span > < span class = "mi" > 100< / span >
< span class = "p" > )< / span >
< / code > < / pre > < / div >
We use Image from python-pillow to change the background from transparent
to white. Opencv seems unable to consume the qrcode when it has a transparent
background.< / p >
< h2 id = "extract-data-from-qrcode" > < strong > Extract Data From QRCODE< / strong > < / h2 >
< div class = "highlight" > < pre > < span > < / span > < code > < span class = "k" > class< / span > < span class = "nc" > TestEnableTOTPViewTestCase< / span > < span class = "p" > (< / span > < span class = "n" > TestCase< / span > < span class = "p" > ):< / span >
< span class = "o" > ...< / span >
< span class = "k" > def< / span > < span class = "nf" > test_enable_totp_view< / span > < span class = "p" > (< / span > < span class = "bp" > self< / span > < span class = "p" > ):< / span >
< span class = "o" > ...< / span >
< span class = "n" > image< / span > < span class = "o" > =< / span > < span class = "n" > cv2< / span > < span class = "o" > .< / span > < span class = "n" > imread< / span > < span class = "p" > (< / span > < span class = "s1" > ' qr.jpg' < / span > < span class = "p" > )< / span >
< span class = "n" > qr_det< / span > < span class = "o" > =< / span > < span class = "n" > cv2< / span > < span class = "o" > .< / span > < span class = "n" > QRCodeDetector< / span > < span class = "p" > ()< / span >
< span class = "n" > qrdata< / span > < span class = "o" > =< / span > < span class = "n" > qr_det< / span > < span class = "o" > .< / span > < span class = "n" > detectAndDecode< / span > < span class = "p" > (< / span > < span class = "n" > image< / span > < span class = "p" > )< / span >
< span class = "n" > totp_code< / span > < span class = "o" > =< / span > < span class = "n" > pyotp< / span > < span class = "o" > .< / span > < span class = "n" > TOTP< / span > < span class = "p" > (< / span >
< span class = "n" > pyotp< / span > < span class = "o" > .< / span > < span class = "n" > parse_uri< / span > < span class = "p" > (< / span > < span class = "n" > qrdata< / span > < span class = "p" > [< / span > < span class = "mi" > 0< / span > < span class = "p" > ])< / span > < span class = "o" > .< / span > < span class = "n" > secret< / span >
< span class = "p" > )< / span > < span class = "o" > .< / span > < span class = "n" > now< / span > < span class = "p" > ()< / span >
< / code > < / pre > < / div >
< ul >
< li > < code > qrdata[0]< / code > will be the < em > otpauth_uri< / em > .< / li >
< li > < code > pyotp.parse_uri(qrdata[0]).secret< / code > is the secret key.< / li >
< li > < code > totp_code< / code > is the one-time password.< / li >
< / ul >
< h2 id = "post-the-totp_code" > < strong > POST the < code > totp_code< / code > < / strong > < / h2 >
< p > < div class = "highlight" > < pre > < span > < / span > < code > < span class = "k" > class< / span > < span class = "nc" > TestEnableTOTPViewTestCase< / span > < span class = "p" > (< / span > < span class = "n" > TestCase< / span > < span class = "p" > ):< / span >
< span class = "o" > ...< / span >
< span class = "k" > def< / span > < span class = "nf" > test_enable_totp_view< / span > < span class = "p" > (< / span > < span class = "bp" > self< / span > < span class = "p" > ):< / span >
< span class = "o" > ...< / span >
< span class = "n" > totp_code< / span > < span class = "o" > =< / span > < span class = "n" > pyotp< / span > < span class = "o" > .< / span > < span class = "n" > TOTP< / span > < span class = "p" > (< / span >
< span class = "n" > pyotp< / span > < span class = "o" > .< / span > < span class = "n" > parse_uri< / span > < span class = "p" > (< / span > < span class = "n" > qrdata< / span > < span class = "p" > [< / span > < span class = "mi" > 0< / span > < span class = "p" > ])< / span > < span class = "o" > .< / span > < span class = "n" > secret< / span >
< span class = "p" > )< / span > < span class = "o" > .< / span > < span class = "n" > now< / span > < span class = "p" > ()< / span >
< span class = "n" > response< / span > < span class = "o" > =< / span > < span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > client< / span > < span class = "o" > .< / span > < span class = "n" > post< / span > < span class = "p" > (< / span >
< span class = "n" > reverse< / span > < span class = "p" > (< / span > < span class = "s1" > ' accounts:enable_totp' < / span > < span class = "p" > ),< / span >
< span class = "p" > {< / span > < span class = "s1" > ' totp_code' < / span > < span class = "p" > :< / span > < span class = "n" > totp_code< / span > < span class = "p" > },< / span >
< span class = "n" > follow< / span > < span class = "o" > =< / span > < span class = "kc" > True< / span >
< span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertEquals< / span > < span class = "p" > (< / span >
< span class = "n" > response< / span > < span class = "o" > .< / span > < span class = "n" > status_code< / span > < span class = "p" > ,< / span > < span class = "mi" > 200< / span >
< span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertTemplateUsed< / span > < span class = "p" > (< / span >
< span class = "n" > response< / span > < span class = "p" > ,< / span > < span class = "s1" > ' base_form.html' < / span >
< span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertEquals< / span > < span class = "p" > (< / span >
< span class = "n" > response< / span > < span class = "o" > .< / span > < span class = "n" > request< / span > < span class = "p" > [< / span > < span class = "s1" > ' PATH_INFO' < / span > < span class = "p" > ],< / span >
< span class = "s1" > ' /accounts/edit-profile/' < / span >
< span class = "p" > )< / span >
< span class = "n" > user_a< / span > < span class = "o" > =< / span > < span class = "n" > User< / span > < span class = "o" > .< / span > < span class = "n" > objects< / span > < span class = "o" > .< / span > < span class = "n" > get< / span > < span class = "p" > (< / span >
< span class = "n" > username< / span > < span class = "o" > =< / span > < span class = "s1" > ' user_a' < / span >
< span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertTrue< / span > < span class = "p" > (< / span > < span class = "n" > user_a< / span > < span class = "o" > .< / span > < span class = "n" > account< / span > < span class = "o" > .< / span > < span class = "n" > use_totp< / span > < span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertEquals< / span > < span class = "p" > (< / span >
< span class = "nb" > len< / span > < span class = "p" > (< / span > < span class = "n" > user_a< / span > < span class = "o" > .< / span > < span class = "n" > account< / span > < span class = "o" > .< / span > < span class = "n" > totp_key< / span > < span class = "p" > ),< / span > < span class = "mi" > 16< / span >
< span class = "p" > )< / span >
< span class = "n" > pathlib< / span > < span class = "o" > .< / span > < span class = "n" > Path< / span > < span class = "p" > (< / span > < span class = "s1" > ' qr.svg' < / span > < span class = "p" > )< / span > < span class = "o" > .< / span > < span class = "n" > unlink< / span > < span class = "p" > ()< / span >
< span class = "n" > pathlib< / span > < span class = "o" > .< / span > < span class = "n" > Path< / span > < span class = "p" > (< / span > < span class = "s1" > ' qr.png' < / span > < span class = "p" > )< / span > < span class = "o" > .< / span > < span class = "n" > unlink< / span > < span class = "p" > ()< / span >
< span class = "n" > pathlib< / span > < span class = "o" > .< / span > < span class = "n" > Path< / span > < span class = "p" > (< / span > < span class = "s1" > ' qr.jpg' < / span > < span class = "p" > )< / span > < span class = "o" > .< / span > < span class = "n" > unlink< / span > < span class = "p" > ()< / span >
< / code > < / pre > < / div >
Post the < code > totp_code< / code > back to the form, and then verify
that the database is updated, and then delete the image
files.< / p >
< h2 id = "complete-testcase" > < strong > Complete TestCase< / strong > < / h2 >
< div class = "highlight" > < pre > < span > < / span > < code > < span class = "c1" > # tp/accounts/tests/test_enable_totp_view.py< / span >
< span class = "c1" > # python manage.py test accounts.tests.test_enable_totp_view< / span >
< span class = "kn" > from< / span > < span class = "nn" > django.test< / span > < span class = "kn" > import< / span > < span class = "n" > TestCase< / span >
< span class = "kn" > from< / span > < span class = "nn" > django.contrib.auth.models< / span > < span class = "kn" > import< / span > < span class = "n" > User< / span >
< span class = "kn" > from< / span > < span class = "nn" > accounts.models< / span > < span class = "kn" > import< / span > < span class = "n" > Account< / span >
< span class = "kn" > from< / span > < span class = "nn" > django.urls< / span > < span class = "kn" > import< / span > < span class = "n" > reverse< / span >
< span class = "kn" > from< / span > < span class = "nn" > bs4< / span > < span class = "kn" > import< / span > < span class = "n" > BeautifulSoup< / span >
< span class = "kn" > import< / span > < span class = "nn" > cv2< / span >
< span class = "kn" > from< / span > < span class = "nn" > cairosvg< / span > < span class = "kn" > import< / span > < span class = "n" > svg2png< / span >
< span class = "kn" > from< / span > < span class = "nn" > PIL< / span > < span class = "kn" > import< / span > < span class = "n" > Image< / span >
< span class = "kn" > import< / span > < span class = "nn" > pyotp< / span >
< span class = "k" > class< / span > < span class = "nc" > TestEnableTOTPViewTestCase< / span > < span class = "p" > (< / span > < span class = "n" > TestCase< / span > < span class = "p" > ):< / span >
< span class = "c1" > # setUP TestCase< / span >
< span class = "k" > def< / span > < span class = "nf" > setUp< / span > < span class = "p" > (< / span > < span class = "bp" > self< / span > < span class = "p" > ):< / span >
< span class = "n" > user_a< / span > < span class = "o" > =< / span > < span class = "n" > User< / span > < span class = "o" > .< / span > < span class = "n" > objects< / span > < span class = "o" > .< / span > < span class = "n" > create< / span > < span class = "p" > (< / span >
< span class = "n" > username< / span > < span class = "o" > =< / span > < span class = "s1" > ' user_a' < / span > < span class = "p" > )< / span >
< span class = "n" > user_a< / span > < span class = "o" > .< / span > < span class = "n" > set_password< / span > < span class = "p" > (< / span > < span class = "s1" > ' password_user_a' < / span > < span class = "p" > )< / span >
< span class = "n" > user_a< / span > < span class = "o" > .< / span > < span class = "n" > save< / span > < span class = "p" > ()< / span >
< span class = "n" > Account< / span > < span class = "o" > .< / span > < span class = "n" > objects< / span > < span class = "o" > .< / span > < span class = "n" > create< / span > < span class = "p" > (< / span > < span class = "n" > user< / span > < span class = "o" > =< / span > < span class = "n" > user_a< / span > < span class = "p" > )< / span >
< span class = "k" > def< / span > < span class = "nf" > test_enable_totp_view< / span > < span class = "p" > (< / span > < span class = "bp" > self< / span > < span class = "p" > ):< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > client< / span > < span class = "o" > .< / span > < span class = "n" > login< / span > < span class = "p" > (< / span >
< span class = "n" > username< / span > < span class = "o" > =< / span > < span class = "s1" > ' user_a' < / span > < span class = "p" > ,< / span >
< span class = "n" > password< / span > < span class = "o" > =< / span > < span class = "s1" > ' password_user_a' < / span >
< span class = "p" > )< / span >
< span class = "c1" > # GET Form< / span >
< span class = "n" > get_response< / span > < span class = "o" > =< / span > < span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > client< / span > < span class = "o" > .< / span > < span class = "n" > get< / span > < span class = "p" > (< / span >
< span class = "n" > reverse< / span > < span class = "p" > (< / span > < span class = "s1" > ' accounts:enable_totp' < / span > < span class = "p" > )< / span >
< span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertEquals< / span > < span class = "p" > (< / span >
< span class = "n" > get_response< / span > < span class = "o" > .< / span > < span class = "n" > status_code< / span > < span class = "p" > ,< / span > < span class = "mi" > 200< / span >
< span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertTemplateUsed< / span > < span class = "p" > (< / span >
< span class = "n" > get_response< / span > < span class = "p" > ,< / span > < span class = "s1" > ' accounts/totp_form.html' < / span >
< span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertEquals< / span > < span class = "p" > (< / span >
< span class = "n" > get_response< / span > < span class = "o" > .< / span > < span class = "n" > request< / span > < span class = "p" > [< / span > < span class = "s1" > ' PATH_INFO' < / span > < span class = "p" > ],< / span >
< span class = "s1" > ' /accounts/enable-totp/' < / span >
< span class = "p" > )< / span >
< span class = "c1" > # Consume SVG with BeautifulSoup< / span >
< span class = "n" > soup< / span > < span class = "o" > =< / span > < span class = "n" > BeautifulSoup< / span > < span class = "p" > (< / span >
< span class = "n" > get_response< / span > < span class = "o" > .< / span > < span class = "n" > content< / span > < span class = "p" > ,< / span > < span class = "n" > features< / span > < span class = "o" > =< / span > < span class = "s2" > " lxml" < / span >
< span class = "p" > )< / span >
< span class = "n" > svg_container< / span > < span class = "o" > =< / span > < span class = "n" > soup< / span > < span class = "o" > .< / span > < span class = "n" > find< / span > < span class = "p" > (< / span >
< span class = "s2" > " div" < / span > < span class = "p" > ,< / span > < span class = "p" > {< / span > < span class = "s2" > " id" < / span > < span class = "p" > :< / span > < span class = "s2" > " svgcontainer" < / span > < span class = "p" > }< / span >
< span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertIsNotNone< / span > < span class = "p" > (< / span > < span class = "n" > svg_container< / span > < span class = "p" > )< / span >
< span class = "n" > scsvg< / span > < span class = "o" > =< / span > < span class = "n" > svg_container< / span > < span class = "o" > .< / span > < span class = "n" > findChild< / span > < span class = "p" > (< / span >
< span class = "s2" > " svg" < / span > < span class = "p" > ,< / span > < span class = "n" > recursive< / span > < span class = "o" > =< / span > < span class = "kc" > False< / span >
< span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertIsNotNone< / span > < span class = "p" > (< / span > < span class = "n" > scsvg< / span > < span class = "p" > )< / span >
< span class = "k" > with< / span > < span class = "nb" > open< / span > < span class = "p" > (< / span > < span class = "s1" > ' qr.svg' < / span > < span class = "p" > ,< / span > < span class = "s1" > ' w' < / span > < span class = "p" > )< / span > < span class = "k" > as< / span > < span class = "n" > f< / span > < span class = "p" > :< / span >
< span class = "n" > x_string< / span > < span class = "o" > =< / span > < span class = "s2" > " < ?xml version=' 1.0' " < / span >
< span class = "n" > x_string< / span > < span class = "o" > +=< / span > < span class = "s2" > " encoding=' utf-8' ?> < / span > < span class = "se" > \n< / span > < span class = "s2" > " < / span >
< span class = "n" > f< / span > < span class = "o" > .< / span > < span class = "n" > write< / span > < span class = "p" > (< / span > < span class = "n" > x_string< / span > < span class = "o" > +< / span > < span class = "nb" > str< / span > < span class = "p" > (< / span > < span class = "n" > scsvg< / span > < span class = "p" > ))< / span >
< span class = "c1" > # svg2png< / span >
< span class = "n" > svg2png< / span > < span class = "p" > (< / span >
< span class = "n" > url< / span > < span class = "o" > =< / span > < span class = "s1" > ' qr.svg' < / span > < span class = "p" > ,< / span > < span class = "n" > write_to< / span > < span class = "o" > =< / span > < span class = "s1" > ' qr.png' < / span > < span class = "p" > ,< / span >
< span class = "n" > scale< / span > < span class = "o" > =< / span > < span class = "mi" > 8< / span >
< span class = "p" > )< / span >
< span class = "c1" > # add white background< / span >
< span class = "n" > t_image< / span > < span class = "o" > =< / span > < span class = "n" > Image< / span > < span class = "o" > .< / span > < span class = "n" > open< / span > < span class = "p" > (< / span > < span class = "s1" > ' qr.png' < / span > < span class = "p" > )< / span >
< span class = "n" > t_image< / span > < span class = "o" > .< / span > < span class = "n" > load< / span > < span class = "p" > ()< / span >
< span class = "n" > background< / span > < span class = "o" > =< / span > < span class = "n" > Image< / span > < span class = "o" > .< / span > < span class = "n" > new< / span > < span class = "p" > (< / span >
< span class = "s2" > " RGB" < / span > < span class = "p" > ,< / span > < span class = "n" > t_image< / span > < span class = "o" > .< / span > < span class = "n" > size< / span > < span class = "p" > ,< / span >
< span class = "p" > (< / span > < span class = "mi" > 255< / span > < span class = "p" > ,< / span > < span class = "mi" > 255< / span > < span class = "p" > ,< / span > < span class = "mi" > 255< / span > < span class = "p" > )< / span >
< span class = "p" > )< / span >
< span class = "n" > background< / span > < span class = "o" > .< / span > < span class = "n" > paste< / span > < span class = "p" > (< / span >
< span class = "n" > t_image< / span > < span class = "p" > ,< / span >
< span class = "n" > mask< / span > < span class = "o" > =< / span > < span class = "n" > t_image< / span > < span class = "o" > .< / span > < span class = "n" > split< / span > < span class = "p" > ()[< / span > < span class = "mi" > 3< / span > < span class = "p" > ]< / span >
< span class = "p" > )< / span >
< span class = "n" > background< / span > < span class = "o" > .< / span > < span class = "n" > save< / span > < span class = "p" > (< / span >
< span class = "s1" > ' qr.jpg' < / span > < span class = "p" > ,< / span > < span class = "s2" > " JPEG" < / span > < span class = "p" > ,< / span >
< span class = "n" > quality< / span > < span class = "o" > =< / span > < span class = "mi" > 100< / span >
< span class = "p" > )< / span >
< span class = "c1" > # extract data from qrcode< / span >
< span class = "n" > image< / span > < span class = "o" > =< / span > < span class = "n" > cv2< / span > < span class = "o" > .< / span > < span class = "n" > imread< / span > < span class = "p" > (< / span > < span class = "s1" > ' qr.jpg' < / span > < span class = "p" > )< / span >
< span class = "n" > qr_det< / span > < span class = "o" > =< / span > < span class = "n" > cv2< / span > < span class = "o" > .< / span > < span class = "n" > QRCodeDetector< / span > < span class = "p" > ()< / span >
< span class = "n" > qrdata< / span > < span class = "o" > =< / span > < span class = "n" > qr_det< / span > < span class = "o" > .< / span > < span class = "n" > detectAndDecode< / span > < span class = "p" > (< / span > < span class = "n" > image< / span > < span class = "p" > )< / span >
< span class = "n" > totp_code< / span > < span class = "o" > =< / span > < span class = "n" > pyotp< / span > < span class = "o" > .< / span > < span class = "n" > TOTP< / span > < span class = "p" > (< / span >
< span class = "n" > pyotp< / span > < span class = "o" > .< / span > < span class = "n" > parse_uri< / span > < span class = "p" > (< / span > < span class = "n" > qrdata< / span > < span class = "p" > [< / span > < span class = "mi" > 0< / span > < span class = "p" > ])< / span > < span class = "o" > .< / span > < span class = "n" > secret< / span >
< span class = "p" > )< / span > < span class = "o" > .< / span > < span class = "n" > now< / span > < span class = "p" > ()< / span >
< span class = "n" > totp_code< / span > < span class = "o" > =< / span > < span class = "n" > pyotp< / span > < span class = "o" > .< / span > < span class = "n" > TOTP< / span > < span class = "p" > (< / span >
< span class = "n" > pyotp< / span > < span class = "o" > .< / span > < span class = "n" > parse_uri< / span > < span class = "p" > (< / span > < span class = "n" > qrdata< / span > < span class = "p" > [< / span > < span class = "mi" > 0< / span > < span class = "p" > ])< / span > < span class = "o" > .< / span > < span class = "n" > secret< / span >
< span class = "p" > )< / span > < span class = "o" > .< / span > < span class = "n" > now< / span > < span class = "p" > ()< / span >
< span class = "c1" > # POST the `totp_code`< / span >
< span class = "n" > response< / span > < span class = "o" > =< / span > < span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > client< / span > < span class = "o" > .< / span > < span class = "n" > post< / span > < span class = "p" > (< / span >
< span class = "n" > reverse< / span > < span class = "p" > (< / span > < span class = "s1" > ' accounts:enable_totp' < / span > < span class = "p" > ),< / span >
< span class = "p" > {< / span > < span class = "s1" > ' totp_code' < / span > < span class = "p" > :< / span > < span class = "n" > totp_code< / span > < span class = "p" > },< / span >
< span class = "n" > follow< / span > < span class = "o" > =< / span > < span class = "kc" > True< / span >
< span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertEquals< / span > < span class = "p" > (< / span >
< span class = "n" > response< / span > < span class = "o" > .< / span > < span class = "n" > status_code< / span > < span class = "p" > ,< / span > < span class = "mi" > 200< / span >
< span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertTemplateUsed< / span > < span class = "p" > (< / span >
< span class = "n" > response< / span > < span class = "p" > ,< / span > < span class = "s1" > ' base_form.html' < / span >
< span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertEquals< / span > < span class = "p" > (< / span >
< span class = "n" > response< / span > < span class = "o" > .< / span > < span class = "n" > request< / span > < span class = "p" > [< / span > < span class = "s1" > ' PATH_INFO' < / span > < span class = "p" > ],< / span >
< span class = "s1" > ' /accounts/edit-profile/' < / span >
< span class = "p" > )< / span >
< span class = "n" > user_a< / span > < span class = "o" > =< / span > < span class = "n" > User< / span > < span class = "o" > .< / span > < span class = "n" > objects< / span > < span class = "o" > .< / span > < span class = "n" > get< / span > < span class = "p" > (< / span >
< span class = "n" > username< / span > < span class = "o" > =< / span > < span class = "s1" > ' user_a' < / span >
< span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertTrue< / span > < span class = "p" > (< / span > < span class = "n" > user_a< / span > < span class = "o" > .< / span > < span class = "n" > account< / span > < span class = "o" > .< / span > < span class = "n" > use_totp< / span > < span class = "p" > )< / span >
< span class = "bp" > self< / span > < span class = "o" > .< / span > < span class = "n" > assertEquals< / span > < span class = "p" > (< / span >
< span class = "nb" > len< / span > < span class = "p" > (< / span > < span class = "n" > user_a< / span > < span class = "o" > .< / span > < span class = "n" > account< / span > < span class = "o" > .< / span > < span class = "n" > totp_key< / span > < span class = "p" > ),< / span > < span class = "mi" > 16< / span >
< span class = "p" > )< / span >
< span class = "n" > pathlib< / span > < span class = "o" > .< / span > < span class = "n" > Path< / span > < span class = "p" > (< / span > < span class = "s1" > ' qr.svg' < / span > < span class = "p" > )< / span > < span class = "o" > .< / span > < span class = "n" > unlink< / span > < span class = "p" > ()< / span >
< span class = "n" > pathlib< / span > < span class = "o" > .< / span > < span class = "n" > Path< / span > < span class = "p" > (< / span > < span class = "s1" > ' qr.png' < / span > < span class = "p" > )< / span > < span class = "o" > .< / span > < span class = "n" > unlink< / span > < span class = "p" > ()< / span >
< span class = "n" > pathlib< / span > < span class = "o" > .< / span > < span class = "n" > Path< / span > < span class = "p" > (< / span > < span class = "s1" > ' qr.jpg' < / span > < span class = "p" > )< / span > < span class = "o" > .< / span > < span class = "n" > unlink< / span > < span class = "p" > ()< / span >
< / code > < / pre > < / div >
< / div >
< / div >
2021-09-10 04:30:02 -07:00
< footer >
2021-04-19 19:26:40 -07:00
2021-09-10 04:30:02 -07:00
< div class = "rst-footer-buttons" role = "navigation" aria-label = "footer navigation" >
2021-04-19 19:26:40 -07:00
2021-09-10 04:30:02 -07:00
< a href = "../prosody-photo-uploads/" class = "btn btn-neutral float-right" title = "Prosody Photo Uploads" > Next < span class = "icon icon-circle-arrow-right" > < / span > < / a >
2021-04-19 19:26:40 -07:00
2021-09-10 04:30:02 -07:00
< a href = "../trents-favorite-podcasts/" class = "btn btn-neutral" title = "Trent's Favorite Podcasts" > < span class = "icon icon-circle-arrow-left" > < / span > Previous< / a >
2021-09-02 13:52:37 -07:00
2021-09-10 04:30:02 -07:00
< / div >
< hr / >
< div role = "contentinfo" >
<!-- Copyright etc -->
2021-09-02 13:52:37 -07:00
2021-04-19 19:26:40 -07:00
< / div >
2021-09-10 04:30:02 -07:00
Built with < a href = "https://www.mkdocs.org/" > MkDocs< / a > using a < a href = "https://github.com/snide/sphinx_rtd_theme" > theme< / a > provided by < a href = "https://readthedocs.org" > Read the Docs< / a > .
2021-04-19 19:26:40 -07:00
< / footer >
2021-09-10 04:30:02 -07:00
< / div >
< / div >
< / section >
< / div >
< div class = "rst-versions" role = "note" aria-label = "versions" >
< span class = "rst-current-version" data-toggle = "rst-current-version" >
2021-04-19 19:26:40 -07:00
2021-09-10 04:30:02 -07:00
< span > < a href = "../trents-favorite-podcasts/" style = "color: #fcfcfc" > « Previous< / a > < / span >
2021-04-19 19:26:40 -07:00
2021-09-10 04:30:02 -07:00
< span > < a href = "../prosody-photo-uploads/" style = "color: #fcfcfc" > Next » < / a > < / span >
< / span >
< / div >
< script > var base _url = '../..' ; < / script >
< script src = "../../js/theme_extra.js" defer > < / script >
< script src = "../../js/theme.js" defer > < / script >
< script src = "../../search/main.js" defer > < / script >
< script defer >
window.onload = function () {
SphinxRtdTheme.Navigation.enable(true);
};
< / script >
< / body >
< / html >