Skip to content

Latest commit

 

History

History
394 lines (311 loc) · 12.3 KB

24_restrict_accessibility.md

File metadata and controls

394 lines (311 loc) · 12.3 KB

Prevent Unauthorised Access

- 

Presently our web app is pretty much open to everyone. Even if you are not logged in, you can view the Home, Add and Calendar pages. Although these pages don't hold sensitive private information, the functionality of these pages depend up a user being logged in:

  • HomeComponent → displays the user's assessment
  • CalendarComponent → displays the user's assessment timeline
  • AddComponent → adds the user's assessment details

This means that if there is no user logged in, then these components will not function correctly. In this tutorial, we will fix that problem.

Planning

For the AddComponent and the CalendarComponent we will add a second card with and error message. Then, when the component is loaded, either the default card or the error card will be displayed.

When it comes to home we will choose between loading the HomeComponent or the WelcomeComponent, depending upon the user login status.

Layout

AddComponent Layout

Open the AddComponent in Design mode.

  1. Choose the current card
  2. Change it's name from self.card_1 to self.card_details

card details

  1. Add a new card between self.card_details and self.label_message (pay close attention to the brown lines)
  2. Name the new card self.card_error

card error

  1. Add a label to the self.card_error
  2. Change the text to Please login to access this feature
  3. Change foreground to #ff0000
  4. Change role to your desired formatting
  5. Change icon to fa:warning
  6. Change align to center

login error

CalendarComponent Layout

We haven't added anything to the CalendarComponent, so we will need to add two cards:

  • call the first one self.card_details
  • call the second one self.card_error

calendar

Leave self.card_details blank, and then change self.card_error to be the same as AddComponent.

Home Page Layout

We don't have to change the layout of the HomeComponent as we will simply load the WelcomeComponent instead. We will worry about the welcomeComponent layout later on.

Code

AddComponent code

The AddComponent code will simply check if someone is logged in and adjust the visibility of self.card_details and self.card_error accordingly.

Open AddComponent in Code mode.

In the __init__ method add the highlighted code:

:linenos:
:lineno-start: 11
:emphasize-lines: 11 - 18
  def __init__(self, **properties):
    # Set Form properties and Data Bindings.
    self.init_components(**properties)
    self.subject = ""
    self.details = ""
    self.start = None
    self.due = None

    # Any code you write here will run before the form opens.
    self.label_message.visible = False
    if anvil.users.get_user():
      self.card_details.visible = True
      self.card_error.visible = False
      self.button_add.visible = True
    else:
      self.card_details.visible = False
      self.card_error.visible = True
      self.button_add.visible = False
:class: notice
- **line 21** → checks if there is a user currently logged in
- **lines 22 - 24** → sets the UI to display the elements appropriate for a logged in user
- **lines 26 - 28** → sets the UI to display the elements appropriate for no logged in user

CalendarComponent code

Use the same logic to display the correct elements in the CalendarComponent.

In CalendarComponents __init__ method, add the code below:

:linenos:
:lineno-start: 11
:emphasize-lines: 6 - 11
  def __init__(self, **properties):
    # Set Form properties and Data Bindings.
    self.init_components(**properties)

    # Any code you write here will run before the form opens.
    if anvil.users.get_user():
      self.card_details.visible = True
      self.card_error.visible = False
    else:
      self.card_details.visible = False
      self.card_error.visible = True

Home page code

The Home page code is trickier, since it involves two components. Luckily we have centralised all component loading into the switch_component method in the MainForm. We can check for user status there and load the HomeComponent is a user is logged in or load the WelcomeComponent if there is no user logged in.

Open the MainForm in Code mode.

First, if we are going to load the WelcomeComponent, we need to import it. Add the highlighted code to the import statement.

:linenos:
:lineno-start: 1
:emphasize-lines: 13
from ._anvil_designer import MainFormTemplate
from anvil import *
import anvil.server
import anvil.tables as tables
import anvil.tables.query as q
from anvil.tables import app_tables
import anvil.users
from ..HomeComponent import HomeComponent
from ..CalendarComponent import CalendarComponent
from ..AddComponent import AddComponent
from ..AccountComponent import AccountComponent
from ..SetDetailsComponent import SetDetailsComponent
from ..WelcomeComponent import WelcomeComponent

Now in the switch_component method add the following code:

:linenos:
:lineno-start: 25
:emphasize-lines: 4-7
  def switch_component(self, state):
    # set state
    if state == "home":
      if anvil.users.get_user():
        cmpt = HomeComponent()
      else:
        cmpt = WelcomeComponent()
      breadcrumb = self.breadcrumb_stem
  • line 27 → note this code is only executed when the home page is being loaded
  • line 28 → checks if there is a user currently logged in
  • line 29 → loads the HomeComponent
  • line 30 & 31 → loads the WelcomeComponent is no user is logged in

Testing

Launch your website. Your should be logged in. Make sure that you can access all three of the pages we have worked on:

  • Home
  • Add
  • Calendar

Then logout and check all three pages again.

testing

Final code state

By the end of this tutorial your code should be the same as below:

Final MainForm

:linenos:
from ._anvil_designer import MainFormTemplate
from anvil import *
import anvil.server
import anvil.tables as tables
import anvil.tables.query as q
from anvil.tables import app_tables
import anvil.users
from ..HomeComponent import HomeComponent
from ..CalendarComponent import CalendarComponent
from ..AddComponent import AddComponent
from ..AccountComponent import AccountComponent
from ..SetDetailsComponent import SetDetailsComponent
from ..WelcomeComponent import WelcomeComponent


class MainForm(MainFormTemplate):
  def __init__(self, **properties):
    # Set Form properties and Data Bindings.
    self.init_components(**properties)
    self.breadcrumb_stem = self.label_title.text

    # Any code you write here will run before the form opens.
    self.switch_component("home")

  def switch_component(self, state):
    # set state
    if state == "home":
      if anvil.users.get_user():
        cmpt = HomeComponent()
      else:
        cmpt = WelcomeComponent()
      breadcrumb = self.breadcrumb_stem
    elif state == "account":
      cmpt = AccountComponent()
      breadcrumb = self.breadcrumb_stem + " - Account"
    elif state == "add":
      cmpt = AddComponent()
      breadcrumb = self.breadcrumb_stem + " - Add"
    elif state == "calendar":
      cmpt = CalendarComponent()
      breadcrumb = self.breadcrumb_stem + " - Calendar"
    elif state == "details":
      cmpt = SetDetailsComponent()
      breadcrumb = self.breadcrumb_stem + " - Account - Set Details"
    
    # execution
    self.content_panel.clear()
    self.content_panel.add_component(cmpt)
    self.label_title.text = breadcrumb
    self.set_active_link(state)
  
  def set_active_link(self, state):
    if state == "home":
      self.link_home.role = "selected"
    else:
      self.link_home.role = None
    if state == "add":
      self.link_add.role = "selected"
    else:
      self.link_add.role = None
    if state == "calendar":
      self.link_calendar.role = "selected"
    else:
      self.link_calendar.role = None

    self.link_register.visible = not anvil.users.get_user()
    self.link_login.visible = not anvil.users.get_user()
    self.link_account.visible = anvil.users.get_user()
    self.link_logout.visible = anvil.users.get_user()
  
  # --- link handlers
  def link_home_click(self, **event_args):
    self.switch_component("home")

  def link_calendar_click(self, **event_args):
    self.switch_component("calendar")

  def link_add_click(self, **event_args):
    self.switch_component("add")

  def link_account_click(self, **event_args):
    """This method is called when the link is clicked"""
    self.switch_component("account")

  def link_register_click(self, **event_args):
    anvil.users.signup_with_form(allow_cancel=True)
    self.switch_component("details")

  def link_login_click(self, **event_args):
    anvil.users.login_with_form(allow_cancel=True)
    self.switch_component("home")

  def link_logout_click(self, **event_args):
    anvil.users.logout()
    self.switch_component("home")

Final AddComponent

:linenos:
from ._anvil_designer import AddComponentTemplate
from anvil import *
import anvil.server
import anvil.tables as tables
import anvil.tables.query as q
from anvil.tables import app_tables
import anvil.users


class AddComponent(AddComponentTemplate):
  def __init__(self, **properties):
    # Set Form properties and Data Bindings.
    self.init_components(**properties)
    self.subject = ""
    self.details = ""
    self.start = None
    self.due = None

    # Any code you write here will run before the form opens.
    self.label_message.visible = False
    if anvil.users.get_user():
      self.card_details.visible = True
      self.card_error.visible = False
      self.button_add.visible = True
    else:
      self.card_details.visible = False
      self.card_error.visible = True
      self.button_add.visible = False

  def button_add_click(self, **event_args):
    # validation
    if not self.text_box_subject.text:
      self.display_error("Subject name needed")
    elif not self.text_box_details.text:
      self.display_error("Assessment details needed")
    elif not self.date_picker_start.date:
      self.display_error("Start date needed")
    elif not self.date_picker_due.date:
      self.display_error("Due date needed")
    else:
      self.subject = self.text_box_subject.text
      self.details = self.text_box_details.text
      self.start = self.date_picker_start.date
      self.due = self.date_picker_due.date
      self.display_save(f"{self.subject} {self.details} assessment: {self.start} to {self.due} recorded")
      anvil.server.call('add_assessment', self.subject, self.details, self.start, self.due)
      self.reset_form()

  def display_error(self, message):
    self.label_message.visible = True
    self.label_message.foreground = "#ff0000"
    self.label_message.icon = "fa:exclamation-triangle"
    self.label_message.bold = True
    self.label_message.text = message

  def display_save(self, message):
    self.label_message.visible = True
    self.label_message.foreground = "#000000"
    self.label_message.icon = "fa:save"
    self.label_message.bold = False
    self.label_message.text = message

  def reset_form(self):
    self.subject = ""
    self.details = ""
    self.start = None
    self.due = None
    self.text_box_subject.text = ""
    self.text_box_details.text = ""
    self.date_picker_start.date = None
    self.date_picker_due.date = None

Finial CalendarComponent

:linenos:
from ._anvil_designer import CalendarComponentTemplate
from anvil import *
import anvil.server
import anvil.tables as tables
import anvil.tables.query as q
from anvil.tables import app_tables
import anvil.users


class CalendarComponent(CalendarComponentTemplate):
  def __init__(self, **properties):
    # Set Form properties and Data Bindings.
    self.init_components(**properties)

    # Any code you write here will run before the form opens.
    if anvil.users.get_user():
      self.card_details.visible = True
      self.card_error.visible = False
    else:
      self.card_details.visible = False
      self.card_error.visible = True