Google Admin SDK Unable to Create User - Exception 403 Forbidden - google-admin-sdk

I am trying to create a user using C# and the google-admin-directory_v1-rev24-csharp-1.7.0-beta client libraries however I keep geting an exception:
Google.GoogleApiException was unhandled
HResult=-2146233088
Message=Google.Apis.Requests.RequestError
Not Authorized to access this resource/api [403]
Errors [
Message[Not Authorized to access this resource/api] Location[ - ] Reason[forbidden] Domain[global]
]
Source=Google.Apis
ServiceName=admin
StackTrace:
at Google.Apis.Requests.ClientServiceRequest`1.Execute() in c:\code\google.com\google-api-dotnet-client\default\Tools\Google.Apis.Release\bin\Debug \output\default\Src\GoogleApis\Apis\Requests\ClientServiceRequest.cs:line 102
at ConsoleApplication1.Program.Main(String[] args) in c:\Users\darin.BASEHEX\Documents\Visual Studio 2013\Projects \ConsoleApplication1\ConsoleApplication1\Program.cs:line 55
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:
I started by creating a project in the Developers console, under the APIs settings page I turned on the ADMIN SDK, under the Credentials page I created a Service Account and downloaded the p12.key certificate, I filled out the Consent Screen and then I went to the Security page on the Admin Console, selected the Advance tab, selected the Manage third party OAuth Client access, pasted in the Client ID from the Service Account I created and assigned the scope https://www.googleapis.com/auth/admin.directory.user/. The exception is being raised on the line:
User results = service.Users.Insert(newuserbody).Execute();
The complete C# program is below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Admin.Directory.directory_v1;
using Google.Apis.Admin.Directory.directory_v1.Data;
using Google.Apis.Services;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
String serviceAccountEmail = "A bunch of alpha numerics#developer.gserviceaccount.com";
var certificate = new X509Certificate2(#"key.p12", "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { DirectoryService.Scope.AdminDirectoryUser },
User = "admin#mydomain.com" <-------- Added ADMIN User to fix
}.FromCertificate(certificate));
// Create the service.
var service = new DirectoryService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "User Provisioning",
});
User newuserbody = new User();
UserName newusername = new UserName();
newuserbody.PrimaryEmail = "bbacon#mydomain.com";
newusername.GivenName = "Bob";
newusername.FamilyName = "Bacon";
newuserbody.Name = newusername;
newuserbody.Password = "iambacon";
User results = service.Users.Insert(newuserbody).Execute();
Console.WriteLine("Press any key to continue!");
Console.ReadKey();
}
}
}
I feel like it is something really simple I am missing however I have run out of any ideas on what to look at. Any help would be greatly appreciated!

It doesn't look like that your service account is impersonating any users in this call. When you want to use a service account to create a new user, you need to impersonate as the administrator account (Note: only administrator can create users).
Take a look at this Drive example for domain wide delegation:
https://developers.google.com/drive/delegation
You can see that for the .net portion, it has this extra line:
ServiceAccountUser = userEmail
You need to create a service object authorized with the service account that will acts on behalf of the given user.

Related

Using MSAL to get access token and cache it in SQL DB, without having to sign in using MSAL

I want to authenticate AAD users to access powerBi resources through MSAL by using application ID and secret. So i want to get the access token and cache it in SQL Db.
went through the documentation but it explains the scenario of using MSAL for sign-in.
also went through the tutorial
i was able to to do the necessary implementations to get the token.
how can i get the access token and cache it, in a scenario like this?
As indicated in other answers, caching tokens are useful in case when you have users signing in, as once the access token expires (typically after 1 hour), you don't want to keep prompting the users to re-authenticate.
So help with these scenarios, Azure AD issues a refresh token along with an access token that is used to fetch access tokens once they expire. Caching is required to cache these refresh tokens as they are valid for 90 days.
When an app signs as itself (and not signing in a user), the client credentials flow is used and it only needs the app id (clientId) and the credential (secret/certificate) to issue an access token. The MSAL library will automatically detect when the access token expires and will use the clientId/credential combination to automatically get a new access token. So caching is not necessary.
The sample you should be looking at is this one.
I'n not sure to understand, I hope these few lines of code will help you.
First, customize token cache serialization :
public class ClientApplicationBuilder
{
public static IConfidentialClientApplication Build()
{
IConfidentialClientApplication clientApplication =
ConfidentialClientApplicationBuilder
.Create(ClientId)
.WithRedirectUri(RedirectUri)
.WithClientSecret(ClientSecret)
.Build();
clientApplication.UserTokenCache.SetBeforeAccessAsync(BeforeAccessNotification);
clientApplication.UserTokenCache.SetAfterAccessAsync(AfterAccessNotification);
return clientApplication;
}
private static async Task<byte[]> GetMsalV3StateAsync()
{
//TODO: Implement code to retrieve MsalV3 state from DB
}
private static async Task StoreMsalV3StateAsync(byte[] msalV3State)
{
//TODO: Implement code to persist MsalV3 state to DB
}
private static async Task BeforeAccessNotification(TokenCacheNotificationArgs args)
{
byte[] msalV3State = await GetMsalV3StateAsync();
args.TokenCache.DeserializeMsalV3(msalV3State);
}
private static async Task AfterAccessNotification(TokenCacheNotificationArgs args)
{
if (args.HasStateChanged)
{
byte[] msalV3State = args.TokenCache.SerializeMsalV3();
await StoreMsalV3StateAsync(msalV3State);
}
}
}
Here's an example to acquire token (by Authorization Code) :
public class MsAccountController
: Controller
{
private readonly IConfidentialClientApplication _clientApplication;
public MsAccountController()
{
_clientApplication = ClientApplicationBuilder.Build();
}
[HttpGet]
public async Task<IActionResult> Index()
{
Uri authorizationRequestUrl = await _clientApplication.GetAuthorizationRequestUrl(ClientApplicationHelper.Scopes).ExecuteAsync();
string authorizationRequestUrlStr = authorizationRequestUrl.ToString();
return Redirect(authorizationRequestUrlStr);
}
[HttpGet]
public async Task<IActionResult> OAuth2Callback(string code, string state)
{
AuthenticationResult authenticationResult = await _clientApplication.AcquireTokenByAuthorizationCode(scopes, code).ExecuteAsync();
return Ok(authenticationResult);
}
}
Finally, acquire a token silently and use auth result for your API client :
public class TaskController
: Controller
{
private readonly IConfidentialClientApplication _clientApplication;
public TaskController()
{
_clientApplication = ClientApplicationBuilder.Build();
}
[HttpGet]
public async Task<IActionResult> Index()
{
IEnumerable<IAccount> accounts = await _clientApplication.GetAccountsAsync();
AuthenticationResult result = await _clientApplication.AcquireTokenSilent(ClientApplicationHelper.Scopes, accounts.FirstOrDefault()).ExecuteAsync();
//TODO: Create your API client using authentication result
}
}
Regards
You can cache the access token (actually, the library does this already), but it is valid for 1 hour only. So it makes no sense to save it in a database, because it will expire quickly.
You should cache the credentials needed to obtain the token (user name and password, app ID and secret, or certificate) and obtain a token when needed.
I've done this for a confidential client application, where I connected to O365 in order to send email.
First, register your app in azure app as the docs say.
Then, set up your confidential client application and use as singleton.
var app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithRedirectUri(redirectUri)
.WithLegacyCacheCompatibility(false)
.WithAuthority(AadAuthorityAudience.AzureAdAndPersonalMicrosoftAccount)
.Build();
app.AddDistributedTokenCache(services => {
services.AddDistributedTokenCaches();
services.AddDistributedSqlServerCache(options => {
options.SchemaName = "dbo";
options.TableName = "O365TokenCache";
options.ConnectionString = sqlCacheConnectionString;
options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
});
});
services.AddSingleton<IConfidentialClientApplication>(app);
The first time you connect a user, you need to redirect to Microsoft identity. You can create the URL using:
var authUrl = await app.GetAuthorizationRequestUrl(new[] { "email", "offline_access", "https://outlook.office.com/SMTP.Send" }).ExecuteAsync();
(Check your scopes are what you want)
When they come back to your redirect url you then get the code from query string and acquire the refresh token:
var token = await app.AcquireTokenByAuthorizationCode(scopes, code).ExecuteAsync();
When you do this, MSAL will cache the access token and refresh token for you, but here's the thing they don't mention: you have to create the table in SQL yourself! If you don't, it just silently fails.
dotnet tool install -g dotnet-sql-cache
dotnet sql-cache create "<connection string>" dbo O365TokenCache
Once you have the access token the first time you can use the following later
var account = await app.GetAccountAsync(accountId);
var token = await app.AcquireTokenSilent(scopes, account).ExecuteAsync();
When you get the access token the first time, you need to look at token.Account.HomeAccountId.Identifier as this is the ID that you need when you call GetAccountAsync. For some reason, GetAccountsAsync (note the extra "s") always returns empty for me but passing the correct ID to GetAccountAsync does return the right one.
For me, I simply store that ID against the logged in user so that I can get that ID at a later time.

AWS Cognito User Pool Sign In Missing Authentication Token

I am trying to authenticate my user in a Xamarin Forms cross platform app using AWS Cognito User Pools.
I am able to sign up a user using the SignUpAysnc() and I can see it populate in the user pool in the AWS console.
CognitoUserPool userPool = AmazonUtils.UserPool;
Dictionary<string, string> userAttributes = new Dictionary<string, string>();
userAttributes.Add("email", email);
userAttributes.Add("given_name", given_name);
userAttributes.Add("family_name", family_name);
userAttributes.Add("gender", gender);
userAttributes.Add("birthdate", birthdate);
userAttributes.Add("address", address);
userAttributes.Add("locale", locale);
userAttributes.Add("phone_number", phone_number);
await userPool.SignUpAsync(email, Password, userAttributes, null);
However when I try to use the email and password provided to sign in I keep getting this exception:
[0:] Missing Authentication Token
My current authentication code is:
private async void LoginButton_Clicked(object sender, EventArgs e)
{
AdminInitiateAuthRequest authRequest = new AdminInitiateAuthRequest();
authRequest.ClientId = Constants.ClientID;
authRequest.AuthParameters.Add("email", "test.user#email.com");
authRequest.AuthParameters.Add("password", "Password12!");
authRequest.UserPoolId = Constants.AuthPoolID;
authRequest.AuthFlow = AuthFlowType.ADMIN_NO_SRP_AUTH;
try
{
AdminInitiateAuthResponse response = await AmazonUtils.IdentityClientProvider.AdminInitiateAuthAsync(authRequest);
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
Does anyone know what I might be missing?
The thing is that you need to initiate the authentication challenge first, and then pass password as well as encrypted string, that contains pool name, username, secret block and other information.
This is a good .Net example: http://blog.mmlac.com/aws-cognito-srp-login-c-sharp-dot-net/
Note that you need to add Org.BouncyCastle.Math and Org.BouncyCastle.Security via NuGet to your project in order to compile it.
There is another example: https://gist.github.com/dbeattie71/44ea3a13145f185d303e620c299ab1c5
It looks promising, but I did not check it so I can't guaranty that it works. From it you could get an understanding on how overall process looks like.

Websphere Role Based WS-Security with UsernameToken

Through the Websphere Console I've setup a Policy Set and a Policy Set Binding in order to support UsernameToken authentication on a webservice. As expected, it is rejecting web service calls without correct usernames and passwords. However, it is now accepting every user in the connected LDAP.
I would like to be able to only allow access to users in a specific LDAP group. I have the feeling that I need to create a custom JAAS Login in the Caller settings, but I'm not completely sure.
Does anybody have a solution for this, or a direction where I should be looking?
Edit: I'm doing this to expose an IBM BPM web service.
Create your web service based on EJB not a POJO, and then use #RolesAllowed annotation to specify roles which are allowed to invoke particular method from your service. Use adminconsole, scirpt or binding file to map defined role to user or groups from the LDAP server.
This is probably much easier than fighting with Login module and more flexible.
You can create a custom JAAS login module to use when consuming the username token. You can use a JAAS config that first calls the built-in token consumer, then your custom consumer. Doing it this way means that you can use the built-in consumer to parse the token and do timestamp and nonce processing and you only have to do the username/password validation in your own login module.
The instructions can be found here: http://www14.software.ibm.com/webapp/wsbroker/redirect?version=phil&product=was-nd-dist&topic=twbs_replace_authmethod_usernametoken
(Please forgive the formatting. I'm doing the best I can with what I have available here.)
Replacing the authentication method of the UsernameToken consumer using a stacked JAAS login module
By default, the Web services security UsernameToken consumer, UNTConsumeLoginModule, always validates the username and password that are contained within the token against the WebSphere registry. You can use the SPIs that GenericSecurityTokenFactory provides to bypass this authentication method.
About this task
If you want to replace the authentication method that UNTConsumeLoginModule uses, you must provide your own custom JAAS login module to do the authentication. The custom login module is stacked under UNTConsumeLoginModule in a custom JAAS configuration. The UNTConsumeLoginModule consumes and validates the token XML. The validation of the values provided for username and password is deferred to the custom stacked login module.
Because the use of UNTConsumeLoginModule carries with it the assumption that the username and password will be authenticated, more requirements are put on a stacked login module that intends to perform this function than are put on login modules that are only intended to provide dynamic token functionality.
To indicate to UNTConsumeLoginModule that it should not authenticate the username and password, you must set the following property on the configured callback handler:
com.ibm.wsspi.wssecurity.token.UsernameToken.authDeferred=true
Like most WS-Security login modles, UNTConsumeLoginModule always puts the consumed token in the shared state map to which all login modules in the stack have access. When authDeferred=true is specified, in the commit phase, UNTConsumeLoginModule ensures that the same UsernameToken object that had originally been put on the shared state has been put in another location in the shared state. If this UsernameToken object cannot be found, a LoginException occurs. Therefore, you cannot just set authDeferred=true on the callback handler without having an accompanying login module return the token to the shared state.
Procedure
Develop a JAAS login module to do the authentication and make it available to your application code. This new login module stacks under the com.ibm.ws.wssecurity.wssapi.token.impl.UNTConsumeLoginModule.
This login module must:
Use the following method to get the UsernameToken that UNTConsumeLoginModule consumes.
UsernameToken unt = UsernameToken)factory.getConsumerTokenFromSharedState(sharedState,UsernameToken.ValueType);
In this code example, factory is an instance of com.ibm.websphere.wssecurity.wssapi.token.GenericSecurityTokenFactory.
Check the username and password in the manner that you choose.
You can call unt.getUsername() and unt.getPassword() to get the username and password.
Your login module should throw a LoginException if there is an authentication error.
Put the UsernameToken, that was obtained in the previous substep, back on the shared state.
Use the following method to put the UsernameToken back on the shared state.
factory.putAuthenticatedTokenToSharedState(sharedState, unt);
Following is an example login module:
package test.tokens;
import com.ibm.websphere.wssecurity.wssapi.token.GenericSecurityTokenFactory;
import com.ibm.websphere.wssecurity.wssapi.WSSUtilFactory;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import com.ibm.websphere.wssecurity.wssapi.token.UsernameToken;
import java.util.ArrayList;
import com.ibm.wsspi.security.registry.RegistryHelper;
import com.ibm.websphere.security.UserRegistry;
public class MyUntAuthenticator implements LoginModule {
private Map _sharedState;
private Map _options;
private CallbackHandler _handler;
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String, ?> sharedState, Map<String, ?> options) {
this._handler = callbackHandler;
this._sharedState = sharedState;
this._options = options;
}
public boolean login() throws LoginException {
//For the sake of readability, this login module does not
//protect against all NPE's
GenericSecurityTokenFactory factory = null;
WSSUtilFactory utilFactory = null;
try {
factory = GenericSecurityTokenFactory.getInstance();
utilFactory = WSSUtilFactory.getInstance();
} catch (Exception e) {
throw new LoginException(e.toString());
}
if (factory == null) {
throw new LoginException("GenericSecurityTokenFactory.getInstance() returned null");
}
UsernameToken unt = (UsernameToken)factory.getConsumerTokenFromSharedState(this._sharedState,UsernameToken.ValueType);
String username = unt.getUsername();
char [] password = unt.getPassword();
//authenticate the username and password
//to validate a PasswordDigest password (fixpack 8.5.5.8 and later)
//String pw = yourCodeToLookUpPasswordForUsername(username);
//boolean match = utilFactory.verifyDigestedPassword(unt, pw.toCharArray());
//if (!match) throw new LoginException("Digested passwords do not match");
//Example:
try {
simpleUserGroupCheck(username, password, "cn=group1,o=ibm,c=us");
} catch (Exception e) {
LoginException le = new LoginException(e.getMessage());
le.initCause(e);
throw le;
}
//Put the authenticated token to the shared state
factory.putAuthenticatedTokenToSharedState(this._sharedState, unt);
return true;
}
private boolean simpleUserGroupCheck(String username, char [] password, String group) throws Exception {
String allowedGroup = null;
//get the default user registry
UserRegistry user_reg = RegistryHelper.getUserRegistry(null);
//authenticate the user against the user registry
user_reg.checkPassword(username, new String(password));
//get the list of groups that the user belongs to
java.util.List<String> groupList = user_reg.getGroupsForUser(username);
//you can either use a hard-coded group
allowedGroup = group;
//or get the value from your own custom property on the callback handler
//WSSUtilFactory util = WSSUtilFactory.getInstance();
//Map map = util.getCallbackHandlerProperties(this._handler);
//allowedGroup = (String) map.get("MY_ALLOWED_GROUP_1");
//check if the user belongs to an allowed group
if (!groupList.contains(allowedGroup)) {
throw new LoginException("user ["+username+"] is not in allowed group ["+allowedGroup+"]");
}
return true;
}
//implement the rest of the methods required by the
//LoginModule interface
}
Create a new JAAS login configuration.
In the administrative console, select Security > Global security.
Under Authentication, select Java Authentication and Authorization Service.
Select System logins.
Click New, and then specify Alias = test.consume.unt.
Click New, and then specify Module class name = com.ibm.ws.wssecurity.wssapi.token.impl.UNTConsumeLoginModule
Click OK.
Click New, and then specify Module class name = test.tokens.MyUntAuthenticator
Select Use login module proxy.
Click OK, and then click SAVE.
Configure your UsernameToken token consumer to use the new JAAS configuration.
Open your bindings configuration that you want to change.
In the administrative console, select WS-Security > Authentication and protection.
Under Authentication tokens, select the UsernameToken inbound token that you want to change.
Select JAAS login = test.consume.unt.
Set the required property on the callback handler that is configured for the UsernameToken consumer.
Click Callback handler.
Add the com.ibm.wsspi.wssecurity.token.UsernameToken.authDeferred=true custom property.
Click OK.
Click SAVE.
Restart the application server to apply the JAAS configuration changes.
Test your service.

How to create Gmail Delegation with Service Account?

We use to create email delegates through Google Email Settings API, but after the deprecation of OAuth 1.0 we were no longer able to authenticate properly. After doing some research I think we should create a service account, delegate domain-wide access for that service account, then authenticate using it. However I can't seem to get it to work, all I receive from Google is 401 unauthorized. Does someone know what I am doing wrong? Here is most of the code, I'm using .Net/c# and I'm using Google Apps for business.
ServiceAccountCredential credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer("serviceAccountEmail")
{
Scopes = new[] { "https://apps-apis.google.com/a/feeds/emailsettings/2.0/ " },
User = "admin email string"
}.FromCertificate({X509 certificate from service account p12 file}));
credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None).Wait(-1);
GoogleMailSettingsService service = new GoogleMailSettingsService("domain name", "appname");
service.SetAuthenticationToken(credential.Token.AccessToken);
service.CreateDelegate("delegator", "delegate");
For those who may need this answer in the future, I was able to provide a solution through the following. For reference I am running a web app using MVC framework, but the solution could be tweaked for a console or GUI standalone app as well.
Basically, I was able to authenticate the GoogleMailSettingsService.Service.RequestFactory with a GOAuth2RequestFactory object.
For instance:
GoogleMailSettingsService service = new GoogleMailSettingsService("domain", "applicationName");
service.RequestFactory = new GOAuth2RequestFactory("service", "AppName", new OAuth2Parameters() { AccessToken = AuthorizationCodeWebApp.AuthResult.Credential.Token.AccessToken });
Now for the AuthorizationCodeWebApp.AuthResult I implemented the following:
public async Task<ActionResult> DelegationMenu(CancellationToken cancellationToken)
{
var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).AuthorizeAsync(cancellationToken);
if (result.Credential == null)
return new RedirectResult(result.RedirectUri); //Will redirect to login page for Google Admin to authenticate.
Session["AuthResult"] = result;
return View();
}
public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "ClientId",
ClientSecret = "ClientSecret"
},
Scopes = new[] { "https://apps-apis.google.com/a/feeds/emailsettings/2.0/" },
DataStore = new FileDataStore("C:\\OAuth2.0Tokens")
});
public override string GetUserId(Controller controller)
{
var user = controller.Session["user"];
if (user == null)
{
user = Guid.NewGuid();
controller.Session["user"] = user;
}
return user.ToString();
}
public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}
}
A service account isn't required for this action. The Email Settings API, within the Admin SDK, allows a Super Admin to set a delegation for an account within the domain without the need to impersonate the user via a service account.
Check out this section of the Developers site for more information on this API. You can also test this on the OAuth Playground and add delegates right from there.

wso2is: how to register a UMListenerServiceComponent?

I would like to re-distribute events in the IS management console. For example if I add/change/delete a user or a role I would like to re-send these updates to other consumers.
The only way I found until now is the UMListenerServiceComponent.
It looks like I can define my own UserStoreManagerListener and register it at the UMListenerServiceComponent.
The following example is from
https://svn.wso2.org/repos/wso2/branches/carbon/3.2.0/core/org.wso2.carbon.user.core/3.2.3/src/main/java/org/wso2/carbon/user/core/jdbc/JDBCUserStoreManager.java
In this case the action of adding a user is triggering the registered listeners.
public void addUser(String userName, Object credential, String[] roleList,
Map<String, String> claims, String profileName, boolean requirePasswordChange)
throws UserStoreException {
for (UserStoreManagerListener listener : UMListenerServiceComponent
.getUserStoreManagerListeners()) {
if (!listener.addUser(userName, credential, roleList, claims, profileName, this)) {
return;
}
}
// persist the user info. in the database.
persistUser(userName, credential, roleList, claims, profileName, requirePasswordChange);
}
My question is how can I implement and register this kind of listeners?
Or is there an easier way?
Thanks in advance!
Problem solved!
Downloaded the identity-manager version which is used for the wso2IS 4.6.0:
http://svn.wso2.org/repos/wso2/carbon/platform/branches/turing/components/identity/org.wso2.carbon.identity.mgt/4.2.1
The class IdentityMgtServiceComponent is registering the IdentityMgtEventListener as an service in the Osgi-Context.
IdentityMgtServiceComponent:
protected void activate(ComponentContext context) {
listener = new IdentityMgtEventListener();
serviceRegistration = context.getBundleContext().registerService(
UserOperationEventListener.class.getName(), listener, null);
Finally I copied this pattern and wrote my own bundle with an extension of AbstractUserOperationEventListener and activated it via a bundle-loader-class.
All the pre/post add/delete actions are triggered fine.
Hope this helps