View Javadoc

1   package fr.in2p3.jsaga.adaptor.security;
2   
3   import java.lang.reflect.Field;
4   import java.util.HashMap;
5   import java.util.Iterator;
6   import java.util.List;
7   import java.util.Map;
8   import java.util.Map.Entry;
9   
10  import org.apache.log4j.Logger;
11  import org.freedesktop.DBus;
12  import org.freedesktop.DBus.Introspectable;
13  import org.freedesktop.DBus.Properties;
14  import org.freedesktop.Secret.Collection;
15  import org.freedesktop.Secret.Item;
16  import org.freedesktop.Secret.Pair;
17  import org.freedesktop.Secret.Service;
18  import org.freedesktop.Secret.Secret;
19  import org.freedesktop.dbus.DBusConnection;
20  import org.freedesktop.dbus.DBusInterface;
21  import org.freedesktop.dbus.exceptions.DBusException;
22  import org.freedesktop.dbus.exceptions.DBusExecutionException;
23  import org.ogf.saga.context.Context;
24  import org.ogf.saga.error.IncorrectStateException;
25  import org.ogf.saga.error.NoSuccessException;
26  import org.ogf.saga.error.TimeoutException;
27  import fr.in2p3.jsaga.adaptor.base.defaults.Default;
28  import fr.in2p3.jsaga.adaptor.base.usage.U;
29  import fr.in2p3.jsaga.adaptor.base.usage.UAnd;
30  import fr.in2p3.jsaga.adaptor.base.usage.UOptional;
31  import fr.in2p3.jsaga.adaptor.base.usage.Usage;
32  import fr.in2p3.jsaga.adaptor.security.impl.UserPassSecurityCredential;
33  
34  /* ***************************************************
35  * *** Centre de Calcul de l'IN2P3 - Lyon (France) ***
36  * ***             http://cc.in2p3.fr/             ***
37  * ***************************************************
38  * File:   SecretServiceSecurityAdaptor
39  * Author: Lionel Schwarz (lionel.schwarz@in2p3.fr)
40  * Date:   24 jan 2012
41  * ***************************************************/
42  
43  public abstract class SecretServiceSecurityAdaptor implements SecurityAdaptor {
44  
45      protected static final String BUS_NAME = "org.freedesktop.secrets";
46      protected static final String ITEM_INTERFACE_NAME = "org.freedesktop.Secret.Item";
47      protected static final String ROOT_OBJECT_PATH = "/org/freedesktop/secrets";
48      protected static final String COLLECTION_OBJECT_PATH = ROOT_OBJECT_PATH + "/collection";
49      protected static final String COLLECTION = "Collection";
50      protected static final String ID = "Id";
51      protected static final String LABEL = "Label";
52  
53      protected abstract String getDefaultCollection();
54      
55      public Class getSecurityCredentialClass() {
56          return UserPassSecurityCredential.class;
57      }
58      
59      public Usage getUsage() {
60          return new UAnd.Builder()
61                           .and(new U(COLLECTION))
62                           .and(new UOptional(ID))
63                           .and(new UOptional(LABEL))
64                           .and(new U(Context.USERID))
65                           .build();
66      }
67  
68      public Default[] getDefaults(Map attributes) throws IncorrectStateException {
69          return new Default[]{
70                  new Default(Context.USERID, System.getProperty("user.name")),
71                  new Default(COLLECTION, getDefaultCollection()),
72          };
73      }
74  
75  
76      public SecurityCredential createSecurityCredential(int usage,
77              Map attributes, String contextId) throws IncorrectStateException, TimeoutException, NoSuccessException {
78          DBusConnection conn;
79          try {
80              //set sys_paths to null: java.library.path will be re-read by JVM before classloader run
81              final Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
82              sysPathsField.setAccessible(true);
83              sysPathsField.set(null, null);
84              
85              try {
86                  conn = DBusConnection.getConnection(DBusConnection.SESSION);
87              } catch (UnsatisfiedLinkError ule) {
88                  throw new NoSuccessException(ule.getMessage() + "; check that java.library.path points to the location /path/to/libmatthew/lib/jni");
89              }
90  
91              String objectPath;
92              Introspectable in;
93              Properties prop;
94              DBusInterface dbusSession=null;
95              String id;
96              String label;
97              Secret secret;
98              
99              objectPath = ROOT_OBJECT_PATH;
100             
101             in = (Introspectable) conn.getRemoteObject(BUS_NAME, objectPath, Introspectable.class);
102             try {
103                 Logger.getLogger(SecretServiceSecurityAdaptor.class).debug(in.Introspect());
104             } catch (DBus.Error.ServiceUnknown su) {
105                 throw new NoSuccessException("Your Gnome keyring or KDE KWallet should be installed and running");
106             }
107             
108             Service serv = (Service) conn.getRemoteObject(BUS_NAME, objectPath, Service.class);
109             // TODO: encrypted ?
110             Pair<org.freedesktop.dbus.Variant,DBusInterface> osr = serv.OpenSession("plain", new org.freedesktop.dbus.Variant(""));
111             dbusSession = osr.b;
112 
113             // First test if the collection exists
114             objectPath = COLLECTION_OBJECT_PATH + "/" + (String) attributes.get(COLLECTION);
115             Logger.getLogger(SecretServiceSecurityAdaptor.class).debug("ObjectPath="+objectPath);
116             in = (Introspectable) conn.getRemoteObject(BUS_NAME, objectPath, Introspectable.class);
117             try {
118                 Logger.getLogger(SecretServiceSecurityAdaptor.class).debug(in.Introspect());
119             } catch (DBusExecutionException dbee) {
120                 if (dbee.getType().equals(org.freedesktop.Secret.Error.NoSuchObject.class.getCanonicalName())) {
121                     throw new NoSuccessException("The collection '" + (String) attributes.get(COLLECTION) + "' does not exist");
122                 }                
123             }
124 
125             // Search item in collection
126             if (attributes.containsKey(ID)) {
127                 id = (String) attributes.get(ID);
128                 objectPath = objectPath + "/" + id;
129                 Logger.getLogger(SecretServiceSecurityAdaptor.class).debug("ObjectPath="+objectPath);
130                 in = (Introspectable) conn.getRemoteObject(BUS_NAME, objectPath, Introspectable.class);
131                 try {
132                     Logger.getLogger(SecretServiceSecurityAdaptor.class).debug(in.Introspect());
133                 } catch (DBusExecutionException dbee) {
134                     if (dbee.getType().equals(org.freedesktop.Secret.Error.NoSuchObject.class.getCanonicalName())) {
135                         throw new NoSuccessException("Found no item with identifier '" + id + "'");
136                     }                
137                 }
138     
139                 prop = (Properties) conn.getRemoteObject(BUS_NAME, objectPath, Properties.class);
140                 Item item = (Item) conn.getRemoteObject(BUS_NAME, objectPath, Item.class);
141                 try {
142                     secret = item.GetSecret(dbusSession);
143                 } catch (DBusExecutionException dbee) {
144                     if (dbee.getType().equals(org.freedesktop.Secret.Error.IsLocked.class.getCanonicalName())) {
145 //                        List<DBusInterface> itemsToUnlock = new java.util.ArrayList<DBusInterface>(1);
146 //                        itemsToUnlock.add(item);
147 //                        Pair<List<DBusInterface>,DBusInterface> usr = serv.Unlock(itemsToUnlock);
148                         throw new NoSuccessException("The item is locked. Please unlock before using it.");
149                     } else {
150                         throw new NoSuccessException(dbee);
151                     }
152                 }
153             } else { // Search by Name: get all secrets from collection
154     
155                 Collection collection = (Collection) conn.getRemoteObject(BUS_NAME, objectPath, Collection.class);
156                 
157                 HashMap<String, String> searchprop = new HashMap<String, String>();
158                 // Get all Attribute whose name starts with "Attribute-" and use for searching
159                 Iterator<?> it = attributes.entrySet().iterator();
160                 while (it.hasNext()) {
161                     Map.Entry attr = (Entry) it.next();
162                     if(attr.getKey().toString().startsWith("Attribute-")) {
163                         String key = attr.getKey().toString().substring("Attribute-".length());
164                         searchprop.put(key, (String)attr.getValue());
165                     }
166                 }
167                 
168                 // Get matching items
169                 Pair<List<DBusInterface>,List<DBusInterface>> itemList = collection.SearchItems(searchprop);
170                 List<DBusInterface> unlockedItems = itemList.a;
171                 List<DBusInterface> lockedItems = itemList.b;
172                 if (lockedItems.size()+unlockedItems.size() == 0) {
173                     throw new NoSuccessException("The collection is empty");
174                 }
175                 secret = null;
176 
177                 // iterate on unlocked items
178                 for (int i=0; i<unlockedItems.size(); i++) {
179                     prop = (Properties)unlockedItems.get(i);
180                     // if JSAGA attribute Label exists, check that it matches
181                     if (attributes.containsKey(LABEL)) {
182                         label = (String) attributes.get(LABEL);
183                         String foundLabel = (String) prop.Get(ITEM_INTERFACE_NAME, LABEL);
184                         Logger.getLogger(SecretServiceSecurityAdaptor.class).debug("found unlocked: " + foundLabel);
185                         if (foundLabel.equals(label)) {
186                             // objects sent by SearchItems do not implement Item, but Properties and Introspectable only
187                             // so we cannot use directly the GetSecret method
188                             // instead we get the objectPath as String and get the remote object that implements Item
189                             secret = getItem(conn,prop).GetSecret(dbusSession);
190                             break;
191                         }
192                     } else {
193                         secret = getItem(conn,prop).GetSecret(dbusSession);
194                         break;
195                     }
196                 }
197                 if (secret==null) {
198                     // check if password is locked
199                     for (int i=0; i<lockedItems.size(); i++) {
200                         if (attributes.containsKey(LABEL)) {
201                             label = (String) attributes.get(LABEL);
202                             prop = (Properties)lockedItems.get(i);
203                             String foundLabel = (String) prop.Get(ITEM_INTERFACE_NAME, LABEL);
204                             Logger.getLogger(SecretServiceSecurityAdaptor.class).debug("found locked: " + foundLabel);
205                             if (foundLabel.equals(label)) {
206                                 // TODO unlock
207                                 // not working
208                                 // FIXME: unlock item: error NotConnected
209 //                                objectPath = prop.toString().split(":")[2];
210 //                                List<DBusInterface> itemsToUnlock = new java.util.ArrayList<DBusInterface>(1);
211 //                                itemsToUnlock.add(getItem(conn,prop));
212 //                                Pair<List<DBusInterface>,DBusInterface> usr = serv.Unlock(itemsToUnlock);
213                                 // FIXME: unlock all items: error NotConnected
214 //                                Pair<List<DBusInterface>,DBusInterface> usr = serv.Unlock(lockedItems);
215                                 // FIXME: unlock collection: error NotConnected
216 //                                List<DBusInterface> collToUnlock = new java.util.ArrayList<DBusInterface>(1);
217 //                                collToUnlock.add(collection);
218 //                                Pair<List<DBusInterface>,DBusInterface> usr = serv.Unlock(collToUnlock);
219                                 // FIXME: Wrong return type: failed to create proxy object for / exported by 1.5
220 //                                Pair<List<DBusInterface>,DBusInterface> usr = serv.Unlock(new ArrayList<DBusInterface>());
221 
222 //                                unlockedItems = usr.a;
223                                 Logger.getLogger(SecretServiceSecurityAdaptor.class).debug(unlockedItems.size());
224                                 throw new NoSuccessException("The item is locked. Please unlock before using it.");
225                             }
226                         }
227                     }
228                     throw new NoSuccessException("No matching passwords");
229                 }
230             }
231             byte[] pw = new byte[secret.value.size()];
232             for (int i=0; i<pw.length; i++) {
233                 pw[i] = secret.value.get(i);
234             }
235             String password = new String(pw);
236             return new UserPassSecurityCredential((String) attributes.get(Context.USERID),password);
237         } catch (DBusException e) {
238             throw new NoSuccessException(e);
239         } catch (SecurityException e) {
240             throw new NoSuccessException(e);
241         } catch (NoSuchFieldException e) {
242             throw new NoSuccessException(e);
243         } catch (IllegalArgumentException e) {
244             throw new NoSuccessException(e);
245         } catch (IllegalAccessException e) {
246             throw new NoSuccessException(e);
247         }
248     }
249 
250     // get the Object path from toString() !!! because there is not getObjectPath() method...
251     // toString = ":busadress+":"+objectpath+":"+iface"
252     private Item getItem(DBusConnection c , Properties p) throws DBusException {
253         return (Item) c.getRemoteObject(BUS_NAME, p.toString().split(":")[2], Item.class);
254     }
255 }