File size: 6,133 Bytes
a05467c
 
 
 
76c3b0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a05467c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
"""
Simple Password Authentication for SPARKNET

Provides password-based access control for the Streamlit app.

SECURITY NOTES:
---------------
This module provides basic password authentication suitable for demos
and internal deployments. For production use, consider:

1. ENHANCED AUTHENTICATION:
   - Integrate with OAuth/OIDC (Google, Azure AD, Okta)
   - Use Streamlit's built-in OAuth support
   - Implement multi-factor authentication (MFA)

2. SESSION MANAGEMENT:
   - Configure session timeouts (default: browser session)
   - Implement session invalidation on logout
   - Consider IP-based session binding

3. PASSWORD SECURITY:
   - Use strong password requirements
   - Implement account lockout after failed attempts
   - Store passwords hashed with bcrypt (not SHA-256) for production

4. AUDIT LOGGING:
   - Log authentication attempts (success/failure)
   - Track user sessions
   - Monitor for suspicious activity

GDPR CONSIDERATIONS:
-------------------
- Authentication logs may contain personal data (usernames, IPs)
- Implement data retention policies for auth logs
- Support right-to-erasure for user accounts
- Document authentication processing in GDPR records

PRIVATE DEPLOYMENT:
------------------
For enterprise deployments:
- Integrate with existing identity providers
- Use LDAP/Active Directory for user management
- Implement role-based access control (RBAC)
- Enable single sign-on (SSO)

See SECURITY.md for comprehensive security documentation.
"""

import streamlit as st
import hashlib
import hmac
from typing import Optional


def hash_password(password: str) -> str:
    """Hash a password for secure storage."""
    return hashlib.sha256(password.encode()).hexdigest()


def check_password() -> bool:
    """
    Show login form and verify password.

    Returns True if password is correct, False otherwise.

    Configure password in Streamlit secrets:
        [auth]
        password = "your-secure-password"

    Or set multiple users:
        [auth]
        passwords = { admin = "admin123", user1 = "pass123" }
    """

    def password_entered():
        """Check if entered password is correct."""
        # Get password(s) from secrets
        if "auth" in st.secrets:
            # Check single password
            if "password" in st.secrets["auth"]:
                if hmac.compare_digest(
                    st.session_state["password"],
                    st.secrets["auth"]["password"]
                ):
                    st.session_state["authenticated"] = True
                    st.session_state["username"] = "user"
                    del st.session_state["password"]
                    return

            # Check multiple users
            if "passwords" in st.secrets["auth"]:
                username = st.session_state.get("username_input", "")
                password = st.session_state.get("password", "")

                passwords = st.secrets["auth"]["passwords"]
                if username in passwords and hmac.compare_digest(
                    password, passwords[username]
                ):
                    st.session_state["authenticated"] = True
                    st.session_state["username"] = username
                    del st.session_state["password"]
                    return

        st.session_state["authenticated"] = False
        st.session_state["password_incorrect"] = True

    # Check if already authenticated
    if st.session_state.get("authenticated", False):
        return True

    # Show login form
    st.markdown("""
    <style>
    .login-container {
        max-width: 400px;
        margin: 100px auto;
        padding: 40px;
        background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
        border-radius: 15px;
        box-shadow: 0 10px 40px rgba(0,0,0,0.3);
    }
    .login-title {
        text-align: center;
        color: #4ECDC4;
        font-size: 2em;
        margin-bottom: 10px;
    }
    .login-subtitle {
        text-align: center;
        color: #8b949e;
        margin-bottom: 30px;
    }
    </style>
    """, unsafe_allow_html=True)

    col1, col2, col3 = st.columns([1, 2, 1])

    with col2:
        st.markdown('<div class="login-title">🔥 SPARKNET</div>', unsafe_allow_html=True)
        st.markdown('<div class="login-subtitle">Document Intelligence Platform</div>', unsafe_allow_html=True)

        st.markdown("---")

        # Check if we have multi-user setup
        has_multi_user = (
            "auth" in st.secrets and
            "passwords" in st.secrets["auth"]
        )

        if has_multi_user:
            st.text_input(
                "Username",
                key="username_input",
                placeholder="Enter username"
            )

        st.text_input(
            "Password",
            type="password",
            key="password",
            placeholder="Enter password",
            on_change=password_entered
        )

        if st.button("🔐 Login", type="primary", use_container_width=True):
            password_entered()

        if st.session_state.get("password_incorrect", False):
            st.error("😕 Incorrect password. Please try again.")

        st.markdown("---")
        st.caption("Contact administrator for access credentials.")

    return False


def logout():
    """Log out the current user."""
    st.session_state["authenticated"] = False
    st.session_state["username"] = None
    st.rerun()


def get_current_user() -> Optional[str]:
    """Get the current logged-in username."""
    return st.session_state.get("username")


def require_auth(func):
    """Decorator to require authentication for a page."""
    def wrapper(*args, **kwargs):
        if check_password():
            return func(*args, **kwargs)
    return wrapper


def show_logout_button():
    """Show logout button in sidebar."""
    if st.session_state.get("authenticated", False):
        with st.sidebar:
            st.markdown("---")
            user = get_current_user()
            st.caption(f"Logged in as: **{user}**")
            if st.button("🚪 Logout", use_container_width=True):
                logout()