feat: allow setting password and login type from superadmin UI
This commit is contained in:
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT flow_status AS \"_id!: Json<Box<RawValue>>\" FROM queue WHERE id = $1 AND workspace_id = $2 LIMIT 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "_id!: Json<Box<RawValue>>",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "0f04f42a596b6759a84ba7d26ea76f06c6077c619c0ab1ac4edf633b22df98c3"
|
||||
}
|
||||
15
backend/.sqlx/query-349396e8fdd96d45875110bc06767e6eb876792ec8f83e9b03c2fb46bb12e0b9.json
generated
Normal file
15
backend/.sqlx/query-349396e8fdd96d45875110bc06767e6eb876792ec8f83e9b03c2fb46bb12e0b9.json
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE password SET login_type = $1 WHERE email = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "349396e8fdd96d45875110bc06767e6eb876792ec8f83e9b03c2fb46bb12e0b9"
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT raw_flow->'modules'->($1)->'value'->>'type' = 'flow' FROM queue WHERE id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "3e539fef054ad31bc1736e27276087775a721a6ee7ae35b03fd4ce3563ea3838"
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT raw_flow->'modules'->($1)::int AS \"_id: Json<Box<RawValue>>\" FROM queue WHERE id = $2 AND workspace_id = $3 LIMIT 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "_id: Json<Box<RawValue>>",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4",
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "cd40b9c552d76664a552457c4a9610c021a0a5b63ffe5afd7878385c8af6bc6c"
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
e3df64af864f9d3546a97ff21f719c553e34ff1c
|
||||
0d9c8813acd28848515c736e7b684220b5a785a3
|
||||
@@ -283,6 +283,71 @@ paths:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/users/set_password_of/{user}:
|
||||
post:
|
||||
summary: set password for a specific user (require super admin)
|
||||
operationId: setPasswordForUser
|
||||
tags:
|
||||
- user
|
||||
parameters:
|
||||
- name: user
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
description: set password
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
password:
|
||||
type: string
|
||||
required:
|
||||
- password
|
||||
responses:
|
||||
"200":
|
||||
description: password set
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/users/set_login_type/{user}:
|
||||
post:
|
||||
summary: set login type for a specific user (require super admin)
|
||||
operationId: setLoginTypeForUser
|
||||
tags:
|
||||
- user
|
||||
parameters:
|
||||
- name: user
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
description: set login type
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
login_type:
|
||||
type: string
|
||||
required:
|
||||
- login_type
|
||||
responses:
|
||||
"200":
|
||||
description: login type set
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
|
||||
/users/create:
|
||||
post:
|
||||
|
||||
@@ -90,6 +90,9 @@ pub fn global_service() -> Router {
|
||||
.route("/accept_invite", post(accept_invite))
|
||||
.route("/list_as_super_admin", get(list_users_as_super_admin))
|
||||
.route("/setpassword", post(set_password))
|
||||
.route("/set_password_of/:user", post(set_password_of_user))
|
||||
.route("/set_login_type/:user", post(set_login_type))
|
||||
|
||||
.route("/create", post(create_user))
|
||||
.route("/update/:user", post(update_user))
|
||||
.route("/delete/:user", delete(delete_user))
|
||||
@@ -866,6 +869,12 @@ pub struct EditPassword {
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EditLoginType {
|
||||
pub login_type: String,
|
||||
}
|
||||
|
||||
|
||||
#[derive(FromRow, Serialize)]
|
||||
pub struct TruncatedToken {
|
||||
pub label: Option<String>,
|
||||
@@ -2028,7 +2037,52 @@ async fn set_password(
|
||||
authed: ApiAuthed,
|
||||
Json(ep): Json<EditPassword>,
|
||||
) -> Result<String> {
|
||||
crate::users_ee::set_password(db, argon2, authed, ep).await
|
||||
let email = authed.email.clone();
|
||||
crate::users_ee::set_password(db, argon2, authed, &email, ep).await
|
||||
}
|
||||
|
||||
async fn set_password_of_user(
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(argon2): Extension<Arc<Argon2<'_>>>,
|
||||
Path(email): Path<String>,
|
||||
authed: ApiAuthed,
|
||||
Json(ep): Json<EditPassword>,
|
||||
) -> Result<String> {
|
||||
require_super_admin(&db, &authed.email).await?;
|
||||
crate::users_ee::set_password(db, argon2, authed, &email, ep).await
|
||||
}
|
||||
|
||||
async fn set_login_type(
|
||||
Extension(db): Extension<DB>,
|
||||
Path(email): Path<String>,
|
||||
authed: ApiAuthed,
|
||||
Json(et): Json<EditLoginType>,
|
||||
) -> Result<String> {
|
||||
require_super_admin(&db, &authed.email).await?;
|
||||
let mut tx = db.begin().await?;
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE password SET login_type = $1 WHERE email = $2",
|
||||
et.login_type,
|
||||
email
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
audit_log(
|
||||
&mut *tx,
|
||||
&authed,
|
||||
"users.set_login_type",
|
||||
ActionKind::Update,
|
||||
"global",
|
||||
Some(&email),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
tx.commit().await?;
|
||||
Ok(format!("login type of {} updated to {}", email, et.login_type))
|
||||
|
||||
}
|
||||
|
||||
async fn login(
|
||||
|
||||
@@ -27,6 +27,7 @@ pub async fn set_password(
|
||||
_db: DB,
|
||||
_argon2: Arc<Argon2<'_>>,
|
||||
_authed: ApiAuthed,
|
||||
_user_email: &str,
|
||||
_ep: EditPassword,
|
||||
) -> Result<String> {
|
||||
Err(Error::InternalErr(
|
||||
|
||||
@@ -72,11 +72,13 @@
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col max-w-2xl p-2">
|
||||
{#if isConflict}
|
||||
<span class="text-sm mb-2 leading-6 font-semibold"
|
||||
>{isConflict ? 'Fix username conflict' : 'Change username'}</span
|
||||
>Fix username conflict</span
|
||||
>
|
||||
{/if}
|
||||
|
||||
<span class="text-xs mb-1 leading-6"
|
||||
<span class="text-sm font-semibold mb-1 leading-6"
|
||||
>{isConflict ? 'Auto-generated instance username' : 'New username'}</span
|
||||
>
|
||||
<input
|
||||
|
||||
@@ -5,17 +5,36 @@
|
||||
import Popup from './common/popup/Popup.svelte'
|
||||
import { offset, flip, shift } from 'svelte-floating-ui/dom'
|
||||
import ChangeInstanceUsernameInner from './ChangeInstanceUsernameInner.svelte'
|
||||
import { UserService } from '$lib/gen'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
|
||||
export let value: string | undefined
|
||||
export let email: string
|
||||
export let username: string | undefined = undefined
|
||||
export let automateUsernameCreation: boolean = false
|
||||
export let login_type: string
|
||||
|
||||
let password: string = ''
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function save() {
|
||||
function saveName() {
|
||||
dispatch('save', value)
|
||||
}
|
||||
async function savePassword() {
|
||||
if (password.length < 5) {
|
||||
sendUserToast('Password must be at least 5 characters long', true)
|
||||
return
|
||||
}
|
||||
await UserService.setPasswordForUser({user: email, requestBody:{ password }})
|
||||
sendUserToast(`Password updated for ${email}`)
|
||||
}
|
||||
|
||||
async function saveLoginType() {
|
||||
await UserService.setLoginTypeForUser({user: email, requestBody:{ login_type }})
|
||||
sendUserToast(`Login type updated for ${email}`)
|
||||
dispatch('refresh')
|
||||
}
|
||||
</script>
|
||||
|
||||
<Popup
|
||||
@@ -34,7 +53,7 @@
|
||||
<ChangeInstanceUsernameInner {email} {username} on:renamed />
|
||||
{/if}
|
||||
<label class="block text-primary">
|
||||
<div class="pb-1 text-xs text-secondary">Name</div>
|
||||
<div class="pb-2 text-sm font-semibold text-primary">Name</div>
|
||||
<div class="flex w-full">
|
||||
<input
|
||||
type="text"
|
||||
@@ -44,7 +63,7 @@
|
||||
on:keydown|stopPropagation
|
||||
on:keypress|stopPropagation={({ key }) => {
|
||||
if (key === 'Enter') {
|
||||
save()
|
||||
saveName()
|
||||
close(null)
|
||||
}
|
||||
}}
|
||||
@@ -57,12 +76,78 @@
|
||||
btnClasses="mt-2 "
|
||||
aria-label="Save ID"
|
||||
on:click={() => {
|
||||
save()
|
||||
saveName()
|
||||
close(null)
|
||||
}}
|
||||
>
|
||||
Update name
|
||||
</Button>
|
||||
</label>
|
||||
<label class="block text-primary">
|
||||
<div class="pb-2 text-sm font-semibold text-primary">Password</div>
|
||||
<div class="flex w-full">
|
||||
<input
|
||||
type="password"
|
||||
bind:value={password}
|
||||
class="!w-auto grow"
|
||||
on:click|stopPropagation={() => {}}
|
||||
on:keydown|stopPropagation
|
||||
on:keypress|stopPropagation={({ key }) => {
|
||||
if (key === 'Enter') {
|
||||
savePassword()
|
||||
close(null)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
size="xs"
|
||||
color="blue"
|
||||
buttonType="button"
|
||||
btnClasses="mt-2 "
|
||||
aria-label="Save ID"
|
||||
on:click={() => {
|
||||
savePassword()
|
||||
close(null)
|
||||
}}
|
||||
>
|
||||
Update password
|
||||
</Button>
|
||||
</label>
|
||||
<label class="block text-primary">
|
||||
<div class="pb-2 text-sm font-semibold text-primary">Login type</div>
|
||||
<div class="text-xs text-secondary mb-2">
|
||||
Must match exact SSO name, "password" or "saml". Examples: password, google, saml, microsoft
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
<input
|
||||
type="text"
|
||||
bind:value={login_type}
|
||||
class="!w-auto grow"
|
||||
on:click|stopPropagation={() => {}}
|
||||
on:keydown|stopPropagation
|
||||
on:keypress|stopPropagation={({ key }) => {
|
||||
if (key === 'Enter') {
|
||||
saveLoginType()
|
||||
close(null)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
size="xs"
|
||||
color="blue"
|
||||
buttonType="button"
|
||||
btnClasses="mt-2 "
|
||||
aria-label="Save login type"
|
||||
on:click={() => {
|
||||
saveLoginType()
|
||||
close(null)
|
||||
}}
|
||||
>
|
||||
Update login type
|
||||
</Button>
|
||||
</label>
|
||||
</div>
|
||||
</Popup>
|
||||
|
||||
@@ -276,9 +276,13 @@
|
||||
<td>
|
||||
<div class="flex flex-row gap-x-1 justify-end">
|
||||
<InstanceNameEditor
|
||||
login_type={login_type}
|
||||
value={name}
|
||||
{username}
|
||||
{email}
|
||||
on:refresh={() => {
|
||||
listUsers(activeOnly)
|
||||
}}
|
||||
on:save={(e) => {
|
||||
updateName(e.detail, email)
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user