Skip to content

Commit

Permalink
[auth] display status in claims configuration
Browse files Browse the repository at this point in the history
[identity] validate user metadata
  • Loading branch information
patatoid committed Oct 25, 2024
1 parent 3162e83 commit bf1b057
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 15 deletions.
19 changes: 17 additions & 2 deletions apps/boruta_admin/assets/src/components/Forms/UserForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
</div>
<section v-if="user.backend.metadata_fields.length">
<h3>Metadata</h3>
<div class="ui segment" v-for="field in user.backend.metadata_fields">
<div class="ui metadata segment" v-for="field in user.backend.metadata_fields">
<h4>{{ field.attribute_name }}</h4>
<div class="ui two column stackable grid">
<div class="ui three column stackable grid">
<div class="column">
<div class="field" :class="{ 'error': user.errors?.metadata }">
<label>Value</label>
Expand All @@ -47,6 +47,15 @@
</select>
</div>
</div>
<div class="column">
<div class="field">
<label>Claim format</label>
<div class="ui toggle checkbox">
<input type="checkbox" v-model="user.metadata[field.attribute_name].displayStatus">
<label>display status</label>
</div>
</div>
</div>
</div>
</div>
</section>
Expand Down Expand Up @@ -172,3 +181,9 @@ export default {
}
}
</script>
<style lang="scss">
.metadata .toggle {
padding: 8px;
}
</style>
24 changes: 22 additions & 2 deletions apps/boruta_admin/assets/src/models/user.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,17 @@ const assign = {
email: function ({ email }) { this.email = email },
totp_registered_at: function ({ totp_registered_at }) { this.totp_registered_at = totp_registered_at },
federated_metadata: function ({ federated_metadata }) { this.federated_metadata = federated_metadata },
metadata: function ({ metadata }) { this.metadata = metadata },
metadata: function ({ metadata: rawMetadata }) {
const metadata = {}

for (const key in rawMetadata) {
metadata[key] = {
displayStatus: rawMetadata[key].display?.includes('status'),
...rawMetadata[key]
}
}
this.metadata = metadata
},
group: function ({ group }) { this.group = group },
authorized_scopes: function ({ authorized_scopes }) {
this.authorized_scopes = authorized_scopes.map((scope) => {
Expand Down Expand Up @@ -109,7 +119,17 @@ class User {
}

get serialized () {
const { id, email, password, metadata, group, authorized_scopes, roles, organizations } = this
const { id, email, password, metadata: rawMetadata, group, authorized_scopes, roles, organizations } = this

const metadata = {}

for (const key in rawMetadata) {
metadata[key] = {
display: rawMetadata[key].displayStatus ? ['status'] : [],
value: rawMetadata[key].value,
status: rawMetadata[key].status
}
}

return {
id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
</div>
</div>
</div>
<router-link :to="{ name: 'client-list' }" class="ui right floated button">Back</router-link>
<router-link :to="{ name: 'user-list' }" class="ui right floated button">Back</router-link>
</div>
</div>
<div class="twelve wide column">
Expand Down
37 changes: 35 additions & 2 deletions apps/boruta_identity/lib/boruta_identity/accounts/schemas/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ defmodule BorutaIdentity.Accounts.User do
updated_at: DateTime.t() | nil
}

@metadata_schema %{
"type" => "object",
"properties" => %{
"value" => %{"type" => "string"},
"status" => %{"type" => "string"},
"display" => %{"type" => "array", "items" => %{"type" => "string"}}
},
"required" => ["value", "status"],
"additionalProperties" => false
}

def account_types, do: [
BorutaIdentity.Accounts.Federated.account_type(),
BorutaIdentity.Accounts.Internal.account_type(),
Expand Down Expand Up @@ -93,13 +104,15 @@ defmodule BorutaIdentity.Accounts.User do
|> metadata_template_filter(backend)
|> validate_required([:backend_id, :uid, :username, :account_type])
|> validate_inclusion(:account_type, account_types())
|> validate_metadata()
|> validate_group()
end

def changeset(user, attrs \\ %{}) do
user
|> cast(attrs, [:metadata, :group])
|> validate_group()
|> validate_metadata()
end

def login_changeset(user) do
Expand Down Expand Up @@ -188,8 +201,10 @@ defmodule BorutaIdentity.Accounts.User do
nil -> true
_ -> false
end)
|> Enum.map(fn {key, value} ->
{key, %{value: value, status: "valid"}}
|> Enum.map(fn
{_key, value} when is_map(value) -> value
{key, value} ->
{key, %{"value" => value, "status" => "valid"}}
end)
|> Enum.into(%{})
end
Expand Down Expand Up @@ -226,4 +241,22 @@ defmodule BorutaIdentity.Accounts.User do
end
end
end

defp validate_metadata(
%Ecto.Changeset{changes: %{metadata: metadata}} = changeset
) do
Enum.reduce_while(metadata, changeset, fn {_attribute, value}, changeset ->
case ExJsonSchema.Validator.validate(@metadata_schema, value) do
:ok ->
{:cont, changeset}

{:error, errors} ->
{:halt, Enum.reduce(errors, changeset, fn {message, path}, changeset ->
add_error(changeset, :metadata, "#{message} at #{path}")
end)}
end
end)
end

defp validate_metadata(changeset), do: changeset
end
4 changes: 0 additions & 4 deletions apps/boruta_identity/lib/boruta_identity/resource_owners.ex
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,6 @@ defmodule BorutaIdentity.ResourceOwners do
metadata
|> User.metadata_filter(backend)
|> metadata_scope_filter(scope, backend)
|> Enum.map(fn
{key, value} when is_binary(value) -> {key, value}
{key, metadata} -> {key, metadata["value"]}
end)
|> Enum.into(%{})

scope
Expand Down
69 changes: 66 additions & 3 deletions apps/boruta_identity/test/boruta_identity/admin_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -158,15 +158,53 @@ defmodule BorutaIdentity.AdminTest do
params = %{
username: "[email protected]",
password: "a valid password",
metadata: %{"attribute_test" => "attribute_test value"}
metadata: %{
"attribute_test" => %{
"value" => "attribute_test value",
"status" => "valid"
}
}
}

assert {:ok,
%User{
metadata: %{"attribute_test" => "attribute_test value"}
metadata: %{
"attribute_test" => %{
"value" => "attribute_test value",
"status" => "valid"
}
}
}} = Admin.create_user(backend, params)
end

test "returns an error with invalid metadata", %{backend: backend} do
metadata_field = %{
"attribute_name" => "attribute_test"
}

{:ok, backend} =
Ecto.Changeset.change(backend, %{
metadata_fields: [
metadata_field
]
})
|> Repo.update()

params = %{
username: "[email protected]",
password: "a valid password",
metadata: %{
"attribute_test" => %{
"value" => "attribute_test value"
}
}
}

assert_raise Ecto.InvalidChangesetError, fn ->
Admin.create_user(backend, params)
end
end

test "creates a user with a group", %{backend: backend} do
params = %{
username: "[email protected]",
Expand Down Expand Up @@ -239,12 +277,37 @@ defmodule BorutaIdentity.AdminTest do
Ecto.Changeset.change(user.backend, %{metadata_fields: [%{attribute_name: "test"}]})
|> Repo.update()

metadata = %{"test" => "test value"}
metadata = %{
"attribute_test" => %{
"value" => "attribute_test value",
"status" => "valid"
}
}

user_params = %{metadata: metadata}

assert {:ok, %User{metadata: ^metadata}} = Admin.update_user(user, user_params)
end

test "returns an error with invalid metadata", %{user: user} do
{:ok, _backend} =
Ecto.Changeset.change(user.backend, %{metadata_fields: [%{attribute_name: "test"}]})
|> Repo.update()

metadata = %{
"attribute_test" => %{
"value" => "attribute_test value"
}
}

user_params = %{metadata: metadata}

assert {:error,
%Ecto.Changeset{
errors: [metadata: {"Required property status was not present. at #", []}]
}} = Admin.update_user(user, user_params)
end

test "returns an error if group is not unique", %{user: user} do
{:ok, _backend} =
Ecto.Changeset.change(user.backend, %{metadata_fields: [%{attribute_name: "test"}]})
Expand Down
2 changes: 1 addition & 1 deletion mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"argon2_elixir": {:hex, :argon2_elixir, "2.4.1", "edb27bdd326bc738f3e4614eddc2f73507be6fedc9533c6bcc6f15bbac9c85cc", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "0e21f52a373739d00bdfd5fe6da2f04eea623cb4f66899f7526dd9db03903d9f"},
"asn1_compiler": {:hex, :asn1_compiler, "0.1.1", "64a4e52b59d1f225878445ace2c75cd2245b13a5a81182304fd9dc5acfc8994e", [:mix], [], "hexpm", "c250d24c22f1a3f305d88864400f9ac2df55c6886e1e3a030e2946efeb94695e"},
"bcrypt_elixir": {:hex, :bcrypt_elixir, "3.1.0", "0b110a9a6c619b19a7f73fa3004aa11d6e719a67e672d1633dc36b6b2290a0f7", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "2ad2acb5a8bc049e8d5aa267802631912bb80d5f4110a178ae7999e69dca1bf7"},
"boruta": {:git, "https://github.com/malach-it/boruta_auth", "fb959e1f9ec337bc87a0ee5d5587f433cd6c1ee5", []},
"boruta": {:git, "https://github.com/malach-it/boruta_auth", "142828018dfb53b73fc47b38aa5ffb5c56d77c21", []},
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
"bypass": {:hex, :bypass, "2.1.0", "909782781bf8e20ee86a9cabde36b259d44af8b9f38756173e8f5e2e1fabb9b1", [:mix], [{:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "d9b5df8fa5b7a6efa08384e9bbecfe4ce61c77d28a4282f79e02f1ef78d96b80"},
"castore": {:hex, :castore, "1.0.9", "5cc77474afadf02c7c017823f460a17daa7908e991b0cc917febc90e466a375c", [:mix], [], "hexpm", "5ea956504f1ba6f2b4eb707061d8e17870de2bee95fb59d512872c2ef06925e7"},
Expand Down

0 comments on commit bf1b057

Please sign in to comment.