Initial commit
This commit is contained in:
commit
0c4e7eb9ab
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
.env
|
2759
Cargo.lock
generated
Normal file
2759
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
24
Cargo.toml
Normal file
24
Cargo.toml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
[package]
|
||||||
|
name = "lldap-controller"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = ["queries"]
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
|
cynic = "3.10.0"
|
||||||
|
insta = "1.42.2"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
queries = { path = "./queries" }
|
||||||
|
anyhow = "1.0.97"
|
||||||
|
lldap_auth = "0.3.0"
|
||||||
|
rand = { version = "0.8.0" }
|
||||||
|
serde_json = "1.0.140"
|
||||||
|
surf = "2.3.2"
|
||||||
|
cynic = { workspace = true, features = ["http-surf"] }
|
||||||
|
tokio = { version = "1.44.0", features = ["full"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
insta = { workspace = true }
|
13
queries/Cargo.toml
Normal file
13
queries/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "queries"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cynic = { workspace = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
insta = { workspace = true }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
cynic-codegen = "3.10.0"
|
7
queries/build.rs
Normal file
7
queries/build.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
fn main() {
|
||||||
|
cynic_codegen::register_schema("lldap")
|
||||||
|
.from_sdl_file("schemas/lldap.graphql")
|
||||||
|
.unwrap()
|
||||||
|
.as_default()
|
||||||
|
.unwrap();
|
||||||
|
}
|
199
queries/schemas/lldap.graphql
Normal file
199
queries/schemas/lldap.graphql
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
type AttributeValue {
|
||||||
|
name: String!
|
||||||
|
value: [String!]!
|
||||||
|
schema: AttributeSchema!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Mutation {
|
||||||
|
createUser(user: CreateUserInput!): User!
|
||||||
|
createGroup(name: String!): Group!
|
||||||
|
createGroupWithDetails(request: CreateGroupInput!): Group!
|
||||||
|
updateUser(user: UpdateUserInput!): Success!
|
||||||
|
updateGroup(group: UpdateGroupInput!): Success!
|
||||||
|
addUserToGroup(userId: String!, groupId: Int!): Success!
|
||||||
|
removeUserFromGroup(userId: String!, groupId: Int!): Success!
|
||||||
|
deleteUser(userId: String!): Success!
|
||||||
|
deleteGroup(groupId: Int!): Success!
|
||||||
|
addUserAttribute(
|
||||||
|
name: String!
|
||||||
|
attributeType: AttributeType!
|
||||||
|
isList: Boolean!
|
||||||
|
isVisible: Boolean!
|
||||||
|
isEditable: Boolean!
|
||||||
|
): Success!
|
||||||
|
addGroupAttribute(
|
||||||
|
name: String!
|
||||||
|
attributeType: AttributeType!
|
||||||
|
isList: Boolean!
|
||||||
|
isVisible: Boolean!
|
||||||
|
isEditable: Boolean!
|
||||||
|
): Success!
|
||||||
|
deleteUserAttribute(name: String!): Success!
|
||||||
|
deleteGroupAttribute(name: String!): Success!
|
||||||
|
addUserObjectClass(name: String!): Success!
|
||||||
|
addGroupObjectClass(name: String!): Success!
|
||||||
|
deleteUserObjectClass(name: String!): Success!
|
||||||
|
deleteGroupObjectClass(name: String!): Success!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Group {
|
||||||
|
id: Int!
|
||||||
|
displayName: String!
|
||||||
|
creationDate: DateTimeUtc!
|
||||||
|
uuid: String!
|
||||||
|
|
||||||
|
# User-defined attributes.
|
||||||
|
attributes: [AttributeValue!]!
|
||||||
|
|
||||||
|
# The groups to which this user belongs.
|
||||||
|
users: [User!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
# A filter for requests, specifying a boolean expression based on field constraints. Only one of
|
||||||
|
# the fields can be set at a time.
|
||||||
|
input RequestFilter {
|
||||||
|
any: [RequestFilter!]
|
||||||
|
all: [RequestFilter!]
|
||||||
|
not: RequestFilter
|
||||||
|
eq: EqualityConstraint
|
||||||
|
memberOf: String
|
||||||
|
memberOfId: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
# DateTime
|
||||||
|
scalar DateTimeUtc
|
||||||
|
|
||||||
|
type Query {
|
||||||
|
apiVersion: String!
|
||||||
|
user(userId: String!): User!
|
||||||
|
users(filters: RequestFilter): [User!]!
|
||||||
|
groups: [Group!]!
|
||||||
|
group(groupId: Int!): Group!
|
||||||
|
schema: Schema!
|
||||||
|
}
|
||||||
|
|
||||||
|
# The details required to create a user.
|
||||||
|
input CreateUserInput {
|
||||||
|
id: String!
|
||||||
|
email: String
|
||||||
|
displayName: String
|
||||||
|
firstName: String
|
||||||
|
lastName: String
|
||||||
|
|
||||||
|
# Base64 encoded JpegPhoto.
|
||||||
|
avatar: String
|
||||||
|
|
||||||
|
# User-defined attributes.
|
||||||
|
attributes: [AttributeValueInput!]
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttributeSchema {
|
||||||
|
name: String!
|
||||||
|
attributeType: AttributeType!
|
||||||
|
isList: Boolean!
|
||||||
|
isVisible: Boolean!
|
||||||
|
isEditable: Boolean!
|
||||||
|
isHardcoded: Boolean!
|
||||||
|
isReadonly: Boolean!
|
||||||
|
}
|
||||||
|
|
||||||
|
# The fields that can be updated for a user.
|
||||||
|
input UpdateUserInput {
|
||||||
|
id: String!
|
||||||
|
email: String
|
||||||
|
displayName: String
|
||||||
|
firstName: String
|
||||||
|
lastName: String
|
||||||
|
|
||||||
|
# Base64 encoded JpegPhoto.
|
||||||
|
avatar: String
|
||||||
|
|
||||||
|
# Attribute names to remove.
|
||||||
|
# They are processed before insertions.
|
||||||
|
removeAttributes: [String!]
|
||||||
|
|
||||||
|
# Inserts or updates the given attributes.
|
||||||
|
# For lists, the entire list must be provided.
|
||||||
|
insertAttributes: [AttributeValueInput!]
|
||||||
|
}
|
||||||
|
|
||||||
|
input EqualityConstraint {
|
||||||
|
field: String!
|
||||||
|
value: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Schema {
|
||||||
|
userSchema: AttributeList!
|
||||||
|
groupSchema: AttributeList!
|
||||||
|
}
|
||||||
|
|
||||||
|
# The fields that can be updated for a group.
|
||||||
|
input UpdateGroupInput {
|
||||||
|
# The group ID.
|
||||||
|
id: Int!
|
||||||
|
|
||||||
|
# The new display name.
|
||||||
|
displayName: String
|
||||||
|
|
||||||
|
# Attribute names to remove.
|
||||||
|
# They are processed before insertions.
|
||||||
|
removeAttributes: [String!]
|
||||||
|
|
||||||
|
# Inserts or updates the given attributes.
|
||||||
|
# For lists, the entire list must be provided.
|
||||||
|
insertAttributes: [AttributeValueInput!]
|
||||||
|
}
|
||||||
|
|
||||||
|
input AttributeValueInput {
|
||||||
|
# The name of the attribute. It must be present in the schema, and the type informs how
|
||||||
|
# to interpret the values.
|
||||||
|
name: String!
|
||||||
|
|
||||||
|
# The values of the attribute.
|
||||||
|
# If the attribute is not a list, the vector must contain exactly one element.
|
||||||
|
# Integers (signed 64 bits) are represented as strings.
|
||||||
|
# Dates are represented as strings in RFC3339 format, e.g. "2019-10-12T07:20:50.52Z".
|
||||||
|
# JpegPhotos are represented as base64 encoded strings. They must be valid JPEGs.
|
||||||
|
value: [String!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
# The details required to create a group.
|
||||||
|
input CreateGroupInput {
|
||||||
|
displayName: String!
|
||||||
|
|
||||||
|
# User-defined attributes.
|
||||||
|
attributes: [AttributeValueInput!]
|
||||||
|
}
|
||||||
|
|
||||||
|
type User {
|
||||||
|
id: String!
|
||||||
|
email: String!
|
||||||
|
displayName: String!
|
||||||
|
firstName: String!
|
||||||
|
lastName: String!
|
||||||
|
avatar: String
|
||||||
|
creationDate: DateTimeUtc!
|
||||||
|
uuid: String!
|
||||||
|
|
||||||
|
# User-defined attributes.
|
||||||
|
attributes: [AttributeValue!]!
|
||||||
|
|
||||||
|
# The groups to which this user belongs.
|
||||||
|
groups: [Group!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AttributeType {
|
||||||
|
STRING
|
||||||
|
INTEGER
|
||||||
|
JPEG_PHOTO
|
||||||
|
DATE_TIME
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttributeList {
|
||||||
|
attributes: [AttributeSchema!]!
|
||||||
|
extraLdapObjectClasses: [String!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Success {
|
||||||
|
ok: Boolean!
|
||||||
|
}
|
154
queries/src/lib.rs
Normal file
154
queries/src/lib.rs
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
#[cynic::schema("lldap")]
|
||||||
|
pub(crate) mod schema {}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
#[cynic(graphql_type = "Query")]
|
||||||
|
pub struct GetUserAttributes {
|
||||||
|
pub schema: Schema,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
pub struct Schema {
|
||||||
|
pub user_schema: AttributeList,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
pub struct AttributeList {
|
||||||
|
pub attributes: Vec<AttributeSchema>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
pub struct AttributeSchema {
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
#[cynic(graphql_type = "Mutation")]
|
||||||
|
pub struct CreateManagedUserAttribute {
|
||||||
|
#[arguments(attributeType: "INTEGER", isEditable: false, isList: false, isVisible: false, name: "managed")]
|
||||||
|
pub add_user_attribute: Success,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
pub struct Success {
|
||||||
|
pub ok: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::Enum, Clone, Copy, Debug)]
|
||||||
|
pub enum AttributeType {
|
||||||
|
String,
|
||||||
|
Integer,
|
||||||
|
JpegPhoto,
|
||||||
|
DateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
#[cynic(graphql_type = "Query")]
|
||||||
|
pub struct ListManagedUsers {
|
||||||
|
#[arguments(filters: { eq: { field: "managed", value: "1" } })]
|
||||||
|
pub users: Vec<User>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
pub struct User {
|
||||||
|
pub id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryVariables, Debug)]
|
||||||
|
pub struct DeleteUserVariables<'a> {
|
||||||
|
pub id: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
#[cynic(graphql_type = "Mutation", variables = "DeleteUserVariables")]
|
||||||
|
pub struct DeleteUser {
|
||||||
|
#[arguments(userId: $id)]
|
||||||
|
pub delete_user: Success,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryVariables, Debug)]
|
||||||
|
pub struct CreateUserVariables<'a> {
|
||||||
|
pub id: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
#[cynic(graphql_type = "Mutation", variables = "CreateUserVariables")]
|
||||||
|
pub struct CreateUser {
|
||||||
|
#[arguments(user: { attributes: { name: "managed", value: "1" }, email: $id, id: $id })]
|
||||||
|
pub create_user: User,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryVariables, Debug)]
|
||||||
|
pub struct AddUserToGroupVariables<'a> {
|
||||||
|
pub group: i32,
|
||||||
|
pub id: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
#[cynic(graphql_type = "Mutation", variables = "AddUserToGroupVariables")]
|
||||||
|
pub struct AddUserToGroup {
|
||||||
|
#[arguments(groupId: $group, userId: $id)]
|
||||||
|
pub add_user_to_group: Success,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_user_attributes_gql_output() {
|
||||||
|
use cynic::QueryBuilder;
|
||||||
|
|
||||||
|
let operation = GetUserAttributes::build(());
|
||||||
|
|
||||||
|
insta::assert_snapshot!(operation.query);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_managed_user_attribute_gql_output() {
|
||||||
|
use cynic::MutationBuilder;
|
||||||
|
|
||||||
|
let operation = CreateManagedUserAttribute::build(());
|
||||||
|
|
||||||
|
insta::assert_snapshot!(operation.query);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_managed_users_gql_output() {
|
||||||
|
use cynic::QueryBuilder;
|
||||||
|
|
||||||
|
let operation = ListManagedUsers::build(());
|
||||||
|
|
||||||
|
insta::assert_snapshot!(operation.query);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delete_user_gql_output() {
|
||||||
|
use cynic::MutationBuilder;
|
||||||
|
|
||||||
|
let operation = DeleteUser::build(DeleteUserVariables { id: "user" });
|
||||||
|
|
||||||
|
insta::assert_snapshot!(operation.query);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_user_gql_output() {
|
||||||
|
use cynic::MutationBuilder;
|
||||||
|
|
||||||
|
let operation = CreateUser::build(CreateUserVariables { id: "user" });
|
||||||
|
|
||||||
|
insta::assert_snapshot!(operation.query);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_user_to_group_gql_output() {
|
||||||
|
use cynic::MutationBuilder;
|
||||||
|
|
||||||
|
let operation = AddUserToGroup::build(AddUserToGroupVariables {
|
||||||
|
id: "user",
|
||||||
|
group: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
insta::assert_snapshot!(operation.query);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
source: src/lib.rs
|
||||||
|
expression: operation.query
|
||||||
|
---
|
||||||
|
mutation AddUserToGroup($group: Int!, $id: String!) {
|
||||||
|
addUserToGroup(groupId: $group, userId: $id) {
|
||||||
|
ok
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
source: queries/src/lib.rs
|
||||||
|
expression: operation.query
|
||||||
|
---
|
||||||
|
mutation CreateManagedUserAttribute {
|
||||||
|
addUserAttribute(attributeType: INTEGER, isEditable: false, isList: false, isVisible: false, name: "managed") {
|
||||||
|
ok
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
---
|
||||||
|
source: src/lib.rs
|
||||||
|
assertion_line: 142
|
||||||
|
expression: operation.query
|
||||||
|
---
|
||||||
|
mutation CreateUser($id: String!) {
|
||||||
|
createUser(user: {attributes: [{name: "managed", value: ["1"]}], email: $id, id: $id}) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
source: src/lib.rs
|
||||||
|
expression: operation.query
|
||||||
|
---
|
||||||
|
mutation DeleteUser($id: String!) {
|
||||||
|
deleteUser(userId: $id) {
|
||||||
|
ok
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
---
|
||||||
|
source: src/lib.rs
|
||||||
|
expression: operation.query
|
||||||
|
---
|
||||||
|
query GetUserAttributes {
|
||||||
|
schema {
|
||||||
|
userSchema {
|
||||||
|
attributes {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
source: src/lib.rs
|
||||||
|
expression: operation.query
|
||||||
|
---
|
||||||
|
query ListManagedUsers {
|
||||||
|
users(filters: {eq: {field: "managed", value: "1"}}) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
1
src/lib.rs
Normal file
1
src/lib.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod lldap;
|
48
src/lldap.rs
Normal file
48
src/lldap.rs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
use anyhow::{anyhow, Context};
|
||||||
|
use lldap_auth::{opaque, registration};
|
||||||
|
use surf::Client;
|
||||||
|
|
||||||
|
pub async fn change_password(client: &Client, user_id: &str, password: &str) -> anyhow::Result<()> {
|
||||||
|
let mut rng = rand::rngs::OsRng;
|
||||||
|
let registration_start_request =
|
||||||
|
opaque::client::registration::start_registration(password, &mut rng)
|
||||||
|
.context("Could not initiate password change")?;
|
||||||
|
|
||||||
|
let start_request = registration::ClientRegistrationStartRequest {
|
||||||
|
username: user_id.into(),
|
||||||
|
registration_start_request: registration_start_request.message,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut response = client
|
||||||
|
.post("/auth/opaque/register/start")
|
||||||
|
.body_json(&start_request)
|
||||||
|
.map_err(|e| anyhow!(e))?
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow!(e))?;
|
||||||
|
|
||||||
|
let response: registration::ServerRegistrationStartResponse =
|
||||||
|
response.body_json().await.map_err(|e| anyhow!(e))?;
|
||||||
|
|
||||||
|
let registration_finish = opaque::client::registration::finish_registration(
|
||||||
|
registration_start_request.state,
|
||||||
|
response.registration_response,
|
||||||
|
&mut rng,
|
||||||
|
)
|
||||||
|
.context("Error during password change")?;
|
||||||
|
|
||||||
|
let request = registration::ClientRegistrationFinishRequest {
|
||||||
|
server_data: response.server_data,
|
||||||
|
registration_upload: registration_finish.message,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _response = client
|
||||||
|
.post("/auth/opaque/register/finish")
|
||||||
|
.body_json(&request)
|
||||||
|
.map_err(|e| anyhow!(e))?
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow!(e))?;
|
||||||
|
|
||||||
|
println!("Changed '{user_id}' password successfully");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
111
src/main.rs
Normal file
111
src/main.rs
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use cynic::{http::SurfExt, MutationBuilder, QueryBuilder};
|
||||||
|
use lldap_controller::lldap::change_password;
|
||||||
|
use queries::{
|
||||||
|
AddUserToGroup, AddUserToGroupVariables, CreateManagedUserAttribute, CreateUser,
|
||||||
|
CreateUserVariables, DeleteUser, DeleteUserVariables, GetUserAttributes, ListManagedUsers,
|
||||||
|
};
|
||||||
|
use surf::{Client, Config, Url};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
let token = std::env::var("LLDAP_TOKEN")?;
|
||||||
|
|
||||||
|
let base_url = "http://localhost:17170";
|
||||||
|
let users = [
|
||||||
|
"authelia".to_owned(),
|
||||||
|
"grafana".to_owned(),
|
||||||
|
"gitea".to_owned(),
|
||||||
|
];
|
||||||
|
|
||||||
|
let client: Client = Config::new()
|
||||||
|
.set_base_url(Url::parse(base_url)?)
|
||||||
|
.set_timeout(Some(Duration::from_secs(1)))
|
||||||
|
.add_header("Authorization", format!("Bearer {token}"))
|
||||||
|
.map_err(|e| anyhow!(e))?
|
||||||
|
.try_into()?;
|
||||||
|
|
||||||
|
let operation = GetUserAttributes::build(());
|
||||||
|
let response = client
|
||||||
|
.post("/api/graphql")
|
||||||
|
.run_graphql(operation)
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow!(e))?;
|
||||||
|
|
||||||
|
let has_managed = response
|
||||||
|
.data
|
||||||
|
.as_ref()
|
||||||
|
.expect("Should get data")
|
||||||
|
.schema
|
||||||
|
.user_schema
|
||||||
|
.attributes
|
||||||
|
.iter()
|
||||||
|
.any(|attr| attr.name == "managed");
|
||||||
|
|
||||||
|
if !has_managed {
|
||||||
|
let operation = CreateManagedUserAttribute::build(());
|
||||||
|
let _response = client
|
||||||
|
.post("/api/graphql")
|
||||||
|
.run_graphql(operation)
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow!(e))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let operation = ListManagedUsers::build(());
|
||||||
|
let response = client
|
||||||
|
.post("/api/graphql")
|
||||||
|
.run_graphql(operation)
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow!(e))?;
|
||||||
|
|
||||||
|
let (existing, remove): (Vec<_>, Vec<_>) = response
|
||||||
|
.data
|
||||||
|
.expect("Should get data")
|
||||||
|
.users
|
||||||
|
.into_iter()
|
||||||
|
.map(|user| user.id)
|
||||||
|
.partition(|user| users.contains(user));
|
||||||
|
|
||||||
|
let (update, create): (Vec<_>, Vec<_>) = users.iter().partition(|user| existing.contains(user));
|
||||||
|
|
||||||
|
for id in &remove {
|
||||||
|
println!("Removing '{id}");
|
||||||
|
|
||||||
|
let operation = DeleteUser::build(DeleteUserVariables { id });
|
||||||
|
let _response = client
|
||||||
|
.post("/api/graphql")
|
||||||
|
.run_graphql(operation)
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow!(e))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for id in create {
|
||||||
|
println!("Creating '{id}'");
|
||||||
|
|
||||||
|
let operation = CreateUser::build(CreateUserVariables { id });
|
||||||
|
let _response = client
|
||||||
|
.post("/api/graphql")
|
||||||
|
.run_graphql(operation)
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow!(e))?;
|
||||||
|
|
||||||
|
let operation = AddUserToGroup::build(AddUserToGroupVariables { id, group: 3 });
|
||||||
|
let _response = client
|
||||||
|
.post("/api/graphql")
|
||||||
|
.run_graphql(operation)
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow!(e))?;
|
||||||
|
|
||||||
|
change_password(&client, id, "JustATest").await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for id in update {
|
||||||
|
println!("Updating '{id}'");
|
||||||
|
|
||||||
|
change_password(&client, id, "JustATest").await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user