Tuesday, April 09, 2013

iOS - use the keychain to store your passwords

Now that we are about to write APPs that access sensitive user related data we need to find a way how to store our username and password or in our case the CAS tickets in a secure storage such as the keychain on the mac.

Fortunately Apple provides access to the local keychain on the device and furthermore they provide a sample code how this could be done, but it is not yet ARC-compatible. After some googling I found a "corrected" version on GitHub which I use now in my project.

Using this class storing something in the keychain is quite easy:
KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"MyAppLogin" accessGroup:nil];
[keychainItem setObject:@"Password" forKey:(__bridge id)(kSecValueData)];
[keychainItem setObject:@"Username" forKey:(__bridge id)(kSecAttrAccount)];
and so is reading it.
NSString *password = [keychainItem objectForKey:(__bridge id)(kSecValueData)];
NSString *username = [keychainItem objectForKey:(__bridge id)(kSecAttrAccount)];
If you don't need the information stored anymore or it is expired do some cleanup
[keychainItem resetKeychainItem];
and that's all.

Tuesday, February 12, 2013

CAS - nested group memberships

In order to do the authorisation on the application side the single application needs to know to which groups the user is assigned. In active directory (AD) this groups could be nested, therefore the query could be fairly difficult.

Fortunately the query at the end was not that difficult... at least if you know what you are searching for :-)

member:1.2.840.113556.1.4.1941:={0} 
//where {0} needs to be replaced by distinguishedName in our case

Now we need to integrate this query into our jasig CAS system. The main idea how this can be done comes from this blog, so if you need some more details try to read the post there. The main idea there is to use the spring-securitys DefaultLdapAuthoritiesPopulator to fetch the Roles and fill them into an authorities-list. So I modified it's definition to use our query and some other customisations to fit into my deployerConfigContext.xml

<bean id="ldapAuthoritiesPopulator"
class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
<constructor-arg ref="contextSource" /> 
<constructor-arg value="${ldap.searchBase}" /> 
<property name="groupRoleAttribute" value="cn" />
<property name="groupSearchFilter" value="(member:1.2.840.113556.1.4.1941:={0})" />
<property name="IgnorePartialResultException" value="true" />
<property name="searchSubtree" value="true" />
<property name="rolePrefix" value="" />
<property name="convertToUpperCase" value="false" />
</bean>

So now we need to put this new bean into relation with the others in order to make use of it. As we are working on getting more attributes we need to add it to the bean that's in charge of retrieving all the attributes.

<bean id="attributeRepository"
class="org.jasig.services.persondir.support.ldap.LdapPersonAttributeAndRoleDao">
<property name="ldapTemplate" ref="ldapTemplate" />
<property name="queryTemplate" value="{0}"/>
<property name="baseDN" value="${ldap.searchBase}" />
<property name="requireAllQueryAttributes" value="false" />
<property name="queryAttributeMapping">
<map>
<entry key="username" value="${ldap.username}" />
</map>
</property>
<property name="resultAttributeMapping">
<map>
<entry key="distinguishedName" value="distinguishedName" />
<entry key="member" value="member" />
</map>
</property>
<property name="ldapAuthoritiesPopulator" ref="ldapAuthoritiesPopulator" />
</bean>

That's quite a straight forward definition, except that the used class is not existing, so we need to define it. Basically its just similar to the one suggested in the other blog, with small modification to fit into our system. (The following just contains the modified method)

public class LdapPersonAttributeAndRoleDao extends LdapPersonAttributeDao {

private DefaultLdapAuthoritiesPopulator ldapAuthoritiesPopulator;

private String groupAttributeName = "member";

@Override
protected List getPeopleForQuery(LogicalFilterWrapper queryBuilder, String queryUserName) {
List attribs = super.getPeopleForQuery(queryBuilder, queryUserName);
final List peopleWithRoles = new ArrayList(attribs.size());
Collection authorities = null;
try {
IPersonAttributes person = attribs.get(0);
if (person.getAttributes().get("distinguishedName") != null) {
authorities = ldapAuthoritiesPopulator.getGrantedAuthorities(new DirContextAdapter((String) person
.getAttributes().get("distinguishedName").get(0)), queryUserName);
}
} catch (Exception nnfe) {
logger.error("error looking up authorities", nnfe);
}
List authoritiesList;
if (null != authorities && authorities.size() > 0) {
authoritiesList = new ArrayList();
for (GrantedAuthority auth : authorities) {
authoritiesList.add(auth);
}
for (IPersonAttributes person : attribs) {
Map> attrs = new HashMap>();
attrs.putAll(person.getAttributes());
attrs.put(getGroupAttributeName(), authoritiesList);
peopleWithRoles.add(new CaseInsensitiveAttributeNamedPersonImpl(this.getConfiguredUserNameAttribute(),
attrs));
}
} else {
peopleWithRoles.addAll(attribs);
}
return peopleWithRoles;
}
}

And that's it basically... but as the casServiceValidationSuccess.jsp mentioned in the blog looks simpler than my proposed solution I changed it to that one.

<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
<cas:authenticationSuccess>
<cas:user>${fn:escapeXml(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.id)}</cas:user>
<c:if test="${not empty pgtIou}">
<cas:proxyGrantingTicket>${pgtIou}</cas:proxyGrantingTicket>
</c:if>
<c:if test="${fn:length(assertion.chainedAuthentications) > 1}">
<cas:proxies>
<c:forEach var="proxy" items="${assertion.chainedAuthentications}"
varStatus="loopStatus" begin="0"
end="${fn:length(assertion.chainedAuthentications)-2}" step="1">
<cas:proxy>${fn:escapeXml(proxy.principal.id)}</cas:proxy>
</c:forEach>
</cas:proxies>
</c:if>
<c:if test="${fn:length(assertion.chainedAuthentications) > 0}">
<cas:attributes>
<c:forEach var="auth" items="${assertion.chainedAuthentications}">
<c:forEach var="attr" items="${auth.principal.attributes}" >
<cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attr.value)}</cas:${fn:escapeXml(attr.key)}>
</c:forEach>
</c:forEach>
</cas:attributes>
</c:if>
</cas:authenticationSuccess>
</cas:serviceResponse>

(That's of course not the first modification we implemented into our CAS, so if you don't understand some of the code try to read my other posts, or drop me a comment)

Thursday, January 24, 2013

Add a custom Tab/App to your Facebook Page


Lately we got the task to create a custom Facebook Fan page for our network. It should contain the most important facts about us and be always up to date. Actually nobody in our company has really the time to deal with a fan page, so we had to include our old homepage or at least parts of it. This is possible using custom tabs, which is not really difficult, but as I guess I will need it more often now I want to share my process in a few steps.

Before starting with the tutorial you'll need to create a Facebook page, then you can start with the following steps which show you how to add a tab to your facebook page
  1. Create a Webpage that contains the content you'd like to display on your custom tab
  2. Ensure that this page is accessible under http and https (Facebook requirement)
  3. Go to https://developers.facebook.com/apps
  4. Create a new App; set the Name, contact email, ...
  5. On the App's detail page there is a section called "Select how your app integrates with Facebook"; Open the "Page Tab" and put the Pages name and the two addresses (http and https) (*)
  6. Go to https://www.facebook.com/dialog/pagetab?next=http://facebook.com&app_id=XYZ (exchange XYZ with your actual AppID) and select to which of your Facebook pages you want to add the new Tab-App
  7. Go to the Facebook page and watch your result
(*) We noticed that the URLs are not allowed to contain commas or semicolon, so you need to encode them first ("," = %2C and ";" = %3B)

The best and shortest tutorial I could find on this topic was the following Youtube video
http://www.youtube.com/watch?v=SOpXDxrUm84

Thursday, January 10, 2013

CAS - Login from iOS

The CAS system is designed in a way that the administrator of the server needs to provide a list of services (URLs) that are allowed to use the CAS. This means, that by login in you'll get a TicketGrantingTicket (TGT) which can then be used to create ServiceTickets (ST) to authenticate to a specific service.

This causes some problems when you try to authenticate an APP on a cellular phone which usually does not have a fixed name or IP which could be registered on the CAS server. However, CAS offers the possibility to allow the generation of TGT and ST via a RESTful interface.

As you have probably seen in my previous posts our CAS comes with a set of customisations to fit into our needs of which the integration of a login-domain is probably the most complicated. Introducing such a new, third field for the authentication causes changes throughout the code and as it is needed now to generate TGTs and STs it needs to be implemented in the RESTful interface as well (see my link above for details)

The general usage of CAS's RESTful interface is stated in the CAS documentation, so I'm not going to copy it to this blog, but the interesting thing is its implementation into an iOS application which is not present there.

The general idea on how it can be done comes of course from the CAS documentation and by adapting this article about how to connect Drupal, CAS and iOS. My solution is partially copied from there introducing a newer more powerful API for consuming the RESTful WS and our new Domain field.

Actually the project does just the creation of the TGT and an ST for a sample service and returns the result after executing the "serviceValidate". This means that after this step we know on the iOS-APP exactly who logged in and all other attributes that are provided by the CAS.

What's still missing is a general solution on how this iOS-APP could authenticate itself now to an arbitrary web server like DRUPAL in the post stated above.

The sample project is hosted on github

Thursday, December 20, 2012

UICollectionView prior to iOS6

Recently we had the need of presenting our entries in a kind of gallery in one of our apps. This is not supposed to be a big problem, as in iOS6 the new UICollectionView was introduced, however we were not allowed yet to drop the support for iOS5 so we were basically screwed.

By looking at a solution to this problem i found the PSTCollectionView that was developed by Peter Steinberger for one of his projects, but fortunately he put it open source onto GitHub.

From the functionalities and the look&feel its almost a 1:1 copy of the native UICollectionView but it works all the way down to iOS 4.3, which is great, as we just need it to work on iOS5. The best feature in my opinion is the full interoperability with the native UICollectionView classes and helper classes.

This means, that you're able to use native classes if they are present (so if its running on >iOS6.0) otherwise there is a build in fallback to the custom PSTCollectionView classes. All you need to do in your code (beside providing the library files) is to change the definitions from e.g. UICollectionView to   PSUICollectionView, UICollectionViewDelegate to   PSUICollectionViewDelegate, and so on. The rest is performed in the background.

Installing it into your project is simple as well, mainly if you use CocoaPods, which I'll discuss in a future article.

Thursday, December 13, 2012

Change the SSID on your Telecom Italia WiFi modem/router

If you live in Italy (like me) and your ADSL is provided by Telecom Italia, then you probably got this wireless router as a welcome gift. 
Initially I though that it is, like any other router provided by the phone companies, a castrated version of a badly working modem. Therefore I just put it into its box and away.

However lately my usual router stopped working and as I needed an immediate substitute, that gift came in my mind. When I started configuring it through its loveless GUI I felt confirmed in my aversion against it, however when you're willing to invest a bit more time into it you'll find more and more options present in Netgear or Fritz!Box routers, just badly presented in that awful GUI.

You can have DynDNS, Port-Forwarding, 256-WLAN-encryption, Print server via USB, ... so basically everything you need...

...Except one thing. By default your WLAN is called something like "Telecom-123456" which could be kind of distracting  so that your computers will find 10 or more WLAN with almost the same name.

So, basically I had to change the name of it. Simple you guess? Me too, however there is no option to change the name of the WLAN in that GUI which was really surprising me, but there is a solution to that problem too.

Basically just create a backup of your routers settings using the GUI, which will be saved as something like "AGConfig20121212.kry". That one is an encrypted version of the settings which we are going to decrypt using the following command

openssl aes-128-cbc -K 65316532656263323039373831383630 -nosalt -iv 0 -d -in AGConfig20121212.kry -out AGConfig20121212.xml

In the XML then you can just search for the name of your WLAN and change it to a name you like it to be. After that we need to encrypt the changed file using the following command (note that "-e"(encrypt) now changed to "-d"(decrypt) and the filenames are in reversed order)

openssl aes-128-cbc -K 65316532656263323039373831383630 -nosalt -iv 0 -e -in AGConfig20121212.xml -out AGConfig20121212.kry

Now you can just restore your router with this newly created file (pay attention on your power supply during that action) and provided that you did not change any other values in the file, your router starts up in the same configuration as before just with a new SSID.

Tuesday, September 25, 2012

iOS - Hide settings below tableView

Recently I posted a question on StackOverflow, on how you could possibly hide the settings menu below a tableview and let it be uncover from the top as it is done in the actual AppShopper App for iPhone.


I got some hints on how this could be done, but at the end I had to program the overall solution by myself. I answered my question already there, so you could probably just take the code from there, but as I find it quite interesting, and it could be used also in other projects I prefer to have it here in my blog.

I just put both views on the controller in the storyboard (the views get created in order, so the last in the list will be on top when the controller is shown) one over the other. Then you need to define a trigger for the uncover/cover function, which could be either a button, or a slide gesture or whatever. This trigger then needs to perform the following code

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
[UIView setAnimationCurve: UIViewAnimationCurveEaseOut];
CGRect newFrame = self.newsTableView.frame;
newFrame.origin.y = newFrame.origin.y + self.subview.frame.size.height;
[self.subView1 setFrame:newFrame];
[UIView commitAnimations];

Maybe you need to adjust something else in your case, but more or less you should be done!