BLT icon indicating copy to clipboard operation
BLT copied to clipboard

Create a private page on the organization that shows all slack apps installed and their commands

Open DonnieBLT opened this issue 9 months ago • 6 comments

DonnieBLT avatar Mar 08 '25 09:03 DonnieBLT

Do you mean that only an admin can access the private page?

igennova avatar Mar 11 '25 16:03 igennova

@DonnieBLT Can i get more information on this issue

nasredeenabdulhaleem avatar Mar 12 '25 09:03 nasredeenabdulhaleem

Yes only the org admin would be able to see this. This will help identify slack apps and their permissions for better securing the workspace.

DonnieBLT avatar Mar 12 '25 12:03 DonnieBLT

@DonnieBLT That is noted

nasredeenabdulhaleem avatar Mar 12 '25 12:03 nasredeenabdulhaleem

@coderabbit code the perfect solution for this

igennova avatar Mar 16 '25 15:03 igennova

[!TIP] For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Let's first understand the project structure and technologies
echo "=== Project Structure ==="
find . -maxdepth 2 -type d | sort

echo -e "\n=== Main Configuration Files ==="
find . -maxdepth 2 -name "*.py" | grep -i "settings\|urls\|config"

echo -e "\n=== Requirements ==="
find . -name "requirements.txt" -exec cat {} \;

echo -e "\n=== Find Authentication Related Files ==="
rg -l "auth|login|permission" --type py

Length of output: 3277


🏁 Script executed:

#!/bin/bash
# Let's examine the organization models and views
echo "=== Organization Model ==="
rg --type py -A 15 "class Organization" website/models.py

echo -e "\n=== Organization Views ==="
cat website/views/organization.py

echo -e "\n=== Authentication/Permission Mechanisms ==="
rg --type py "admin_required|is_admin" website/

echo -e "\n=== Template Structure ==="
find website/templates -type d | sort

Length of output: 67568


🏁 Script executed:

#!/bin/bash
# Look for Slack-related code
echo "=== Slack Related Code ==="
rg -l "slack" --type py

echo -e "\n=== Slack Handler View ==="
cat website/views/slack_handlers.py

echo -e "\n=== Looking for Slack App Models ==="
rg --type py "class.*Slack" website/models.py

Length of output: 66507


🏁 Script executed:

#!/bin/bash
# Let's find out the models related to Slack and their structure
echo "=== SlackIntegration Model ==="
rg --type py -A 15 "class SlackIntegration" website/models.py

echo -e "\n=== SlackBotActivity Model ==="
rg --type py -A 15 "class SlackBotActivity" website/models.py

echo -e "\n=== Organization Admin Access Methods ==="
rg --type py "is_admin|admin_required" website/

echo -e "\n=== Authentication Decorator for Views ==="
rg --type py "@login_required" website/

echo -e "\n=== Check URL Routing ==="
cat blt/urls.py

echo -e "\n=== Check Website URL Routing ==="
rg --type py "path\(" website/

Length of output: 50629


@igennova Here's the complete solution to create a private page showing all Slack apps installed and their commands, only accessible by organization admins:

1. Create the View

First, let's create a new view function in website/views/organization.py:

from slack_sdk.web import WebClient
from slack_sdk.errors import SlackApiError

@login_required(login_url="/accounts/login")
def organization_slack_apps(request, template="organization_slack_apps.html"):
    try:
        # Check if user is an organization admin
        organization_admin = OrganizationAdmin.objects.get(user=request.user)
        if not organization_admin.is_active:
            return HttpResponseRedirect("/")
            
        # Get the organization
        organization = organization_admin.organization
        
        # Check if organization has Slack integration
        try:
            slack_integration = SlackIntegration.objects.get(integration__organization=organization)
            workspace_token = slack_integration.bot_access_token
            
            if not workspace_token:
                return render(request, template, {
                    "error": "Slack integration not properly configured. Bot access token is missing.",
                    "organization": organization
                })
                
            # Initialize Slack client
            slack_client = WebClient(token=workspace_token)
            
            # Fetch installed apps
            apps_response = slack_client.apps_list()
            installed_apps = apps_response.get("apps", [])
            
            # For each app, fetch available commands if possible
            apps_with_commands = []
            for app in installed_apps:
                app_id = app.get("id")
                app_name = app.get("name")
                app_description = app.get("description", "")
                app_commands = []
                
                # Try to fetch commands for this app
                try:
                    commands_response = slack_client.apps_commands_list(app_id=app_id)
                    app_commands = commands_response.get("commands", [])
                except SlackApiError:
                    # Some apps might not expose commands or we might not have permission
                    pass
                
                apps_with_commands.append({
                    "id": app_id,
                    "name": app_name,
                    "description": app_description,
                    "commands": app_commands
                })
            
            context = {
                "organization": organization,
                "apps": apps_with_commands,
                "workspace_name": slack_integration.workspace_name or "Your Workspace"
            }
            return render(request, template, context)
            
        except SlackIntegration.DoesNotExist:
            return render(request, template, {
                "error": "This organization does not have Slack integration configured.",
                "organization": organization
            })
            
    except OrganizationAdmin.DoesNotExist:
        return HttpResponseRedirect("/")

2. Create the HTML Template

Create a new template file website/templates/organization_slack_apps.html:

{% extends "dashboard_base.html" %}
{% load static %}

{% block title %}Slack Apps - {{ organization.name }}{% endblock %}

{% block content %}
<div class="container py-4">
    <div class="row mb-4">
        <div class="col-12">
            <div class="d-flex justify-content-between align-items-center">
                <h1 class="h3">Slack Apps for {{ organization.name }}</h1>
                <a href="{% url 'organization_dashboard_home' %}" class="btn btn-outline-secondary">
                    <i class="fas fa-arrow-left"></i> Back to Dashboard
                </a>
            </div>
            <p class="text-muted">This page shows all Slack apps installed on your workspace and their available commands.</p>
        </div>
    </div>

    {% if error %}
    <div class="alert alert-warning">
        <i class="fas fa-exclamation-triangle"></i> {{ error }}
    </div>
    {% else %}
    <div class="row mb-4">
        <div class="col-12">
            <div class="card">
                <div class="card-header bg-light">
                    <h5 class="mb-0">{{ workspace_name }} - Installed Apps</h5>
                </div>
                <div class="card-body p-0">
                    {% if apps %}
                    <div class="accordion" id="appsAccordion">
                        {% for app in apps %}
                        <div class="accordion-item">
                            <h2 class="accordion-header" id="heading{{ forloop.counter }}">
                                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse{{ forloop.counter }}" aria-expanded="false" aria-controls="collapse{{ forloop.counter }}">
                                    <strong>{{ app.name }}</strong> 
                                    {% if app.commands %}<span class="badge bg-info ms-2">{{ app.commands|length }} command(s)</span>{% endif %}
                                </button>
                            </h2>
                            <div id="collapse{{ forloop.counter }}" class="accordion-collapse collapse" aria-labelledby="heading{{ forloop.counter }}" data-bs-parent="#appsAccordion">
                                <div class="accordion-body">
                                    <p>{{ app.description }}</p>
                                    
                                    {% if app.commands %}
                                    <h6>Available Commands:</h6>
                                    <div class="table-responsive">
                                        <table class="table table-sm table-striped">
                                            <thead>
                                                <tr>
                                                    <th>Command</th>
                                                    <th>Description</th>
                                                    <th>Usage</th>
                                                </tr>
                                            </thead>
                                            <tbody>
                                                {% for command in app.commands %}
                                                <tr>
                                                    <td><code>{{ command.command }}</code></td>
                                                    <td>{{ command.description }}</td>
                                                    <td>{% if command.usage %}<code>{{ command.usage }}</code>{% else %}<em>No usage info</em>{% endif %}</td>
                                                </tr>
                                                {% endfor %}
                                            </tbody>
                                        </table>
                                    </div>
                                    {% else %}
                                    <p class="text-muted">No commands available or accessible for this app.</p>
                                    {% endif %}
                                </div>
                            </div>
                        </div>
                        {% endfor %}
                    </div>
                    {% else %}
                    <div class="p-4 text-center">
                        <p>No Slack apps found in this workspace.</p>
                    </div>
                    {% endif %}
                </div>
            </div>
        </div>
    </div>
    {% endif %}
</div>
{% endblock %}

{% block extra_scripts %}
<script>
    // Add any additional JavaScript if needed
</script>
{% endblock %}

3. Add URL Route

Add this to blt/urls.py in the existing urlpatterns list:

path(
    "dashboard/organization/slack-apps/",
    organization_slack_apps,
    name="organization_slack_apps",
),

4. Add Menu Link in the Dashboard

Update the organization dashboard template to include a link to the new page. Add this to the appropriate sidebar or navigation section in website/templates/dashboard_base.html or your equivalent template:

{% if user.is_authenticated and user_is_organization_admin %}
<li class="nav-item">
    <a class="nav-link" href="{% url 'organization_slack_apps' %}">
        <i class="fab fa-slack"></i> Slack Apps
    </a>
</li>
{% endif %}

5. Update Requirements

Make sure the required packages are in your requirements.txt:

slack_sdk>=3.19.5

This solution leverages the existing authentication and organization structure while adding a dedicated page for viewing Slack app information. The page is protected by the login_required decorator and checks if the user is an organization admin, ensuring only authorized users can access this information.

The implementation fetches both the list of installed Slack apps and their available commands, displaying them in an organized, collapsible interface for easy navigation.

coderabbitai[bot] avatar Mar 16 '25 15:03 coderabbitai[bot]

Hi @DonnieBLT ! I’ve completed reviewing the requirement for this feature.

I’ve implemented a private organization-level page that lists:

  • All Slack apps installed in the workspace
  • Each app’s available commands
  • Proper permission checks for organization admins
  • Sidebar navigation entry under the organization dashboard
  • Clean and minimal templates following existing BLT patterns
  • Everything is ready and fully tested locally.

Could you please /assign this issue to me? I’ll open the PR immediately

Tia-ani avatar Nov 20 '25 07:11 Tia-ani

Hello @Tia-ani! You've been assigned to OWASP-BLT/BLT issue #3856. You have 24 hours to complete a pull request.


This comment was generated by OWASP BLT-Action

github-actions[bot] avatar Nov 20 '25 07:11 github-actions[bot]