How do I import Active Directory users into JIRA only from specific groups? - active-directory

A caveat to begin with - I don't actually know if what I want to do is possible, particularly because I'm not well versed with LDAP/Active Directory or JIRA.
I'm trying to integrate my shiny new installation of JIRA with my existing active directory. What I want to do is set up some specific JIRA groups (e.g. in London\Security Groups\JIRA*) and then have JIRA only import the users who have membership of those groups. However, in the directory set up page in JIRA, I don't understand how to do this. It seems to indicate that I can import users and groups, but not users from groups.
What am I missing? (apart from expert level knowledge of AD!)
Update
Under my domain, I have an organisational structure like this:
London\Users
London\Security Groups\JIRA
Under the latter organisational unit, I have a security group called "jira-users". The former contains all users.
So far I've tried the following queries and none of them have worked :
(all prefixed with &(objectCategory=Person)(sAMAccountName=*)")
memberof=CN=jira-users,ou=London,ou=Security Groups,ou=JIRA,dc=mycompany,dc=local
memberof=CN=JIRA,ou=London,ou=Security Groups,dc=mycompany,dc=local
(prefixed with just &(objectCategory=Person)")
memberof=CN=jira-users,ou=London,ou=Security Groups,ou=JIRA,dc=mycompany,dc=local
Completed
The query that works is this :
memberof=CN=jira-users,OU=JIRA,OU=Security Groups,OU=London,DC=mycompany,DC=local
I hadn't realised that for a folder structure that is logically, left to right, London\Security Groups\JIRA, the organisational units need to be listed in reverse order.
Further Update
This only works when using the DirectorySearcher class for some reason, e.g.
DirectoryEntry rootEntry = new DirectoryEntry("LDAP://dc=mycompany,dc=local");
DirectorySearcher srch = new DirectorySearcher(rootEntry);
srch.SearchScope = SearchScope.Subtree;
srch.Filter = "(&(objectCategory=Person)(sAMAccountName=*)(memberof=CN=jira-users,ou=London,ou=Security Groups,ou=JIRA,dc=mycompany,dc=local))";
SearchResultCollection results = srch.FindAll();
This doesn't work in the LDAP explorer tool and subsequently, not in JIRA itself.
Last Update
So...for JIRA, you need to reverse the order AND remove the wildcard. Working query in the end is :
(&(objectCategory=Person)(memberof=CN=jira-users,OU=JIRA,OU=Security Groups,OU=London,DC=mycomapny,DC=local))

When you are setting up the user directory look under the User Schema settings. You should see a "User Object Filter" field. In there you should be able to add something like this:
(memberOf=cn=jira-users,ou=London,dc=mydomain,dc=com)
This will allow you to filter based on a specific LDAP group. Of course you will need to edit the values above to reflect your own environment.

Related

Programmatically creating permissions for pages and collections in Wagtail

I'm wondering if anyone has examples of how to create permissions in Wagtail for collections and pages and then give specific users access to that role. I see how to create the Collections and AuthGroups easily, but I don't see how to specify the AuthGroup permissions in detail.
Edit:
In my case, the answer below in addition, to this bit of code for module permissions allowed for everything to be automatically added for a group:
try:
perm = course_group.permissions.get(codename=x.codename, content_type=x.content_type)
except Permission.DoesNotExist:
perm_created = course_group.permissions.add(x)
I have some code like this in a population script. In this example, I want a group named Team to be able to add, change and choose wagtail images and documents in any collection. I also want them to be able to add, edit and publish any pages on the site. This example can hopefully be modified to suit another setup.
from wagtail.models import Page
from wagtail.core.models import Collection, GroupCollectionPermission, GroupPagePermission
from django.contrib.auth.models import Group, Permission
team_group, created = Group.objects.get_or_create(name='Team')
if created:
#This is only done once, when the group didn't already exist
root_collection = Collection.get_first_root_node()
GroupCollectionPermission.objects.create(group=team_group, collection=root_collection, permission=Permission.objects.get(content_type__app_label='wagtailimages', codename='add_image'))
GroupCollectionPermission.objects.create(group=team_group, collection=root_collection, permission=Permission.objects.get(content_type__app_label='wagtailimages', codename='change_image'))
GroupCollectionPermission.objects.create(group=team_group, collection=root_collection, permission=Permission.objects.get(content_type__app_label='wagtailimages', codename='choose_image'))
GroupCollectionPermission.objects.create(group=team_group, collection=root_collection, permission=Permission.objects.get(content_type__app_label='wagtaildocs', codename='add_document'))
GroupCollectionPermission.objects.create(group=team_group, collection=root_collection, permission=Permission.objects.get(content_type__app_label='wagtaildocs', codename='change_document'))
GroupCollectionPermission.objects.create(group=team_group, collection=root_collection, permission=Permission.objects.get(content_type__app_label='wagtaildocs', codename='choose_document'))
root_page = Page.objects.get(id=1)
GroupPagePermission.objects.create(group=team_group, page=root_page, permission_type='add')
GroupPagePermission.objects.create(group=team_group, page=root_page, permission_type='edit')
GroupPagePermission.objects.create(group=team_group, page=root_page, permission_type='publish')
To find the available wagtail content types and list their identifiers, I ran
for x in Permission.objects.order_by().values('content_type__app_label').distinct():
print(x['content_type__app_label'])
To find the permission codenames of a given content type, I ran
for x in Permission.objects.filter(content_type__app_label='wagtailimages'):
print(x.codename)
For the GroupPagePermission parameter permission_type, I found the options in the wagtail source. It lists these options:
PAGE_PERMISSION_TYPES = [
('add', _("Add"), _("Add/edit pages you own")),
('edit', _("Edit"), _("Edit any page")),
('publish', _("Publish"), _("Publish any page")),
('bulk_delete', _("Bulk delete"), _("Delete pages with children")),
('lock', _("Lock"), _("Lock/unlock pages you've locked")),
('unlock', _("Unlock"), _("Unlock any page")),
]
In my project I'm not adding users to groups programmatically, but hopefully this answer helps

How do you check in code if a request matches an EPiServer Visitor Group

We've set up a new "visitor group" in EPiServer 6r2, and we want to add a css class to the <body> tag of the site if the user is in that group, so different groups get different site designs. I'm trying to find out if the current visitor is in a matching group in the code-behind of a masterpage file in order to add this extra class and can't get the below code to return anything but false.
I'm not sure if the role name mentioned is the name you enter in the CMS UI when adding a visitor group.
Paul Smith blogged a proposed solution to this but I haven't been able to get it to return anything but false yet, and judging by the only comment on the blog article I'm not alone. Code sample #1 from this link (which is the one I'm using):
using EPiServer.Personalization.VisitorGroups;
...
bool match = EPiServer.Security.PrincipalInfo.CurrentPrincipal
.IsInRole("My Visitor Group", SecurityEntityType.VisitorGroup);
I found the developer guide to membership and role providers which states that replacePrincipal must be set to true for the correct principal to be in place. I checked and this is already the case for my config.
Documentation
EPiServer 7 doc
IPrincipal.IsInRole() extension
SecurityEntityType enum
Oddly I searched the 6r2 documentation from http://sdk.episerver.com/ and can't find the documentation for IPrincipalExtensions at all, even though I see the class in object browser in 6.2. in my sln. Details: Assembly EPiServer.ApplicationModules - C:\Windows\assembly\GAC_MSIL\EPiServer.ApplicationModules\6.2.267.1__8fe83dea738b45b7\EPiServer.ApplicationModules.dll - public static bool IsInRole(this System.Security.Principal.IPrincipal principal, string role, EPiServer.Security.SecurityEntityType type)
Member of EPiServer.Personalization.VisitorGroups.IPrinicipalExtensions
Please comment if you spot errors or I've missed anything as coding for EPiServer is a bit of a fog-of-war affair and I'm a little battle-weary.
Found it by browsing the object model and guessing. So much for documentation.
using EPiServer.Personalization.VisitorGroups;
using EPiServer.Security;
const string visitorGroupName = "Some users";
var groupHelper = new VisitorGroupHelper();
bool isPrincipalInGroup = groupHelper.IsPrincipalInGroup(
PrincipalInfo.CurrentPrincipal, visitorGroupName);
Tested and working in EPiServer 6r2 (aka 6.1).
String visitorGroupName must match the string entered into the "Name" box on the EPiServer admin interface when creating / editing the visitor group. See screenshot below:

Trouble using dbforge with PyroCMS (CI based CMS)

I have been using PyroCMS and CI for quite some time, and truly love it.
I am extending a DB module that will allow an admin user to manage a DB without having to use something like phpMyAdmin.
The only thing I have been able to get working however is Browsing a table's field values (i.e 'SELECT * FROM 'table_name').
I want to include more functions, but I can't seem to get dbforge to work properly. I know it is loaded because dbforge is used to uninstall modules. I also get no error when calling functions from it.
Here is an example of my code from the controller (dbforge has already been loaded).
public function drop($table_name)
{
$table_name = $this->uri->segment(4);
$this->dbforge->drop_table($table_name);
redirect('admin/database/tables');
}
Lets say the function gets called from this url:
.../admin/database/drop/table_name
It appears to work... but instead it just redirects to the tables overview.
Is there something I am missing? Shouldn't [$this->dbforge->drop_table($table_name);] always drop a table (given $table_name is valid)?
EDIT
As a work around, I was able to use:
public function drop($table_name)
{
$table_name = $this->uri->segment(4);
//$this->dbforge->drop_table($table_name);
$this->db->query("DROP TABLE ".$table_name);
redirect('admin/database/tables');
return TRUE;
}
I really would like to use DB forge, however...
I think you might be getting a little confused by the site prefixes in PyroCMS 1.3.x.
By default all installations of Community and Professional will have default_ as a prefix for all tables in the first site. If you have Professional you can add new sites and the site reference will be whatever_ instead of default_
This prefix is accounted for by dbforge, so when you want to delete default_blog you would just delete:
/admin/database/drop/blog
Also, why are you accepting the $table_name as an argument then overriding it with a uri segment?
Also, why are you accepting the $table_name as an argument then overriding it with a uri segment?
See what I did there? xD
public function drop($table_name)
{
$this->dbforge->drop_table($table_name);
redirect('admin/database/tables');
}

How to get LDAP unboundid AttributeSyntax?

I'm trying to find out the unboundid AttributeSyntax type for a specific attribute name and it's simply not working.
Here's the example test code that I'm using to achieve this:
#Test
public void testLDAPSchema() {
try {
LDAPConnection connection = new LDAPConnection();
connection.connect("hessmain", 389);
connection.bind("CN=Administrator,CN=Users,DC=FISHBOWL,DC=NET", "password");
Schema s = connection.getSchema();
System.out.println(s.toString());
AttributeTypeDefinition atd = s.getAttributeType("directReports");
Set<AttributeTypeDefinition> oat = s.getOperationalAttributeTypes();
Set<AttributeSyntaxDefinition> l = s.getAttributeSyntaxes();
AttributeSyntaxDefinition asd1 = s.getAttributeSyntax(atd.getOID());
AttributeSyntaxDefinition asd2 = s.getAttributeSyntax(atd.getSyntaxOID());
AttributeSyntaxDefinition asd3 = s.getAttributeSyntax(atd.getBaseSyntaxOID());
connection.close();
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
From the above code, all the sets are empty. This also means that no matter which OID I pass to the schema getAttributeSyntax method that I will simply get a null return.
Is there any reason why I can't get the attribute syntaxes from an Active Directory server schema?
Thanks
I don't think that this is specific to the UnboundID LDAP SDK for Java. I'm not sure that Active Directory exposes this information over LDAP. When I perform a general LDAP search to retrieve schema information, I can see the attributeTypes and objectClasses attributes, but ldapSyntaxes isn't returned (and in fact ldapSyntaxes doesn't appear in the list of attribute types).
Similarly, none of the attribute type definitions includes a USAGE element, which is what is used to indicate that the attribute type is operational (e.g., "USAGE directoryOperation").
It may well be that Active Directory simply doesn't report this information at all. It could be that it provides some other non-standard way to get this information (e.g., a control or extended operation, or some other entry that can be retrieved), but if there is then I don't know about it.

Resolve domain address in/for Active Directory [.net]

Is there a simple way to resolve the Active Directory path of a Domain name in Active Directory?
E.g. your user account might be SITE\Username or DEPARTMENT\Username but the actual path of the domain SITE might be site.company.com or DEPARTMENT might be dep.company.com etc
I'm trying to find a way of turning DEPARTMENT into DC=dep,DC=company,DC=com etc for searching correctly in Active Directory...
What you probably have is a company with one forrest (company.com) and a couple of child domains (SITE and DEPARTMENT in your case). The SITE- and DEPARTMENT-bits are the NetBIOS representations of the domains. Is not very common that they differ from the corresponding DNS names but it is possible. Just make sure we're not talking about OU:s and "physical" Active Directory sites.
Assuming the above, here are a couple of options:
The following assumes that your application is running under an account with "forrest-wide read access" (the ability to access your different domains):
using System.DirectoryServices.ActiveDirectory;
// ...
DirectoryContext directoryContext
= new DirectoryContext(DirectoryContextType.Domain, "DEPARTMENT");
Domain domain = Domain.GetDomain(directoryContext);
String domainName = domain.Name;
String domainComponent = "DC=" + domainName.Replace(".", ",DC=");
Console.WriteLine(domainComponent);
(I haven't found a "System.DirectoryServices-built-in" way to transform domain.company.com to DC=domain,DC=company,DC=com but the simple string manipulation above should do the trick.)
If you're using a non-forrest-wide account (or if DEPARTMENT and SITE are not domains in the same forrest but in themselves separate forrests), then you'll have to maintain a list of usernames and passwords to use depending on the "DEPARTMENT" or "SITE"-strings:
// if(domainNetBios == "DEPARMENT")...
DirectoryContext directoryContext
= new DirectoryContext(DirectoryContextType.Domain,
"DEPARTMENT",
"UserInDEPARTMENT",
"PassForUserInDEPARTMENT");
If you're not willing to bind to the different forrests/domains to get the domain name/component you could try something along the lines of:
IPAddress[] addresses = Dns.GetHostAddresses("DEPARTMENT");
IPHostEntry host = Dns.GetHostEntry(addresses[0]);
Int32 dotIndex = host.HostName.IndexOf('.');
String domain =
host.HostName.Substring(dotIndex + 1, host.HostName.Length - dotIndex - 1);
Console.WriteLine(domain);
But the above assumes that the NETBios-name is the same as the first part of the DNS-name and that DNS resolution is working correctly. What we're doing above is to query for a list of domain controllers then removing the hostnames from the DNS-names. Not a particularly clean option...

Resources