13_Developer_Guide - Anisan/osysHome-Users GitHub Wiki

Developer Guide

Relevant source files

The following files were used as context for generating this wiki page:

Purpose and Scope

This Developer Guide provides information for engineers who want to extend or modify the osysHome-Users plugin. The guide covers the plugin architecture, extension points, and development patterns. For information about using the user management system as an administrator, see User Management Plugin and for details on the role system, see Role-Based Access Control.

Plugin Architecture Overview

The osysHome-Users plugin follows a modular design pattern built on Flask. Understanding this architecture is essential for making modifications or extensions.

classDiagram
    class BasePlugin {
        +__init__(app)
        +initialization()
        +admin(request)
        +render(template, content)
    }
    
    class Users {
        +__init__(app)
        +initialization()
        +admin(request)
        -handle_add_user()
        -handle_edit_user()
        -handle_delete_user()
        -handle_set_password()
        -handle_upload_image()
    }
    
    class UserForm {
        +username: StringField
        +image: StringField
        +role: SelectField
        +home_page: StringField
        +apikey: StringField
        +timezone: StringField
        +submit: SubmitField
    }
    
    class PasswordForm {
        +password: StringField
        +repeat_password: StringField
        +submit: SubmitField
    }
    
    BasePlugin <|-- Users
    Users --> UserForm: uses
    Users --> PasswordForm: uses
Loading

The Users plugin inherits from BasePlugin and connects to form classes for data validation and collection.

Sources: init.py:19-95, forms/UserForm.py:6-13, forms/PasswordForm.py:6-9

Operation Flow

The plugin uses an operation-based routing system where a single endpoint handles multiple operations through the op query parameter.

flowchart TD
    A["admin(request)"] --> B{"op parameter?"}
    B -->|"op=add"| C["Create new user"]
    B -->|"op=edit"| D["Edit existing user"]
    B -->|"op=delete"| E["Delete user"]
    B -->|"op=setPassword"| F["Change password"]
    B -->|"op=upload_image"| G["Upload avatar"]
    B -->|"no op"| H["List all users"]
    
    C -->|"POST"| C1["Validate form"]
    C1 -->|"valid"| C2["addObject()"]
    C1 -->|"invalid"| C3["Re-render form"]
    
    D -->|"POST"| D1["Validate form"]
    D1 -->|"valid"| D2["setProperty()"]
    D1 -->|"invalid"| D3["Re-render form"]
    
    E --> E1["deleteObject()"]
    
    F -->|"POST"| F1["Validate form"]
    F1 -->|"valid"| F2["set_password()"]
    F1 -->|"invalid"| F3["Re-render form"]
    
    G -->|"POST"| G1["Save file"]
    G1 --> G2["Update image URL"]
Loading

Understanding this flow is essential when adding new operations or modifying existing ones.

Sources: init.py:31-94

Extending the Plugin

Adding New User Properties

To add new properties to the user model:

  1. Extend the UserForm class with new fields.
  2. Update the user.html template to display the new fields.
  3. Modify the plugin's admin method to handle the new properties.

Example for adding a new "department" field:

  1. Add to UserForm:
# In forms/UserForm.py
department = StringField('Department')
  1. Store the property in the op=add and op=edit handlers:
# In __init__.py admin method
obj.setProperty("department", form.department.data)

Sources: forms/UserForm.py:6-13, init.py:34-44, init.py:45-56

Adding New Operations

To add a new operation to the plugin:

  1. Add a new branch to the op conditional in the admin method.
  2. Implement the operation logic.
  3. Create any necessary templates or modify existing ones.

Example structure for a new operation:

# In __init__.py admin method
elif op == 'new_operation':
    # Handle GET request (display form)
    if request.method == 'GET':
        # Initialize form or prepare data
        return self.render('template.html', context_data)
    
    # Handle POST request (process form submission)
    if form.validate_on_submit():
        # Process form data
        return redirect(self.name)
    else:
        # Re-render form with validation errors
        return self.render('template.html', context_data)

Sources: init.py:31-87

Working with Templates

The plugin uses Jinja2 templates with a consistent inheritance structure:

flowchart TD
    A["layouts/module_admin.html"] --> B["templates/users.html"]
    A --> C["templates/user.html"]
    A --> D["templates/password.html"]
    
    E["forms/UserForm.py"] --> C
    F["forms/PasswordForm.py"] --> D
Loading

When modifying templates:

  1. Respect the block structure defined in the parent template.
  2. Use the {{ form.field.label() }} and {{ form.field() }} pattern for form fields.
  3. For AJAX operations, follow the pattern used in the image upload functionality.

Sources: templates/user.html:1-159

Client-Side Development

API Key Generation

The plugin includes JavaScript for secure client-side API key generation. When implementing similar functionality:

  1. Use the native crypto API for secure random values.
  2. Apply appropriate formatting for readability.
function generateSecureKey() {
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    const crypto = window.crypto || window.msCrypto;
    const randomValues = new Uint8Array(16);
    crypto.getRandomValues(randomValues);
    
    return Array.from(randomValues)
        .map(v => chars[v % chars.length])
        .join('')
        .replace(/(.{4})/g, '$1-')
        .slice(0, -1);
}

Sources: templates/user.html:141-157

File Upload Handling

For implementing file uploads:

  1. Use FormData to prepare the upload payload.
  2. Include appropriate error handling.
  3. Update UI elements after successful upload.

The plugin uses this pattern for avatar image uploads:

function uploadFile() {
    var formData = new FormData($('#uploadForm')[0]);
    formData.append('user', "{{ form.username.data}}");
    
    $.ajax({
        url: 'Users?op=upload_image',
        type: 'POST',
        data: formData,
        contentType: false,
        processData: false,
        success: function(response) {
            // Handle success
        },
        error: function(xhr, status, error) {
            // Handle error
        }
    });
}

Sources: templates/user.html:118-140

Data Modeling and Object Access

The plugin uses a core application object system rather than direct database access. Understanding this pattern is crucial when extending the plugin.

sequenceDiagram
    participant Plugin as "Users Plugin"
    participant Core as "Core API"
    participant Storage as "Object Storage"
    
    Note over Plugin, Storage: Creating a user
    Plugin->>Core: addObject(username, "Users")
    Core->>Storage: Create object
    Core-->>Plugin: Object reference
    Plugin->>Core: obj.setProperty("field", value)
    Core->>Storage: Update object
    
    Note over Plugin, Storage: Getting user data
    Plugin->>Core: getObject(username)
    Core->>Storage: Retrieve object
    Core-->>Plugin: Object reference
    Plugin->>Core: obj.getProperty("field")
    Core-->>Plugin: Property value
    
    Note over Plugin, Storage: Updating user data
    Plugin->>Core: obj.setProperty("field", value)
    Core->>Storage: Update object
Loading

Key methods for working with the object system:

Method Purpose Example Usage
getObjectsByClass() Retrieve all objects of a class users = getObjectsByClass("Users")
getObject() Get a specific object by ID obj = getObject(user_name)
addObject() Create a new object obj = addObject(form.username.data, "Users")
setProperty() Set a property on an object obj.setProperty("role", form.role.data)
getProperty() Get a property from an object obj.getProperty("lastLogin")
deleteObject() Delete an object deleteObject(user_name)

Sources: init.py:31-94

Security Considerations

When extending the plugin, keep these security best practices in mind:

  1. Input Validation: Always use WTForms validators to validate user input.
  2. Password Handling: Never store plaintext passwords. The plugin uses the set_password() method from the User model.
  3. File Uploads: Validate file types and sizes for uploaded images to prevent security vulnerabilities.
  4. API Keys: Generate strong, random API keys and validate them properly when used for authentication.

Sources: init.py:60-69, templates/user.html:146-157

Development Workflow

When modifying the plugin:

  1. Fork the repository from https://github.com/Anisan/osysHome-Users
  2. Make your changes following the architecture patterns described in this guide
  3. Test your changes thoroughly
  4. Submit a pull request with a detailed description of your changes

Troubleshooting

Common issues when extending the plugin:

  1. Form validation errors: Check that all required fields have validators and data is correctly passed to the form.
  2. Template rendering issues: Ensure your templates extend the correct base template and use the proper block structure.
  3. Object access errors: Verify that object names are correct and the core API is being used properly.
⚠️ **GitHub.com Fallback** ⚠️