1717import com .google .api .client .util .Beta ;
1818import com .google .api .client .util .IOUtils ;
1919import com .google .api .client .util .Maps ;
20+ import com .google .api .client .util .Throwables ;
2021
2122import java .io .File ;
2223import java .io .FileInputStream ;
2324import java .io .FileOutputStream ;
2425import java .io .IOException ;
2526import java .io .Serializable ;
27+ import java .lang .reflect .InvocationTargetException ;
28+ import java .lang .reflect .Method ;
2629import java .util .logging .Logger ;
2730
2831/**
2932 * {@link Beta} <br/>
3033 * Thread-safe file implementation of a credential store.
3134 *
35+ * <p>
36+ * For security purposes, the file's permissions are set to be accessible only by the file's owner.
37+ * Note that Java 1.5 does not support manipulating file permissions, and must be done manually or
38+ * using the JNI.
39+ * </p>
40+ *
3241 * @since 1.16
3342 * @author Yaniv Inbar
3443 */
@@ -54,16 +63,7 @@ public FileDataStoreFactory(File dataDirectory) throws IOException {
5463 if (!dataDirectory .exists () && !dataDirectory .mkdirs ()) {
5564 throw new IOException ("unable to create directory: " + dataDirectory );
5665 }
57- // disable access by other users if O/S allows it
58- if (!dataDirectory .setReadable (false , false ) || !dataDirectory .setWritable (false , false )
59- || !dataDirectory .setExecutable (false , false )) {
60- LOGGER .warning ("unable to change permissions for everybody: " + dataDirectory );
61- }
62- // set file permissions to readable and writable by user
63- if (!dataDirectory .setReadable (true ) || !dataDirectory .setWritable (true )
64- || !dataDirectory .setExecutable (true )) {
65- throw new IOException ("unable to set permissions: " + dataDirectory );
66- }
66+ setPermissionsToOwnerOnly (dataDirectory );
6767 }
6868
6969 /** Returns the data directory. */
@@ -117,4 +117,45 @@ public FileDataStoreFactory getDataStoreFactory() {
117117 return (FileDataStoreFactory ) super .getDataStoreFactory ();
118118 }
119119 }
120+
121+ /**
122+ * Attempts to set the given file's permissions such that it can only be read, written, and
123+ * executed by the file's owner.
124+ *
125+ * @param file the file's permissions to modify
126+ * @throws IOException
127+ */
128+ static void setPermissionsToOwnerOnly (File file ) throws IOException {
129+ // Disable access by other users if O/S allows it and set file permissions to readable and
130+ // writable by user. Use reflection since JDK 1.5 will not have these methods
131+ try {
132+ Method setReadable = File .class .getMethod ("setReadable" , boolean .class , boolean .class );
133+ Method setWritable = File .class .getMethod ("setWritable" , boolean .class , boolean .class );
134+ Method setExecutable = File .class .getMethod ("setExecutable" , boolean .class , boolean .class );
135+ if (!(Boolean ) setReadable .invoke (file , false , false )
136+ || !(Boolean ) setWritable .invoke (file , false , false )
137+ || !(Boolean ) setExecutable .invoke (file , false , false )) {
138+ LOGGER .warning ("unable to change permissions for everybody: " + file );
139+ }
140+ if (!(Boolean ) setReadable .invoke (file , true , true )
141+ || !(Boolean ) setWritable .invoke (file , true , true )
142+ || !(Boolean ) setExecutable .invoke (file , true , true )) {
143+ LOGGER .warning ("unable to change permissions for owner: " + file );
144+ }
145+ } catch (InvocationTargetException exception ) {
146+ Throwable cause = exception .getCause ();
147+ Throwables .propagateIfPossible (cause , IOException .class );
148+ // shouldn't reach this point, but just in case...
149+ throw new RuntimeException (cause );
150+ } catch (NoSuchMethodException exception ) {
151+ LOGGER .warning ("Unable to set permissions for " + file
152+ + ", likely because you are running a version of Java prior to 1.6" );
153+ } catch (SecurityException exception ) {
154+ // ignored
155+ } catch (IllegalAccessException exception ) {
156+ // ignored
157+ } catch (IllegalArgumentException exception ) {
158+ // ignored
159+ }
160+ }
120161}
0 commit comments