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:

11  def __init__(self, **properties):
12    # Set Form properties and Data Bindings.
13    self.init_components(**properties)
14    self.subject = ""
15    self.details = ""
16    self.start = None
17    self.due = None
18
19    # Any code you write here will run before the form opens.
20    self.label_message.visible = False
21    if anvil.users.get_user():
22      self.card_details.visible = True
23      self.card_error.visible = False
24      self.button_add.visible = True
25    else:
26      self.card_details.visible = False
27      self.card_error.visible = True
28      self.button_add.visible = False

Code explaination

  • 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:

11  def __init__(self, **properties):
12    # Set Form properties and Data Bindings.
13    self.init_components(**properties)
14
15    # Any code you write here will run before the form opens.
16    if anvil.users.get_user():
17      self.card_details.visible = True
18      self.card_error.visible = False
19    else:
20      self.card_details.visible = False
21      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.

 1from ._anvil_designer import MainFormTemplate
 2from anvil import *
 3import anvil.server
 4import anvil.tables as tables
 5import anvil.tables.query as q
 6from anvil.tables import app_tables
 7import anvil.users
 8from ..HomeComponent import HomeComponent
 9from ..CalendarComponent import CalendarComponent
10from ..AddComponent import AddComponent
11from ..AccountComponent import AccountComponent
12from ..SetDetailsComponent import SetDetailsComponent
13from ..WelcomeComponent import WelcomeComponent

Now in the switch_component method add the following code:

25  def switch_component(self, state):
26    # set state
27    if state == "home":
28      if anvil.users.get_user():
29        cmpt = HomeComponent()
30      else:
31        cmpt = WelcomeComponent()
32      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

 1from ._anvil_designer import MainFormTemplate
 2from anvil import *
 3import anvil.server
 4import anvil.tables as tables
 5import anvil.tables.query as q
 6from anvil.tables import app_tables
 7import anvil.users
 8from ..HomeComponent import HomeComponent
 9from ..CalendarComponent import CalendarComponent
10from ..AddComponent import AddComponent
11from ..AccountComponent import AccountComponent
12from ..SetDetailsComponent import SetDetailsComponent
13from ..WelcomeComponent import WelcomeComponent
14
15
16class MainForm(MainFormTemplate):
17  def __init__(self, **properties):
18    # Set Form properties and Data Bindings.
19    self.init_components(**properties)
20    self.breadcrumb_stem = self.label_title.text
21
22    # Any code you write here will run before the form opens.
23    self.switch_component("home")
24
25  def switch_component(self, state):
26    # set state
27    if state == "home":
28      if anvil.users.get_user():
29        cmpt = HomeComponent()
30      else:
31        cmpt = WelcomeComponent()
32      breadcrumb = self.breadcrumb_stem
33    elif state == "account":
34      cmpt = AccountComponent()
35      breadcrumb = self.breadcrumb_stem + " - Account"
36    elif state == "add":
37      cmpt = AddComponent()
38      breadcrumb = self.breadcrumb_stem + " - Add"
39    elif state == "calendar":
40      cmpt = CalendarComponent()
41      breadcrumb = self.breadcrumb_stem + " - Calendar"
42    elif state == "details":
43      cmpt = SetDetailsComponent()
44      breadcrumb = self.breadcrumb_stem + " - Account - Set Details"
45    
46    # execution
47    self.content_panel.clear()
48    self.content_panel.add_component(cmpt)
49    self.label_title.text = breadcrumb
50    self.set_active_link(state)
51  
52  def set_active_link(self, state):
53    if state == "home":
54      self.link_home.role = "selected"
55    else:
56      self.link_home.role = None
57    if state == "add":
58      self.link_add.role = "selected"
59    else:
60      self.link_add.role = None
61    if state == "calendar":
62      self.link_calendar.role = "selected"
63    else:
64      self.link_calendar.role = None
65
66    self.link_register.visible = not anvil.users.get_user()
67    self.link_login.visible = not anvil.users.get_user()
68    self.link_account.visible = anvil.users.get_user()
69    self.link_logout.visible = anvil.users.get_user()
70  
71  # --- link handlers
72  def link_home_click(self, **event_args):
73    self.switch_component("home")
74
75  def link_calendar_click(self, **event_args):
76    self.switch_component("calendar")
77
78  def link_add_click(self, **event_args):
79    self.switch_component("add")
80
81  def link_account_click(self, **event_args):
82    """This method is called when the link is clicked"""
83    self.switch_component("account")
84
85  def link_register_click(self, **event_args):
86    anvil.users.signup_with_form(allow_cancel=True)
87    self.switch_component("details")
88
89  def link_login_click(self, **event_args):
90    anvil.users.login_with_form(allow_cancel=True)
91    self.switch_component("home")
92
93  def link_logout_click(self, **event_args):
94    anvil.users.logout()
95    self.switch_component("home")

Final AddComponent

 1from ._anvil_designer import AddComponentTemplate
 2from anvil import *
 3import anvil.server
 4import anvil.tables as tables
 5import anvil.tables.query as q
 6from anvil.tables import app_tables
 7import anvil.users
 8
 9
10class AddComponent(AddComponentTemplate):
11  def __init__(self, **properties):
12    # Set Form properties and Data Bindings.
13    self.init_components(**properties)
14    self.subject = ""
15    self.details = ""
16    self.start = None
17    self.due = None
18
19    # Any code you write here will run before the form opens.
20    self.label_message.visible = False
21    if anvil.users.get_user():
22      self.card_details.visible = True
23      self.card_error.visible = False
24      self.button_add.visible = True
25    else:
26      self.card_details.visible = False
27      self.card_error.visible = True
28      self.button_add.visible = False
29
30  def button_add_click(self, **event_args):
31    # validation
32    if not self.text_box_subject.text:
33      self.display_error("Subject name needed")
34    elif not self.text_box_details.text:
35      self.display_error("Assessment details needed")
36    elif not self.date_picker_start.date:
37      self.display_error("Start date needed")
38    elif not self.date_picker_due.date:
39      self.display_error("Due date needed")
40    else:
41      self.subject = self.text_box_subject.text
42      self.details = self.text_box_details.text
43      self.start = self.date_picker_start.date
44      self.due = self.date_picker_due.date
45      self.display_save(f"{self.subject} {self.details} assessment: {self.start} to {self.due} recorded")
46      anvil.server.call('add_assessment', self.subject, self.details, self.start, self.due)
47      self.reset_form()
48
49  def display_error(self, message):
50    self.label_message.visible = True
51    self.label_message.foreground = "#ff0000"
52    self.label_message.icon = "fa:exclamation-triangle"
53    self.label_message.bold = True
54    self.label_message.text = message
55
56  def display_save(self, message):
57    self.label_message.visible = True
58    self.label_message.foreground = "#000000"
59    self.label_message.icon = "fa:save"
60    self.label_message.bold = False
61    self.label_message.text = message
62
63  def reset_form(self):
64    self.subject = ""
65    self.details = ""
66    self.start = None
67    self.due = None
68    self.text_box_subject.text = ""
69    self.text_box_details.text = ""
70    self.date_picker_start.date = None
71    self.date_picker_due.date = None

Finial CalendarComponent

 1from ._anvil_designer import CalendarComponentTemplate
 2from anvil import *
 3import anvil.server
 4import anvil.tables as tables
 5import anvil.tables.query as q
 6from anvil.tables import app_tables
 7import anvil.users
 8
 9
10class CalendarComponent(CalendarComponentTemplate):
11  def __init__(self, **properties):
12    # Set Form properties and Data Bindings.
13    self.init_components(**properties)
14
15    # Any code you write here will run before the form opens.
16    if anvil.users.get_user():
17      self.card_details.visible = True
18      self.card_error.visible = False
19    else:
20      self.card_details.visible = False
21      self.card_error.visible = True