Two factor authentication : TOTP #5
@ -11,4 +11,5 @@ body {
|
|||||||
|
|
||||||
.page_body {
|
.page_body {
|
||||||
padding: 3rem;
|
padding: 3rem;
|
||||||
|
overflow-y: scroll;
|
||||||
}
|
}
|
@ -60,6 +60,7 @@ pub struct UpdateUserQuery {
|
|||||||
admin: Option<String>,
|
admin: Option<String>,
|
||||||
grant_type: String,
|
grant_type: String,
|
||||||
granted_clients: String,
|
granted_clients: String,
|
||||||
|
two_factor: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn users_route(user: CurrentUser, users: web::Data<Addr<UsersActor>>, update_query: Option<web::Form<UpdateUserQuery>>) -> impl Responder {
|
pub async fn users_route(user: CurrentUser, users: web::Data<Addr<UsersActor>>, update_query: Option<web::Form<UpdateUserQuery>>) -> impl Responder {
|
||||||
@ -80,6 +81,11 @@ pub async fn users_route(user: CurrentUser, users: web::Data<Addr<UsersActor>>,
|
|||||||
user.enabled = update.0.enabled.is_some();
|
user.enabled = update.0.enabled.is_some();
|
||||||
user.admin = update.0.admin.is_some();
|
user.admin = update.0.admin.is_some();
|
||||||
|
|
||||||
|
if let Some(factors) = user.two_factor.as_mut() {
|
||||||
|
let factors_to_keep = update.0.two_factor.split(';').collect::<Vec<_>>();
|
||||||
|
factors.retain(|f| factors_to_keep.contains(&f.id.0.as_str()));
|
||||||
|
}
|
||||||
|
|
||||||
user.authorized_clients = match update.0.grant_type.as_str() {
|
user.authorized_clients = match update.0.grant_type.as_str() {
|
||||||
"all_clients" => None,
|
"all_clients" => None,
|
||||||
"custom_clients" => Some(update.0.granted_clients.split(',')
|
"custom_clients" => Some(update.0.granted_clients.split(',')
|
||||||
|
@ -41,7 +41,7 @@ pub struct User {
|
|||||||
pub admin: bool,
|
pub admin: bool,
|
||||||
|
|
||||||
/// 2FA
|
/// 2FA
|
||||||
pub second_factors: Option<Vec<SecondFactor>>,
|
pub two_factor: Option<Vec<SecondFactor>>,
|
||||||
|
|
||||||
/// None = all services
|
/// None = all services
|
||||||
/// Some([]) = no service
|
/// Some([]) = no service
|
||||||
@ -64,16 +64,20 @@ impl User {
|
|||||||
verify_password(pass, &self.password)
|
verify_password(pass, &self.password)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_factor(&mut self, factor: SecondFactor) {
|
pub fn has_two_factor(&self) -> bool {
|
||||||
if self.second_factors.is_none() {
|
self.two_factor.as_ref().map(|f| !f.is_empty()).unwrap_or(false)
|
||||||
self.second_factors = Some(vec![]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.second_factors.as_mut().unwrap().push(factor);
|
pub fn add_factor(&mut self, factor: SecondFactor) {
|
||||||
|
if self.two_factor.is_none() {
|
||||||
|
self.two_factor = Some(vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.two_factor.as_mut().unwrap().push(factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_factor(&mut self, factor_id: FactorID) {
|
pub fn remove_factor(&mut self, factor_id: FactorID) {
|
||||||
if let Some(f) = self.second_factors.as_mut() {
|
if let Some(f) = self.two_factor.as_mut() {
|
||||||
f.retain(|f| f.id != factor_id);
|
f.retain(|f| f.id != factor_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,7 +103,7 @@ impl Default for User {
|
|||||||
need_reset_password: false,
|
need_reset_password: false,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
admin: false,
|
admin: false,
|
||||||
second_factors: Some(vec![]),
|
two_factor: Some(vec![]),
|
||||||
authorized_clients: Some(Vec::new()),
|
authorized_clients: Some(Vec::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</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.as_deref().unwrap_or_default() %}
|
||||||
|
<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 }} ({{ f.type_str() }})
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</fieldset>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<!-- Granted clients -->
|
<!-- Granted clients -->
|
||||||
<fieldset class="form-group">
|
<fieldset class="form-group">
|
||||||
<legend class="mt-4">Granted clients</legend>
|
<legend class="mt-4">Granted clients</legend>
|
||||||
@ -161,11 +180,15 @@
|
|||||||
|
|
||||||
document.querySelector("input[name=granted_clients]").value = authorized_clients;
|
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();
|
form.submit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
@ -23,7 +23,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for f in user.second_factors.as_deref().unwrap_or_default() %}
|
{% for f in user.two_factor.as_deref().unwrap_or_default() %}
|
||||||
<tr id="factor-{{ f.id.0 }}">
|
<tr id="factor-{{ f.id.0 }}">
|
||||||
<td>{{ f.type_str() }}</td>
|
<td>{{ f.type_str() }}</td>
|
||||||
<td>{{ f.name }}</td>
|
<td>{{ f.name }}</td>
|
||||||
|
Loading…
Reference in New Issue
Block a user