280 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			280 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| {% extends "base_settings_page.html" %}
 | |
| {% block content %}
 | |
| 
 | |
| <form method="post" action="/admin/users" id="edit_user_form">
 | |
|     <!-- User ID -->
 | |
|     <div class="form-group">
 | |
|         <label class="form-label mt-4" for="userID">User ID</label>
 | |
|         <input class="form-control" id="userID" type="text" readonly=""
 | |
|                name="uid" value="{{ u.uid.0 }}"/>
 | |
|     </div>
 | |
| 
 | |
|     <!-- User name -->
 | |
|     <div class="form-group">
 | |
|         <label class="form-label mt-4" for="username">Username</label>
 | |
|         <input class="form-control" id="username" type="text" autocomplete="nope"
 | |
|                name="username" value="{{ u.username }}" required/>
 | |
|         <div class="valid-feedback">This username is valid</div>
 | |
|         <div class="invalid-feedback">This username is already taken.</div>
 | |
|     </div>
 | |
| 
 | |
|     <!-- First name -->
 | |
|     <div class="form-group">
 | |
|         <label class="form-label mt-4" for="first_name">First name</label>
 | |
|         <input class="form-control" id="first_name" type="text"
 | |
|                name="first_name" value="{{ u.first_name }}"/>
 | |
|     </div>
 | |
| 
 | |
|     <!-- Last name -->
 | |
|     <div class="form-group">
 | |
|         <label class="form-label mt-4" for="last_name">Last name</label>
 | |
|         <input class="form-control" id="last_name" type="text"
 | |
|                name="last_name" value="{{ u.last_name }}"/>
 | |
|     </div>
 | |
| 
 | |
|     <!-- Email -->
 | |
|     <div class="form-group">
 | |
|         <label class="form-label mt-4" for="email">Email address</label>
 | |
|         <input class="form-control" id="email" type="email"
 | |
|                name="email" value="{{ u.email }}"/>
 | |
|     </div>
 | |
| 
 | |
|     <div class="form-group mt-4">
 | |
|         <!-- Generate new password -->
 | |
|         <div class="form-check">
 | |
|             <input class="form-check-input" type="checkbox" name="gen_new_password" id="gen_new_password" {% if
 | |
|                    u.password.is_empty() %} checked="" {% endif %}>
 | |
|             <label class="form-check-label" for="gen_new_password">
 | |
|                 Generate a new temporary password
 | |
|             </label>
 | |
|         </div>
 | |
| 
 | |
|         <!-- Enabled -->
 | |
|         <div class="form-check">
 | |
|             <input class="form-check-input" type="checkbox" name="enabled" id="enabled"
 | |
|                    {% if u.enabled %} checked="" {% endif %}>
 | |
|             <label class="form-check-label" for="enabled">
 | |
|                 Enabled
 | |
|             </label>
 | |
|         </div>
 | |
| 
 | |
|         <!-- 2FA exemption after successful login -->
 | |
|         <div class="form-check">
 | |
|             <input class="form-check-input" type="checkbox" name="two_factor_exemption_after_successful_login"
 | |
|                    id="two_factor_exemption_after_successful_login"
 | |
|                    {% if u.two_factor_exemption_after_successful_login %} checked="" {% endif %}>
 | |
|             <label class="form-check-label" for="two_factor_exemption_after_successful_login">
 | |
|                 Exempt user from 2FA authentication for an IP address after a successful login for a limited time
 | |
|             </label>
 | |
|         </div>
 | |
| 
 | |
|         <!-- Admin -->
 | |
|         <div class="form-check">
 | |
|             <input class="form-check-input" type="checkbox" name="admin" id="admin"
 | |
|                    {% if u.admin %} checked="" {% endif %}>
 | |
|             <label class="form-check-label" for="admin">
 | |
|                 Grant admin privileges
 | |
|             </label>
 | |
|         </div>
 | |
|     </div>
 | |
| 
 | |
|     <!-- Two-Factor authentication -->
 | |
|     <input type="hidden" name="two_factor" value=""/>
 | |
|     {% if u.has_two_factor() %}
 | |
|     <fieldset class="form-group">
 | |
|         <legend class="mt-4">Two factor authentication</legend>
 | |
|         <strong>If you uncheck a factor, it will be DELETED</strong>
 | |
|         {% for f in u.two_factor %}
 | |
|         <div class="form-check">
 | |
|             <label class="form-check-label">
 | |
|                 <input type="checkbox" class="form-check-input two-fact-checkbox"
 | |
|                        value="{{ f.id.0 }}"
 | |
|                        checked=""/>
 | |
|                 {{ f.name }} (<img src="{{ f.type_image() }}" alt="Factor icon" style="height:1em;"/>
 | |
|                 {{ f.type_str() }})
 | |
|             </label>
 | |
|         </div>
 | |
|         {% endfor %}
 | |
|     </fieldset>
 | |
|     {% endif %}
 | |
| 
 | |
|     <!-- Two factor authentication history -->
 | |
|     {% if !u.last_successful_2fa.is_empty() %}
 | |
|     <fieldset class="form-group">
 | |
|         <legend class="mt-4">Last successful 2FA authentications</legend>
 | |
| 
 | |
|         <!-- Clear 2FA history -->
 | |
|         <div class="form-check">
 | |
|             <input class="form-check-input" type="checkbox" name="clear_2fa_history" id="clear_2fa_history">
 | |
|             <label class="form-check-label" for="clear_2fa_history">
 | |
|                 Clear 2FA authentication history
 | |
|             </label>
 | |
|         </div>
 | |
| 
 | |
|         <ul>
 | |
|             {% for e in u.get_formatted_2fa_successful_logins() %}
 | |
|             {% if e.can_bypass_2fa %}
 | |
|             <li style="font-weight: bold;">{{ e.ip }} - {{ e.fmt_time() }} - BYPASS 2FA</li>
 | |
|             {% else %}
 | |
|             <li>{{ e.ip }} - {{ e.fmt_time() }}</li>
 | |
|             {% endif %}
 | |
|             {% endfor %}
 | |
|         </ul>
 | |
|     </fieldset>
 | |
|     {% endif %}
 | |
| 
 | |
|     <!-- Authorized authentication sources -->
 | |
|     <fieldset class="form-group">
 | |
|         <legend class="mt-4">Authorized authentication sources</legend>
 | |
| 
 | |
|         <!-- Local login -->
 | |
|         <div class="form-check">
 | |
|             <input class="form-check-input" type="checkbox" name="allow_local_login" id="allow_local_login"
 | |
|                    {% if u.allow_local_login %} checked="" {% endif %}>
 | |
|             <label class="form-check-label" for="allow_local_login">
 | |
|                 Allow local login
 | |
|             </label>
 | |
|         </div>
 | |
| 
 | |
|         <!-- Upstream providers -->
 | |
|         <input type="hidden" name="authorized_sources" id="authorized_sources"/>
 | |
|         {% for prov in providers %}
 | |
|         <div class="form-check">
 | |
|             <input class="form-check-input authorized_provider" type="checkbox" name="prov-{{ prov.id.0 }}"
 | |
|                    id="prov-{{ prov.id.0 }}"
 | |
|                    data-id="{{ prov.id.0 }}"
 | |
|                    {% if u.can_login_from_provider(prov) %} checked="" {% endif %}>
 | |
|             <label class="form-check-label" for="prov-{{ prov.id.0 }}">
 | |
|                 Allow login from {{ prov.name }}
 | |
|             </label>
 | |
|         </div>
 | |
|         {% endfor %}
 | |
|     </fieldset>
 | |
| 
 | |
|     <!-- Granted clients -->
 | |
|     <fieldset class="form-group">
 | |
|         <legend class="mt-4">Granted clients</legend>
 | |
|         <div class="form-check">
 | |
|             <label class="form-check-label">
 | |
|                 <input type="radio" class="form-check-input" name="grant_type"
 | |
|                        value="all_clients" {% if u.granted_clients()== GrantedClients::AllClients %} checked="" {% endif
 | |
|                        %}>
 | |
|                 Grant all clients
 | |
|             </label>
 | |
|         </div>
 | |
|         <div class="form-check">
 | |
|             <label class="form-check-label">
 | |
|                 <input type="radio" class="form-check-input" name="grant_type"
 | |
|                        value="custom_clients" {% if matches!(self.u.granted_clients(), GrantedClients::SomeClients(_))
 | |
|                        %} checked="checked" {% endif %}>
 | |
|                 Manually specify allowed clients
 | |
|             </label>
 | |
|         </div>
 | |
| 
 | |
|         <div id="clients_target">
 | |
|             <input type="hidden" name="granted_clients" value=""/>
 | |
|             {% for c in clients %}
 | |
|             <div class="form-check">
 | |
|                 <input id="client-{{ c.id.0 }}" class="form-check-input authorize_client_checkbox" type="checkbox"
 | |
|                        data-id="{{ c.id.0 }}"
 | |
|                        {% if u.can_access_app(c) %} checked="" {% endif %}>
 | |
|                 <label class="form-check-label" for="client-{{ c.id.0 }}">
 | |
|                     {{ c.name }}
 | |
|                 </label>
 | |
|             </div>
 | |
|             {% endfor %}
 | |
|         </div>
 | |
| 
 | |
|         <div class="form-check">
 | |
|             <label class="form-check-label">
 | |
|                 <input type="radio" class="form-check-input" name="grant_type"
 | |
|                        value="no_client" {% if u.granted_clients()== GrantedClients::NoClient %} checked="checked" {%
 | |
|                        endif %}>
 | |
|                 Do not grant any client
 | |
|             </label>
 | |
|         </div>
 | |
|     </fieldset>
 | |
| 
 | |
|     <input type="submit" class="btn btn-primary mt-4" value="{{ p.page_title }}">
 | |
| </form>
 | |
| 
 | |
| <script>
 | |
|     // Check Username
 | |
|     async function find_username(username) {
 | |
|         let data = new URLSearchParams();
 | |
|         data.append("username", username);
 | |
| 
 | |
|         return (await(await fetch("/admin/api/find_username", {
 | |
|             body: data,
 | |
|             method: "POST",
 | |
|         })).json()).user_id
 | |
|     }
 | |
| 
 | |
|     const usernameEl = document.getElementById("username")
 | |
|     async function check_username() {
 | |
|          try {
 | |
|                 usernameEl.classList.remove("is-invalid");
 | |
|                 usernameEl.classList.remove("is-valid");
 | |
| 
 | |
|                 if (usernameEl.value === "")
 | |
|                     return;
 | |
| 
 | |
|                 const userID = await find_username(usernameEl.value);
 | |
|                 usernameEl.classList.add((userID === null || userID === "{{ u.uid.0 }}") ? "is-valid" : "is-invalid");
 | |
| 
 | |
|             } catch(e) {
 | |
|                 console.error(e);
 | |
|             }
 | |
|     }
 | |
| 
 | |
|     check_username();
 | |
|     usernameEl.addEventListener("change", check_username);
 | |
|     usernameEl.addEventListener("keyup", check_username);
 | |
| 
 | |
| 
 | |
|     // Clients granted
 | |
|     function refreshDisplayAuthorizedClients() {
 | |
|         const clientsSelectorEl = document.getElementById("clients_target");
 | |
|         const radioBtn = document.querySelector("input[name=grant_type][value=custom_clients]");
 | |
|         clientsSelectorEl.style.display = radioBtn.checked ? "block" : "none";
 | |
|     }
 | |
|     refreshDisplayAuthorizedClients();
 | |
|     document.querySelectorAll("input[name=grant_type]").forEach(el=> {
 | |
|         el.addEventListener("change", refreshDisplayAuthorizedClients)
 | |
|     })
 | |
| 
 | |
| 
 | |
|     // Handle submitted form
 | |
|     const form = document.getElementById("edit_user_form");
 | |
|     form.addEventListener("submit", (ev) => {
 | |
|         ev.preventDefault();
 | |
| 
 | |
|         const authorized_sources = [...document.querySelectorAll(".authorized_provider")]
 | |
|             .filter(e => e.checked)
 | |
|             .map(e => e.getAttribute("data-id")).join(",")
 | |
| 
 | |
|         document.querySelector("input[name=authorized_sources]").value = authorized_sources;
 | |
| 
 | |
| 
 | |
|         const authorized_clients = [...document.querySelectorAll(".authorize_client_checkbox")]
 | |
|             .filter(e => e.checked)
 | |
|             .map(e => e.getAttribute("data-id")).join(",")
 | |
| 
 | |
|         document.querySelector("input[name=granted_clients]").value = authorized_clients;
 | |
| 
 | |
|         const factors_to_keep = [...document.querySelectorAll(".two-fact-checkbox")]
 | |
|             .filter(e => e.checked)
 | |
|             .map(e => e.value)
 | |
|             .join(";")
 | |
| 
 | |
|         document.querySelector("input[name=two_factor]").value = factors_to_keep;
 | |
| 
 | |
|         form.submit();
 | |
|     });
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| </script>
 | |
| 
 | |
| {% endblock content %} |