[THEME MUSIC] Simple modifications are documented in the "Identity Manager Web Portal Customization" document. They include changing top logo color, background color, and logo color; changing background image; and showing additional columns for the dynamic roles.
Source code for API server is not available to users. So every time we need to extend functionality of the API server, like reading data from some complex SQL statements or adding modifying records in Identity Manager, we need to create a custom plugin. This plugin will be added to QER app portal API project to extend portal functionality. It will accept the user's log into the portal. And the endpoint will look like this. It will look like server name/portal/methodendpoint.
We will be using API Samples Solution. That is distributed with installation media under Modules/QBM/AddOn/ApiSamples. It's a completely working solution. And it demos the way to write code for various methods.
Also, there are two Visual Studio solutions with custom components and completed code provided in the UNITE/ApiServer folder. This is a sample plugin and Unite plugin solution. Sample plugin solution is-- it's a working solution, but it's not really necessary.
All code is copied from the solution provided with installation. It is just there to show that all the custom plugin methods will be combined into one client TGG file when it is built. The main solution is our Unite plugin.
To create a new plugin, first, we need to open Visual Studio 2022-- also, 2019 or '17 are also fine-- and create a new project. For new project, we need to select Class Library .NET Select Next. Project name-- we need to give it some specific name.
Here, we'll have it-- "UnitePlugin." And for the location, we select Projects and ApiServer. And select framework as Framework 4.8. Visual Studio generates for us a new project. What we need to do is rename class one to some meaningful name, like "MyMethods." Asking if we want to also rename class name-- say, yes, sure. Why not? And class name changes to "MyMethods."
And now we need to make sure that class implements the IApiProvider-- implements IApiProvider for PortalApiProject. This way, a plugin will be added to QER app portal API.
And now we need to add references. Right click here-- References. Right click-- Add Reference. And we need to browse. It shows historical references. I just will click on Browse. All the referenced DLLs are located in One Identity installation folder.
And here is the full list of them-- VI Base, VI DB, and log, and all these composition APIs. We need all of them. The list is provided in the document. Now, we can fix it by adding the using statements. The last one is IApiProvider. And it needs to be implemented. Implement interface.
Now we need to write code for the new methods. But we're not going to write code. It takes too much time. So we'll just copy code from the UnitePlugin-- MyMethods. Open it up with Notepad++. And copy everything. And just paste it also here.
It contains-- we just copied two new methods. And one method we'll be calling script in Identity Manager. And it will pass a central account. Actually, it's a user ID, which I didn't change the name. It will call a script and retrieve data from that script. And this is the script name The second method we'll be calling the predefined SQL-- and retrieve data from it. And there are some other methods we're not going to use in this demo.
We can view this script and predefined SQL in the designer. Here they are. They call the same exact SQL statement, except that script retrieves data from the SQL statement and then packages it into this type of class report fold. And return the list of these classes to the client, where predefined SQL is just a predefined SQL. And it returns data in the standard format also to the client.
Now we need to modify slightly the Visual Studio project so that, when it starts, it executes the imxclient.exe to run local API server. For that, we'll go to Properties, Debug, Start external program. We'll place the path to imxclient.exe-- Program Files/One Identity.
And then we need to add command line arguments so that it runs local API server-- on the API server. And then we need to pass the DLL name. We can check the DLL name here. This is the assembly name. So here, we'll put this assembly name dot DLL.
And now we also need to modify assembly info, add another attribute. Without this attribute, it will not work right. It will not get picked up by the API server. So this attribute is necessary.
Now we can run plugin in debug mode. Maybe we should first put, like, a breakpoint. In the method, calling a predefined SQL breakpoint doesn't break, doesn't get set well. But we can set a breakpoint on the method calling script. And we can start debugging, start running in debug mode. For that, we'll click on the green button here.
We can see that the IMX Client is being started. It pops up a login dialogue. And now it's running in the local host port 8182. We can open it. The landing page has opened. And we can see our Unite plugin being picked up by the local API server.
So now we can open the landing page on the local API server and click on API documentation to Swagger. Now this logger is loaded. And we can find our methods. We'll copy the method name. They all start with "UnitePlugin." Go to Swagger. Find the Unite plugin. And here they are-- getreportfromscript and getreportfrompredefinedsql.
Now we can try them, but first we need to log in. As before, we go to imx/login, portal, true to disable XSRF. And here is our login string. Press Execute. And sometimes error happens on the first try. Execute one more time. And here is a correct response.
Now we can go test our plugin [INAUDIBLE]. First, getreportfromscript-- try it out. We need central account of the person who has reports. It's 1003 in our case. Sorry, it's not central account. It's uid person. Execute. And we can see the script has stopped on the breakpoint. So we can read the variables. We can see the data returned. We can execute step by step.
And here's the return data-- multiple records. Press F5 to continue. And here we see a result. Data is returned in a standard JSON format with key value pairs.
Let's run another method calling the predefined SQL. Try it out. 1003. And it returns data. And it doesn't stop in the breakpoint. . I'm not sure how to stop it for this particular method. But this is the data in some format more complicated than the previous one.
It shows a list of columns. And each column is an object. And it has its value. It's green only. Property is also there. So now we see both methods work. And we can deploy them to the server.
To deploy a plugin to the production server, all we need to do is copy DLL to that production server. But here, first, we need to stop debugging and go to our project-- bin/Debug.
Usually, we should run it in production mode, but we don't have to right out for the demo. We take this generated DLL and copy it to the installed API server. [INAUDIBLE] root, the bin. And we placed it right here. Here it is.
Now we can go to installed API server and click on Administration-- on API Recommendation to Swagger. Now Swagger is displayed. And we can see these new methods were picked up. Find, "unite." And here, we see these new methods. So now the plugin is installed in the production server.
Now we need to build a client package. A client package is a wrapper, like a TypeScript or a JavaScript wrapper, around calls to API server. So you don't need to make actual calls. You just call functions. And the function names will be the names of our new methods. And it will be generated automatically.
So to create this package, we need to copy DLLs like UnitePlugin DLL to the Program File-- basically, One Identity Manager installation directory. Copy it here. And also, just for testing, for demonstration, we can copy SamplePlugin DLL also there. This is to demonstrate that methods from both DLLs will appear in the end in this package.
Now DLLs are copied. We need to run a command. Change directory of the installation directory.
And then we need to run this command-- "imxclient compile api." And here, we just give it a name. We can give it any name. But in our case, it's "uniteplugin." Let's call it "uniteplugin." And again, it's a dialogue. We need to log in.
Now it's compiled. We can see it here. [INAUDIBLE] is mentioned here. And we will see in the installation directory a new file-- imx-api-unitedplugin.tgg.
And let's see what's inside of this file. We'll copy it somewhere [INAUDIBLE] temp directory and use 7-Zip to unzip it. And we unzip it one more time.
And here, we see what's inside of this package. If we open TypedClient.ts, we'll see our methods from the "uniteplugin" and also methods from "sampleplugin."
Now we need to add this generated package to our angular project so [INAUDIBLE] can start using these methods. To do so, we open Visual Studio. And that file needs to be copied to imx modules folder. You'll right click, Reveal in Explorer-- imx modules. And go to this One Identity Manager installation. And copy this file right here.
Now we need to modify the project so that it sees this new file and can use it. For that, we need to go to package json file. And we will see all the other packages here. We just add a new line.
It points to our new file. And in the terminal, we can run-- install our new module.
We will use this module with this new component in the API server further in the advanced modifications.
In this video, we'll demonstrate advanced web portal modification, adding an additional column to the system entitlement membership form here. To add an extra column, first, we need to find the component code. So first, we need to get to that form-- so component, System entitlements, and here.
We would like to display an extra column pointing to the source of this domain user's entitlement. Because here, it's not really clear where it's coming from. So we'll click on the data, Inspect. And as always, Inspect opens at the cell location. Go to role location. And we can get up to the data table. And we get this data table identifier copied. That's it.
Now we'll go to Visual Studio. It placed it in Visual Studio files for us. So this is the real one. Now we can switch to the explorer to see which project this component belongs to. It's QER since a static component. And also, we see role membership components, but-- Now we have two files open-- HTML and corresponding TS file.
Now we can start debugging. First, we need to compile QER. And now it's compiled. And it's being watched for changes. In another terminal, we need to run portal. Now portal is compiled. And we can run it in debug mode. First, we need to run our local API server. It runs.
And now we need to put a breakpoint to see the data return from the Identity Manager to see if the column we're looking for is actually returned. So we'll put a breakpoint on the getData function. And now we can run it in debug mode. "QER App Portal (Chrome)--" click on green button, Start Debugging.
And it stops. The code stops at our breakpoint. We'll press F10 to step 1 execute the line at the breakpoint. And we can look at the data source. Here's the data-- PersonalGroupmembership entity, columns. And here, we'll look at the columns.
And UED_UNSRoot is pretty much the column we are looking for. It shows us which target system this entitlement comes from. So that's what we want to display. We're lucky this API server method returned the column that we need.
Press F5 to continue. And let's think where do we need to put modifications. If we scroll up to right below constructor, we see a local variable, displayedColumns. It's an array of columns.
And we can just try to add another column to this array and see if it will be displayed. "UED_UNSRoot,". And you can see it's being recompiled right away. And the portal is being recompiled. Everything's recompiled. And let's see what it looks like.
System entitlement, breakpoint. F5 to continue running. And here, we have another column. Just the name of it is "UED_UNSRoot." So we need to somehow change the display name of this column.
Let's modify the column so it will have a different display name. So right before "displayedColumns," we'll add wait until the component is compiled and portal is compiled. And let's see the changes. Membership, System entitlement. F5 to continue. And here we have "Target System" and the value.
One last thing-- we probably should check if the entity schema columns contains the actual UED_UNSRoot column before we try to change the display name and edit the displayed columns. So we just put an if statement-- entitySchema.Columns-- UED Root-- that equals null. We will run it as it is right now.
And else-- we just list displayed columns as they were before without the UED_UNSRoot. We can do it in a more elegant way, but here, we have time constraints. And let's see. Portal is compiled. Component is compiling in the portal.
Since component we just modified is QER and it's a static component, we don't need to deploy it to the production server. But we need to rebuild portal application and the rest of the components that depend on the QER. But specifically, if the other dynamic components don't use this particular functionality-- like System entitlements, Membership-- and we don't need to recompile them. But portal application we definitely need to recompile.
So we need to stop debugging. Close. Ctrl-C. And another Ctrl-C. We need to build our portal in the production mode. We need to execute this command, "npm run build:app qer-app-portal." Now it's built. And it appears in the dist folder.
Here it is. Right click, Review in File Explorer. We need to zip everything inside of this folder except the HTML folder. So we just delete it. Select everything. Right click, 7-Zip, Add to archive. And rename this archive to "html_" and then the name of the component or application dot zip. Here is the file. Control-- Cut. And we're going to put it into the production server.
The same thing can be done with software loader. imxweb/custom. We'll place it into the custom folder. We don't need to remove the old one because custom folders are loaded first.
Now we can go to the installed web portal and see if changes are there. Log in. Membership, Systemic entitlements. And here, we have our new "Target System" column
In this video, we'll demonstrate another advanced web portal modification, adding a column to business roles membership form. First, we need to find the component to modify. This is installed web portal. Log in and go to the business role-- Memberships, Business roles. It would be nice to display here another column showing where this role comes from, like role class and maybe hierarchy of the roles or path to the role.
So here, again, we'll right click, Inspect, and find identity-- imx-data-table and the identifier. And it looks familiar. Go to Visual Studio, Search. Place the identifier. Yeah, it's the same component. It displays business roles.
Switch to explorer. Find the component. Here, we already have a code for displaying target system. We will just add a code to display extra column for the business roles.
So let's run it in debug mode. So let's compile this component in debug mode. It's compiled. Let's build a portal. Switch to Portal, Terminal. And run this command. Portal is compiled.
Let's see. The breakpoint is still there from the previous video. That's from a local API server.
And it's running. And now we can run it in debug mode. Memberships. And there is no business roles. In order to see business roles, we need to run in debug mode a dynamic component called an RMB. For the business rules. Otherwise, the menu doesn't appear here.
So let's start over. And we'll add another terminal, rename it, call it "rmb," and run a specific command for dynamic component-- "npm run build:watch:dynamic rmb."
Now it's built. Let's see what's going on with QER and the portal. Everything looks fine. Refresh. And here, we have "Business roles" appear. And the code stopped at the breakpoint.
Let's press F10 to execute this line and see what data source returned from the Identity Manager. One entity, columns. We have a RoleFullPath. We are lucky. We have RoleFullPath.
And also, since this component displays data for various data types, we need to check how to determine whether it's a business roles or something else. If we look at the same returned data, the data source, there is an attribute called TypeName. And it's called "PersonInOrg." And this is enough for us information to determine whether it's a business roles or some other data.
Since we already have a customization from the previous video, we need to add another if statement, checking if the data is coming from the business roles. And then we will just add another column called "RoleFullPath" here. So we just add this code here.
What it says is, if entitySchema.TypeName equals "PersonInOrg", we just add a column called "RoleFullPath." And in the case else, we just display columns as they were originally. We can see that node is already recompiled-- and portal recompiled.
And let's restart debugging. It starts fine. Click Memberships, Business roles. And the data stops at the breakpoint. F5 to continue. And we see a new column. And it even has the right column name. So we don't need to change anything.
Deploying is the same as in the previous video. Basically, we need to recompile QER app portal and deploy it. That's it. Thank you.
In this video, we will demonstrate how to add a column if it's not returned from the API call. We will need to find a way to return this column from the API, modify the client to make the call, and receive that extra column, and display it. First, we need to find the component to modify. For that, we'll open portal, as always. Log in. Find the component.
I would like to add a column here to the user account column that tells us whether our account is enabled or disabled and put it somewhere here at the end. And as before, we right click, Inspect. And let's find-- it is identity-membership-table.
Go to Visual Studio. Search "identity-membership-table." Here, we've found the HTML. Switch-- our file belongs to the tsb library. Now we open component and put a breakpoint to see what kind of data is returned from the API call. Maybe this column was already returned as before or maybe not.
Let's run it in debug mode. Therefore, tsb. Run tsb. We also need to run local API portal.
The tsb is built. Let's run the web portal. Portal is running. And we can start debugging. I placed a breakpoint right after the call to the getAccounts call-- the next line. So we'll be able to examine the values returned from that call.
All right. So let's discover the columns that have been returned from the getAccounts call. [INAUDIBLE] entity, columns. And there are no columns.
AccountName, CanonicalName, [INAUDIBLE] and TypeName is "UNSAccount." So whenever the data is returned from the UNSAccount table from Identity Manager-- and there is no AccountDisabled column here.
Now we need to check the call to the API server to see if it is capable of returning that column to the client. So we'll switch to the installed API server and get to Swagger. And we need to log in first.
This is our login string. Copy that. Try it out. Portal, true, and login string. Execute. And it's logged in.
Sorry I-- we needed first to find which call is this-- so for the accountService.getAccounts, which API call it calls. So we just right click, and Go to Definition. Here, we do it again. We see apiService calls typedClient.PortalPersonalAccounts.
Right click, Go to Definition. PortalPersonalAccounts of this type, PortalPoersonalAccountsWrapper-- right clock, Go to Definition. And here, we have a V2Client.
Now we are in those wrappers that generated classes that we generated before when we worked with API server. These files are generated. We cannot quantify them, but we can study them. Look at definition.
And here, we see all these methods-- portal_candidates. Here's portal_person_accounts_get. I assume this is the call. I assume this is the right call-- portal_person_accounts_get.
As such, it gets converted into the-- it will look like this-- "portal/person" and then something else. And so we go Find, "portal/person." And we're looking for accounts. And here it is-- /portal/person slash uid of the person slash accounts.
Let's try to run it. We'll try it now. uid person 1006. Let's-- I just remember it. And we'll execute. And the call returns us one account, who has columns AccountName, CanonicalName. There is no AccountDisabled column. We just know that UNSAccount account has the AccountDisabled column in it.
So we can try here with properties. We can pass extra-- we can request extra columns from the table. And let's try like that. Execute. And here it is. It return us AccountDisabled column.
So now what we need to do is we need to modify call to the API server and pass this extra column to it. Let's do some research on the component and the service-- the calls that has been made. So component calls the accountService.getAccounts. If we go to service, we see that this getAccounts-- it returns us a collection of PortalPersonalAccount.
If we Go to Definition, we will see that PortalPersonalAccounts has only four columns and nothing else. So we need to create a separate class that extends this PortalPersonalAccounts and also exposes one more column that we will want to return. Also, we need to change the call to the PortalPersonalAccounts get. We need to be able to pass extra column to it.
So we have a prepackaged code that was-- solution that's completed already. We'll just copy it from there. And on declarations, we have this class already defined. We'll paste it into our project.
And it appears here-- declarations. And we'll have PortalPersonalAccountsPlus, where it has an extra column and it extends PortalPersonalAccounts class. During construction, it instantiates the base class and also populates the extra column.
Now the code for this method is here. We'll go a little bit over it line by line. And I need to quick fix it so it points to the-- it doesn't want to point to that-- OK-- account extension declarations. No. And then we just manually add imports statement. Imports PortalPersonalAccountsPlus from this file, from here.
And now we need to modify component to make call to this method. So we'll comment this out, and just copy it, and change-- with extra columns. And here, we need to pass the column name, "AccountDisabled."
Let's put-- let's see how extra columns are passed through the method. Here, we'll call the method the same way as we call in the original call. But there is an extra parameter we can pass. And that parameter is just a class kind of with various values that we saw in Swagger. All these values can be passed to it.
And we just-- and one of these values is withProperties. You see, all of them have quotation marks, meaning that you can not pass anything to it. It will still work. So withProperties is what we need.
So this extra column kind of comes in. And we'll place it into this parametersOptional-- withProperties. And the value is that extra column name. And this is what will pass to the call, the PortalPersonalAccounts get.
So let's put a breakpoint here and start debugging. Actually, we need to recompile it. Stop. And then we will start. Now it's revealed. And we can start debugging.
Breakpoint is here in the service. Log in. Memberships. And here is the breakpoint with our optional parameter "AccountDisabled." And we'll put a breakpoint and press F10. It jumps to the next step. And we can see what data was returned from the service call with extra column requested.
We have entity, columns. And we still don't have an entity. There's no column returned-- or maybe we do. It turns out that we have to specifically expose additional columns. They will not be seen by default because everything is hard-- typed. So we need to do a special operation to expose this extra column that we requested. It's returned, but we cannot see it as of now.
So we look through every record that is returned from PortalPersonalAccounts for the user. So we look through every personal account. We get entity-- personalaccountEntity. And then we ApplySchema.
And in the schema, we tell which column we want to apply, we want to expose, and type of this column, and column name. And it's a little comment. It "exposes additional column in the data that are missing in the existing schema." After we do that, we can see that the AccountDisabled appeared in the entity.
And here, we just populate the new portal account plus. And we populate the structure to return. And we return these values to the component. And component passes this value to the DST system, which got picked up by the HTML file. It's your dstSettings here.
We do it. We run. And we see no column. Because first, we need to modify the HTML file. And we can see that HTML file. It specifies the column specifically here-- one column, two column. UNSRoot, DPRNameSpace, and GetDisplayValue-- its account name.
So we need to add another column here. Another column-- we have it prepared. This is the extra column. And we'll place it somewhere here. See here, we have a disabled column. It has a column description. That's because we don't have an AccountDisabled return with entitySchemaAccount. entitySchemaAccount is retrieved from
GetSchema call to the API server. If we look at it, it's in the service. And we're calling the PortalPersonalAccounts.GetSchema. So by default, it doesn't return this extra column. And we need to make this column up.
So if we go to components, there is a prepared column. We'll put it before displayed columns. And we need to declare it somewhere. And it's declared right here, as any. Because in our case, only our data will go here. So we can say, hey, it doesn't really matter.
And the column is just the plus with the column name, description, display. And value type is Boolean. And this is enough information for us to declare a column.
So we'll type it. And we'll place it here at the end of these displayed columns. And here, we can go debug. Membership.
And this is AccountDisabled. AccountDisabled, Yes. For a different user, AccountDisabled, No. So here, we added another column from the API server call that originally out-of-the-box does not return this call. And these operations will need to be performed every time you want to retrieve an extra column from the API server.
To deploy this dynamic TSP library, we'll need to build it in the production mode. So we stop debugging. Stop debugging here. Run a command. And now it's built.
We'll go to this folder, tsb, Review in File Explorer. Zip it. And then name. And put it into our custom component. We have the earlier version of it. We can just replace it.
And let's see if it picks it up automatically. And we'll log in. Membership. And AccountDisabled column is here. Thank you.
[THEME MUSIC]