Introduction
In this tutorial, we will focus on how to manage IO permissions to read, write, and delete local files using the SecurityManager
class.
Goals
At the end of this tutorial, you would have learned:
- What policy files are and how to use them.
- How to use policy files and the
SecurityManager
class to manage IO permissions.
Prerequisite Knowledge
- Basic Java.
- Java IO/NIO Concepts.
Tools Required
- A Java IDE that supports JDK 11 (I prefer NIO2/Path over older IO/File).
Concept Overview
The Java class SecurityManager
can be used to limit the permissions of a program to perform tasks in these categories: File, Socket, Net, Security, Runtime, Property, AWT, Reflect, and Serializable. The focus of this tutorial would only be on File read, write, and delete permissions.
By default, a SecurityManager
is not provided in the runtime environment. In order to use the SecurityManager
, one must either set the runtime flag java.security.manager
on the command line or provide an instance of SecurityManager
in code. Our example will use the latter approach, which will create an instance of SecurityManager
manually.
It is possible to extend the SecurityManager
class, but that is out of scope of this tutorial, so the instance of SecurityManager
that we will create later will be of the default implementation.
The java.policy File
When the SecurityManager
instance is loaded, it will automatically look for java.policy
files located at the paths below:
java.home/lib/security/java.policy (Solaris/Linux)
java.home\lib\security\java.policy (Windows)
user.home/.java.policy (Solaris/Linux)
user.home\.java.policy (Windows)
In case you are unaware, java.home
and user.home
are environment variables that can be retrieved with the System#getProperty
Java method. For this tutorial, we will create a java.policy
file at the user.home
location (on Windows).
Go ahead and create a blank file java.policy
under your user home directory. For example, if my username is John, then the java.policy
file would be located at:
C:\Users\John\java.policy
The grant keyword
Copy and paste the text below into the java.policy
file.
grant {
//permission java.io.FilePermission "c:/ioPractice/test.txt", "read";
//permission java.io.FilePermission "c:/ioPractice/test.txt", "write";
//permission java.io.FilePermission "c:/ioPractice/test.txt", "delete";
//permission java.io.FilePermission "c:/ioPractice/test.txt", "read, write, delete";
};
To allow read, write, or delete permissions for a file, we can use the grant
keyword with the syntax above.
Note that all of the permissions are commented out at this stage. You can either combine all of the permissions together or add them separately.
Setting up the Test environment
To see how the SecurityManger
works, we need to perform the steps below to set up our test environment.
-
Create a folder called
IOPractice
in theC:\
Drive (or/
if you are on Linux/Mac). -
Inside
IOPractice
, create a file calledtest.txt
. -
Add the text “
Hello World!
” (without quotes) intotest.txt
. -
The path to your
test.txt
file should look like thisC:\IOPractice\test.txt
-
Create a new Java project.
-
Create a new package
com.example
. -
Create a new Java class
Entry.java
inside thecom.example
package. -
Copy and paste the code below into
Entry.java
.package com.example; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; public class Entry { private static final SecurityManager SECURITY_MANAGER = new SecurityManager(); //1 static { //System.setSecurityManager(SECURITY_MANAGER); //2 } public static void main(String[] args) { Path testFile = Path.of("C:\\ioPractice\\test.txt"); //3 readFile(testFile); //4 writeFile(testFile); //5 deleteFile(testFile); //6 } private static void readFile(Path file) { //7 try { String content = Files.readString(file); System.out.println(content); } catch (IOException e) { e.printStackTrace(); } } private static void writeFile(Path file) { //8 try { Files.writeString(file, "Hello World Again!", StandardOpenOption.APPEND); readFile(file); } catch (IOException e) { e.printStackTrace(); } } private static void deleteFile(Path file) { //9 try { Files.delete(file); } catch (IOException e) { e.printStackTrace(); } } }
Here are the explanations for the code above:
- Line 1 is where we instantiate a
SecurityManager
object using the default implementation. - But creating a
SecurityManager
object is not enough for the program to use it, we also have to set it manually at line 2, but that is commented out for now to show you the effect later. - At line 3, we create a
Path
object with an absolute path to thetest.txt
file. - Lines 4, 5, 6 are call sites for the 3 convenient methods
readFile
,writeFile
, anddeleteFile
. - Lines 7, 8, 9 are where the convenient methods were created.
Test the code
Because the SecurityManager
has not been set yet, when we execute the code above, everything will run without any exception(but you must re-create the test.txt
file each time because it would be deleted on a successful run).
Now uncomment out line 2, and we would see the code failing to run, throwing this exception:
Exception in thread "main" java.security.AccessControlException: access denied ("java.io.FilePermission" "C:\ioPractice\test.txt" "read")
If you uncomment the very first permission, read, in the java.policy
file and save, you can see that your program is able to read the file content, but it is still unable to write to the file, so we would see an exception regarding the write permission:
Exception in thread "main" java.security.AccessControlException: access denied ("java.io.FilePermission" "C:\ioPractice\test.txt" "write")
Once again, after we uncomment the write permission in the file, our program can now write to the file successfully, but obviously we will still run into an exception with the delete permission.
Exception in thread "main" java.security.AccessControlException: access denied ("java.io.FilePermission" "C:\ioPractice\test.txt" "delete")
Uncomment the delete permission in java.policy
, and all methods will have permission to run successfully.
Solution Code
package com.example;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
public class Entry {
private static final SecurityManager SECURITY_MANAGER = new SecurityManager(); //1
static {
System.setSecurityManager(SECURITY_MANAGER); //2
}
public static void main(String[] args) {
Path testFile = Path.of("C:\\ioPractice\\test.txt"); //3
readFile(testFile); //4
writeFile(testFile); //5
deleteFile(testFile); //6
}
private static void readFile(Path file) { //7
try {
String content = Files.readString(file);
System.out.println(content);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void writeFile(Path file) { //8
try {
Files.writeString(file, "Hello World Again!", StandardOpenOption.APPEND);
readFile(file);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void deleteFile(Path file) { //9
try {
Files.delete(file);
} catch (IOException e) {
e.printStackTrace();
}
}
}
java.policy
grant {
permission java.io.FilePermission "c:/ioPractice/test.txt", "read";
permission java.io.FilePermission "c:/ioPractice/test.txt", "write";
permission java.io.FilePermission "c:/ioPractice/test.txt", "delete";
//permission java.io.FilePermission "c:/ioPractice/test.txt", "read, write, delete";
};
Summary
We have successfully learned how to enable the SecurityManager
and manage IO permissions using the java.policy
file.
Even though we have this capability, be aware that SecurityManager
usage should be reserved as a last resort to secure your APIs. Your APIs should be designed for security in the first place.
The full project code can be downloaded here: https://github.com/dmitrilc/DaniWebJavaIoSecurityManager