We are trying to setup Azkaban with LDAP authentication in our production environment. Any leads on how to do this? Documentation says it can be done by adding plugin jar file by extending UserManager class . I am a newbie to azkaban , looking for some example code for this
You will need to install a custom "user manager" plugin. It can be found on github: https://github.com/researchgate/azkaban-ldap-usermanager
The instructions on how to configure the user manager plugin can be found on the front page of the github repo.
In essence you will need to:
Download and build the plugin
Copy the .jar file that you built into the ./extlib directory of your Azkaban installation
Edit azkaban.properties file, specifying user.manager.class and a number of user.manager.ldap properties.
We also wanted to setup an LDAP authentication in azkaban - but the open source project mentioned in first answer has very limited capabilities and doesn't allow to start TLS negotiation after the connection to LDAP server is established.
We have written a completely new java class to take care of the following scenarios:
Establish a LDAP connection unsecurely (on port 389)
Start TLS Response and TLS negotiate
And then athenticate the user credentials.
With this approach we also dont have to create a service user in LDAP only for azkaban.
Take a look in the sample code block
#Override
public User getUser(String username, String password) throws UserManagerException {
if (username == null || username.trim().isEmpty()) {
throw new UserManagerException("Username is empty.");
} else if (password == null || password.trim().isEmpty()) {
throw new UserManagerException("Password is empty.");
}
String email = null;
String groups = null;
StringBuilder url = new StringBuilder("ldap://").append(ldapHost).append(":").append(ldapPort);
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, url.toString());
try {
// Create initial context
LdapContext ctx = new InitialLdapContext(env, null);
// Start TLS
StartTlsResponse tls = (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest());
SSLSession sess = tls.negotiate();
// Adding username and password to environment
StringBuilder sb = new StringBuilder("uid=").append(username).append(",").append(ldapUserBase);
ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");
ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, sb.toString());
ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password);
// Search the user in a LDAP directory
final LdapCtx x = (LdapCtx) ctx.lookup(sb.toString());
// Lookup is successful - creating an Azkaban user
User user = new User(username);
// Searching LDAP directory to get email and group
SearchControls ctls = new SearchControls();
String[] attributes = { "cn", "memberOf", "mail" };
ctls.setReturningAttributes(attributes);
NamingEnumeration<?> answer = ctx.search(ldapUserBase, "(uid=" + username + ")", ctls);
Boolean isAdmin = false;
// Search user email and groups
while (answer.hasMore()) {
SearchResult rslt = (SearchResult) answer.next();
Attributes attrs = rslt.getAttributes();
groups = attrs.get("memberof").toString().split(":")[1].trim();
if (attrs.get("memberof") != null && attrs.get("memberof").toString().split(":").length > 0) {
groups = attrs.get("memberof").toString().split(":")[1].trim();
for (String group : groups.split(",")) {
if (ldapAdminGroups.contains(group))
isAdmin = true;
}
}
if (attrs.get("mail") != null) {
email = attrs.get("mail").toString().split(":")[1].trim();
user.setEmail(email);
}
}
// Assign the correct role
if (isAdmin)
user.addRole("admin");
else
user.addRole("read");
ctx.close();
return user;
} catch (NamingException e) {
throw new UserManagerException("LDAP error: " + e.getMessage(), e);
} catch (IOException e) {
// TODO Auto-generated catch block
throw new UserManagerException("IO error", e);
}
}
Note: I haven't done lot of exception handling in this - you need to do it as per your needs.
How to make it make it work in Azkaban:
Build a maven or gradle project.
No additional library is required (except Azkaban - i.e. com.linkedin.azkaban)
Have a new class which will inherit 'azkaban.user.UserManager'
Build and copy the jar in azkaban/extlibs
In azkaban.properties - set "user.manager.class=" and also all required properties like host, port and ldap userbase (ou=Users,dc=stackoverflow,dc=com) details.
And you should be good to authenticate users via LDAP.
Happy Coding !!
Thanks,
Hussain Bohra
Related
I'm trying to get google report activity by calling https://www.googleapis.com/admin/reports/v1/activity/users/all/applications/meet
I created a service account and I have to use the generated private key (json file) as access token.
My code was:
String PROTECTED_RESOURCE_URL = "https://www.googleapis.com/admin/reports/v1/activity/users/all/applications/meet?eventName=call_ended&maxResults=10&access_token=";
String graph = "";
try
{
JSONParser parser = new JSONParser();
JSONObject data = (JSONObject) parser.parse(
new FileReader("C:/Users/Administrateur/Desktop/GoogleApis/Interoperability-googleApis/target/classes/my-first-project-274515-361633451f1c.json"));//path to the JSON file.
String json_private_key = data.toJSONString();
URL urUserInfo = new URL(PROTECTED_RESOURCE_URL + json_private_key);
HttpURLConnection connObtainUserInfo = (HttpURLConnection) urUserInfo.openConnection();
if (connObtainUserInfo.getResponseCode() == HttpURLConnection.HTTP_OK)
{
StringBuilder sbLines = new StringBuilder("");
BufferedReader reader = new BufferedReader(new InputStreamReader(connObtainUserInfo.getInputStream(), "utf-8"));
String strLine = "";
while ((strLine = reader.readLine()) != null)
{
sbLines.append(strLine);
}
graph = sbLines.toString();
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
System.out.println("--------------- Result: " + graph);
but I got null value.
Could you please tell me what I misses ?.
Big Thanks.
The Access Token is not part of your request URL. You can read here about the OAuth2 protocol and how it works.
However, Google built an API that enables you to authenticate your requests without worrying about the underlying OAuth2 process.
You should be using the Java Google Reports API to access activities. Here you can find the Java Quickstart that will help you with the first set up of your Java Application.
Here the Java translation of what you are trying to do, using the Google Reports API:
Reports service = new Reports.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))
.setApplicationName(APPLICATION_NAME)
.build();
String userKey = "all";
String applicationName = "meet";
String eventName = "call_ended";
Activities result = service.activities().list(userKey, applicationName)
.setEventName(eventName)
.setMaxResults(10)
.execute();
Edit:
Be sure to use the last version of the Java API package. You can find the Java API docs here: https://developers.google.com/resources/api-libraries/documentation/admin/reports_v1/java/latest/
If you are using Gradle be sure to have this line in the dependencies parameter.
dependencies {
...
compile 'com.google.apis:google-api-services-admin-reports:reports_v1-rev89-1.25.0'
}
References
OAuth2
Google Reports API
I'm new in Ldap
now I tried changed ldap records' password with c#.
for example;
users in ldap
username:12345678900
password:78900
I can do bellow with help
try
{
searchRoot = new DirectoryEntry(String.Format("LDAP://{0}/{1}",
domainController, container),
adminUser, adminPassword, authenticationTypes);
searcher = new DirectorySearcher(searchRoot);
searcher.Filter = String.Format("sAMAccountName={0}", userName);
searcher.SearchScope = SearchScope.Subtree;
searcher.CacheResults = false;
SearchResult searchResult = searcher.FindOne();
catch (Exception ex)
{
return ex.ToString();
}
how to do I?
I'm not familiar with the C# LDAP API but it looks similar to JNDI, where the SearchResult gives you either a Context or a DN from which you can get a Context via a lookup, and you can then change the attribute via the Context.
I've got a problem with a LightSwitch 2011 web application using forms authentication.
I've implemented my own login screen which authenticates the user against the active directory. My code also checks to see if the user is assigned to a specific active directory group to decide if they can add / edit / delete data.
The login form is placed on the Login.aspx page. The button to login holds the following code:
protected void buttonLogin_Click(object sender, EventArgs e)
{
LdapAuthentication authentication = new LdapAuthentication();
try
{
bool isUserAdmin = false;
if (authentication.IsUserAuthenticated(textBoxUserName.Text, textBoxPassword.Text, ref isUserAdmin))
{
FormsAuthenticationTicket authenticationTicket = new FormsAuthenticationTicket(1,
textBoxUserName.Text, DateTime.Now, DateTime.Now.AddSeconds(1), false, String.Empty);
//Encrypt the ticket.
string encryptedTicket = FormsAuthentication.Encrypt(authenticationTicket);
//Create a cookie, and then add the encrypted ticket to the cookie as data.
HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
//Add the cookie to the outgoing cookies collection.
Response.Cookies.Add(authCookie);
//If the everyoneAdmin is set to true the validation of the administratorgroup
//is decativated so we have to grant the current user administrator rights
if (everyoneAdmin)
isUserAdmin = true;
Session["isUserAdmin"] = isUserAdmin ;
Response.Redirect("default.htm");
}
}
catch (Exception ex)
{
labelError.Text = ex.Message;
labelError.Visible = true;
textBoxPassword.Text = String.Empty;
}
}
public bool IsUserAuthenticated(String userName, String password, ref bool isUserAdmin)
{
if (String.IsNullOrEmpty(userName) || String.IsNullOrEmpty(password))
return false;
String domain = String.Empty;
if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["Domain"]))
domain = Convert.ToString(ConfigurationManager.AppSettings["Domain"]).Trim();
else
throw new NullReferenceException("The Domain in the configuration must not be null!");
String ldpa = String.Empty;
if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["LDPA"]))
ldpa = String.Format("LDAP://{0}", Convert.ToString(ConfigurationManager.AppSettings["LDPA"]).Trim());
else
throw new NullReferenceException("The LDPA in the configuration must not be null!");
String administrationGroup = String.Empty;
if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["AdministratorGroup"]))
administrationGroup = Convert.ToString(ConfigurationManager.AppSettings["AdministratorGroup"]).Trim();
else
throw new NullReferenceException("The AdministrationGroup in the configuration must not be null!");
String domainUserName = String.Format(#"{0}\{1}", domain.Trim(), userName.Trim());
DirectoryEntry directoryEntry = new DirectoryEntry(ldpa, domainUserName, password);
try
{
//Bind to the native AdsObject to force authentication.
object obj = directoryEntry.NativeObject;
DirectorySearcher directorySearcher = new DirectorySearcher(directoryEntry);
directorySearcher.Filter = String.Format("(SAMAccountName={0})", userName.Trim());
directorySearcher.PropertiesToLoad.Add("cn");
directorySearcher.PropertiesToLoad.Add("memberOf");
SearchResult directorySearchResult = directorySearcher.FindOne();
//unable to find a user with the provided data
if (directorySearchResult == null)
return false;
if (directorySearchResult.Properties["memberof"] != null)
{
//If the memberof string contains the specified admin group
for (int i = 0; i < directorySearchResult.Properties["memberof"].Count; i++)
{
string temp = directorySearchResult.Properties["memberof"].ToString();
// get the group name, for example:
if (directorySearchResult.Properties["memberof"].ToString().ToLower().Contains(administrationGroup.ToLower()))
{
isUserAdmin = true;
break;
}
}
}
}
catch (Exception ex)
{
throw new Exception(String.Format("Error authenticating user.\n\rMessage:\n\r {0}", ex.Message));
}
return true;
}
In the class which holds the CanExcecute (server tier) methods I've implemented the following method:
public bool IsCurrentUserAdmin()
{
if (HttpContext.Current.Session["isUserAdmin"] == null)
return false;
return (bool)(HttpContext.Current.Session["isUserAdmin"]);
}
For example, the CanExcecute methods for one table
partial void dtFacilities_CanDelete(ref bool result)
{
result = this.IsCurrentUserAdmin();
}
partial void dtFacilities_CanInsert(ref bool result)
{
result = this.IsCurrentUserAdmin();
}
partial void dtFacilities_CanUpdate(ref bool result)
{
result = this.IsCurrentUserAdmin();
}
WebConfig
<authentication mode="Forms">
<form>s name=".ASPXAUTH"
loginUrl="Login.aspx"
protection="All"
timeout="30"
path="/"
requireSSL="false"
slidingExpiration="true"
defaultUrl="Home.aspx"
cookieless="UseUri" />
</authentication>
<authorization>
<deny users="?">
</deny></authorization>
Problems:
The problem is that if the user is idle for longer than the timeout the session times out. So, the session token isUserAdmin is NULL. At this point I want the application to return to the login screen. A Response.Redirect and a Server.Transfer did not work in the IsCurrentUserAdmin() method. How can I get the application to return the user to the login screen if the session token isUserAdmin is NULL?! Remember, the session token is set in the login.aspx page code behind
When the user closes the final tab of the Lightswitch application, the application opens a new tab and navigates past the login page and they are automatically logged in without processing the login process on the login.aspx page. This means that the session token isUserAdmin is NULL. This happens even if the user has not logged in before they closed the final tab of the application. This leads again to problem 1.
Thanks in advance!
If I understand your problem correctly, if, for whatever reason, isUserAdmin is set to NULL, you want to return the user to to the login screen.
In my application, I simply use a button that the user can click to log off. But the underlying method should work just the same in your case.
First create a new page called LogOff.aspx. The page itself, you can leave default generated code:
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
</div>
</form>
</body>
</html>
For the code behind, you'll want something like this (please check this, I converted from my project which is in VB):
using System.Web.Security;
namespace LightSwitchApplication
{
public partial class LogOff : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
FormsAuthentication.SignOut();
Response.Redirect("default.htm");
}
}
}
This is my code in which I use a button. But if you take the section where the Dispatcher calls Navigate and place it in your IsCurrentUserAdmin() method, it should do the same trick (again, check the C#):
using Microsoft.LightSwitch.Threading;
using System.Windows.Browser;
partial void btnLogOff_Execute()
{
Dispatchers.Main.Invoke(() =>
{
HtmlPage.Window.Navigate(new Uri("LogOff.aspx", UriKind.Relative));
});
}
In my experience, there is a bit of a gotcha in Lightswitch. If you were to execute as is, you would probably receive the following:
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its
dependencies) could have been removed, had its name changed, or is
temporarily unavailable. Please review the following URL and make
sure that it is spelled correctly.
Requested URL: /LogOff.aspx
The fix is this:
First right click your project name in Solution Explorer and Unload Project. Once the project is unloaded, right click it and Edit project_name.lsproj. Ctrl+F for default.htm. You're looking for the section where it is proceeded by _BuildFile. Copy that section from _BuildFile to /_BuildFile, paste below that section and modify as follows.
<_BuildFile Include="Server/LogOff.aspx">
<SubFolder>
</SubFolder>
<PublishType>
</PublishType>
</_BuildFile>
Now right click and Reload your project. If you get errors when trying to build, try Build | Clean and build again. If you run the application in Debug, this code will just reload the page. But once you publish and subsequently cause isUserAdmin to be NULL the code should log you out and take you back to the log on screen.
References:
Original MSDN Forum Thread
My experience implementing it
I'm trying to use Tridion's ContentManagment API to retrieve taxonomy categories and keywords, but I'm running into an Access denied error.
I have the following method:
public Dictionary<string, string> GetKeywords(string tcmUri)
{
var result = new Dictionary<string, string>();
try
{
// _settings.ImpersonationUser = "MYDOMAIN/myusername"
using (var session = new Session(_settings.ImpersonationUser))
{
var category = new Category(new TcmUri(tcmUri), session);
var keywords = category.GetKeywords(new Filter());
if (keywords != null && keywords.Count > 0)
{
foreach (var keyword in keywords)
{
result.Add(keyword.Id.ToString(), keyword.Title);
}
}
}
}
catch (Exception ex)
{
Logger.Log.Error(
"Failed to retrieve keywords for '{0}'.".FormatWith(tcmUri), ex);
}
return result;
}
The user I've got in _settings.ImpersonationUser has access to the Tridion Content Manager, is configured as an administrator, and has been added to Impersonation users in the "SDL Tridion Content Manager configuration" snap-in.
The error I'm getting is the following:
System.Runtime.InteropServices.COMException (0x80040302):
<?xml version="1.0"?>
<tcm:Error xmlns:tcm="http://www.tridion.com/ContentManager/5.0"
ErrorCode="80040302" Category="16" Source="Kernel" Severity="2">
<tcm:Line ErrorCode="80040302" Cause="true" MessageID="16226">
<![CDATA[Access denied for the user MYDOMAIN\myuser.]]
<tcm:Token>MYDOMAIN\myuser</tcm:Token>
</tcm:Line>
<tcm:Details>
<tcm:CallStack>
<tcm:Location>SystemBLST.GetUserContext</tcm:Location>
<tcm:Location>SystemBLST.IBLSecurityST_GetUserContext</tcm:Location>
</tcm:CallStack>
</tcm:Details>
</tcm:Error>
Does anyone have any clues to what I'm doing wrong?
Thanks in advance!
Here's a few things to understand when it comes to impersonation & Tridion...
The user executing the code should not have access to Tridion.
The user executing the code should be configured as a valid "Impersonation User"
The user that the code impersonates should be a valid Tridion user.
If all those 3 conditions are true, impersonation will work.
By executing the code, I mean the Windows account under which the code is being executed. If this account has access to Tridion, you do NOT need to use impersonation.
Hope this helps.
I am facing a problem with an LDAP operation. I want to dynamically add a member to an LDAP group when selected by the user from GUI / browser. I paste the code below which works perfectly well when I run it in a Test class (using com.sun.jndi.ldap.LdapCtxFactory). But, when I package it in my build, deploy on websphere app server 7.0 (using com.ibm.websphere.naming.WsnInitialContextFactory), and invoke this method according to user's selection, then I get the error below. I wonder what's wrong I am doing. Doesn't WAS provide implementation of ldap connection factory? I also tried deploying on WAS with the sun's ldap which otherwise works on the Test class, but I am getting the same exception as below. I'd appreciate if anybody can give a clue.
Problem adding member: javax.naming.OperationNotSupportedException: [LDAP: error code 53 - 00000561: SvcErr: DSID-031A120C, problem 5003 (WILL_NOT_PERFORM), data 0
My Code:
public class LDAPManager
{
String GROUPS_OU = "cn=users,dc=mit,dc=hq,dc=com";
public Boolean addMember(String user, String group)
{
Hashtable env = new Hashtable();
String adminName = "CN=Administrator,CN=Users,DC=mit,DC=hq,DC=com";
String adminPassword = "asdfasdf21Q";
String ldapURL = "ldap://mybox451Dev.mit.hq.com:389";
String userName = "CN="+user+",CN=Users,DC=mit,DC=hq,DC=com";
String groupName = "CN="+group+",CN=Users,DC=mit,DC=hq,DC=com";
//env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.ibm.websphere.naming.WsnInitialContextFactory");
//set security credentials, note using simple cleartext authentication
env.put(Context.SECURITY_AUTHENTICATION,"simple");
env.put(Context.SECURITY_PRINCIPAL,adminName);
env.put(Context.SECURITY_CREDENTIALS,adminPassword);
//connect to my domain controller
env.put(Context.PROVIDER_URL, "ldap://mybox451Dev.mit.hq.com:389");
try {
// Create the initial directory context
InitialDirContext ctx = new InitialDirContext(env);
//Create a LDAP add attribute for the member attribute
ModificationItem mods[] = new ModificationItem[1];
mods[0]= new ModificationItem(DirContext.ADD_ATTRIBUTE, new BasicAttribute("member", userName));
//update the group
ctx.modifyAttributes(groupName,mods);
ctx.close();
//System.out.println("Added " + userName + " to " + groupName);
}
catch (NamingException e) {
System.err.println("Problem adding member: " + e);
}
return true;
}
}
I got it solved. Posting solution here, hope this helps someone.
Use the standard JNDI context of sun, not websphere.
Additional properties I was missing in the hashtable, once I added them, it worked like a charm.
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
//env.put(Context.INITIAL_CONTEXT_FACTORY,"com.ibm.websphere.naming.WsnInitialContextFactory");
//set security credentials, note using simple cleartext authentication
env.put(Context.SECURITY_AUTHENTICATION,"simple");
env.put(Context.SECURITY_PRINCIPAL,adminName);
env.put(Context.SECURITY_CREDENTIALS,adminPassword);
env.put(Context.URL_PKG_PREFIXES, "com.sun.jndi.url");
env.put(Context.REFERRAL, "ignore");
Well, it's been more than a year since this question has been asked; so, I don't know answering will add any value. But, here it is. See WAS Javadocs for details on how what that factory class actually does and how it works. You may need to adjust your jndiprovider.properties file for WAS.