OneIdentity / IdentityManager.Imx

HTML5 source code for Identity Manager web apps
Other
26 stars 107 forks source link

Reset Password attribute for accounts. #152

Closed juancarloscamargo closed 2 weeks ago

juancarloscamargo commented 3 weeks ago

We're trying to implement a password reset service for unlinked google accounts in our custom version of qer-app-portal Just as the google admin console does:

  1. The operator selects the account
  2. Resets the password to a random one
  3. The password is sent to a delegated admin email address or the user's contact email address, that's irrelevant

For linked accounts, the owner can reset his/her password and it get sync'ed with Google. That is not a problem at all either.

We're trying to create our own api code for password rewrite , but have found nothing related in the SDK documentation. Another option could be reuse endpoints for passwordreset or opsupport but we would need some directions on how to do it , without logging out qer-app-portal.

Any ideas?

hannoquest commented 3 weeks ago

Hi @juancarloscamargo

You could quite easily expose these accounts in the Passwordreset portal and use the OOB API to access them.

I have a code sample for this (this is actually Composition API plugin sample code that will be added in the next release). you add a password item provider to the system that adds whatever accounts/password fields you need to the normal interface.

using QBM.CompositionApi.Password;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading;
using System;
using System.Linq;
using VI.DB.Entities;
using VI.DB;

namespace QBM.CompositionApi
{
    public class SamplePasswordItemProvider : IPasswordItemProvider
    {
        public async Task<IEnumerable<IPasswordItem>> GetPasswordItemsAsync(ISession session,
            CancellationToken ct = default)
        {
            // Determine which accounts the current user is allowed to see/reset. This is just an example.
            var filter = "uid_person in ( select uid_Person from person where IsPwdResetByHelpdeskAllowed=1)";
            var q = Query.From("GAPUser")
                .Where(filter)
                .Select("XObjectKey", "UID_GAPOrgUnit", "UID_Person");

            var accounts = await session.Source()
                .GetCollectionAsync(q, EntityCollectionLoadType.ForeignDisplaysForAllColumns
                                       | EntityCollectionLoadType.LoadForeignDisplaysEvenWhenExpensive, ct)
                .ConfigureAwait(false);

            var list = new List<IPasswordItem>();
            foreach (var accn in accounts)
            {
                try
                {
                    list.AddRange(await GetPasswordItemsAsync(session, accn, ct).ConfigureAwait(false));
                }
                catch (Exception exc)
                {
                    throw new Exception("Error while processing account: " + accn.Display, exc);
                }
            }

            return list;
        }

        private static async Task<IEnumerable<IPasswordItem>> GetPasswordItemsAsync(ISession session, IEntity account, CancellationToken ct)
        {
            var list = new List<IPasswordItem>();

            var tbl = await session.MetaData().GetTableAsync(account.Tablename, ct).ConfigureAwait(false);
            var columns = new[] { tbl.Columns["Password"] };

            foreach (var col in columns)
            {
                // check if this column is writable for this object
                var canEdit = account.Columns[col.Columnname].CanEdit;
                if (!canEdit)
                    continue;

                var rootDisplay = await account.GetDisplayValueAsync(session, "UID_GAPOrgUnit", ct).ConfigureAwait(false);

                // Get the password policy that applies to this account
                var policy = await PasswordItemCollector.GetPolicyAsync(session, account, col.Columnname, ct)
                    .ConfigureAwait(false);

                list.Add(new PasswordItem
                {
                    ColumnName = col.Columnname,
                    Key = account.GetValue(SpecialColumns.XObjectKey),
                    Display = $"{account.Display} ({rootDisplay})",
                    Policy = policy,
                    // PasswordLastSet = lastSet, // set where the target system provides this information
                    Type = PasswordItemType.Other
                });
            }

            return list;
        }

        public Task<IEnumerable<IPasswordItem>> GetPasswordItemsAsync(ISession session, string uidPerson,
            CancellationToken ct = default)
        {
            // This method is intended for a helpdesk user.
            return Task.FromResult(Enumerable.Empty<IPasswordItem>());
        }

    }
}
juancarloscamargo commented 2 weeks ago

Thanks for the insight @hannoquest

In my case, the account has no uid_person linked at all, it's an isolated account that needs a password reset. So I cannot use the password reset portal or operations support. The first one lists those accounts linked to a person, only, The second does not allow password changing of an isolated account either. The password tab is not shown at all and the api always takes an uidperson as a parameter. Also, I cannot use those api endpoints while I'm logged to the portal project, unless there'd be a way to sso between apps (an interesting point , tho)

In the end what I need is an api way of changing the GAPUser.Password attribute . I could eventually call a method but I was looking for a safer or oob way

Thanks again!

hannoquest commented 2 weeks ago

Hi @juancarloscamargo ,

I think you could use the Password Reset Portal for that. There does not need to be relation from the GAPUser to the identity; you can use any filter condition on GAPUser that you like. The only requirement is that the identity has write permissions on the Password attribute.

IMHO that would be the preferred way because of the added security & integration of password policy checks.

juancarloscamargo commented 2 weeks ago

I think you mean this endpoint: image

1.- Can I use it if I'm logged to qer-app-portal? I guess I'll need to log to qer-app-pwdportal too? 2.- I don't know the meaning of the params. I might guess checkonly or newpassword, but Ids stands for?

Thanks again.

hannoquest commented 2 weeks ago

This API is only available when you are logged in to Password Reset Portal - you cannot use it from the Portal.

The Id is the key of the password item

juancarloscamargo commented 2 weeks ago

Thanks. So method is my only chance.