java.nio.file.Files.setOwner() not permitted - java

I am writing an attendance program in Java for use in class; my goal is to be able to have the students download the class file to their accounts (we'll be working on the same network) and then run them with their usernames and a password that will change daily. My program works fine, but I've run into a bug that I can't seem to overcome.
When it's run for the first time in any given day, attend.java will create a new text file in a specified directory on my account and then append the username of whoever ran the program to the file (currently it just appends the first argument of the program to the file, but I hope to modify it so it is more accurate and no one can give a false username), thereby compiling a list of students.
For example, running:
>> java attend desadams cheesecake
will take the given password "cheesecake" and if it matches the day's password either create a new textfile called "(today's date).txt" and write the username "desadams" to it (if it is being run for the first time that day) or else simply append the username "desadams" to the preexisting "(today's date).txt" file.
I wrote my own createFile() method that utilizes the java.nio.file package to create a file with custom permissions:
public static void createFile(File file) throws IOException{
Path newFile = file.toPath();
//create new file and set permissions
/* For the purposes of this program, the new file (the attendance list) must readable and writable to everbody
* in order for this program to work properly when run from their user, because this program both reads and modif\
ies.
*/
Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxrw-rw-");
Files.createFile(newFile);
Files.setPosixFilePermissions(newFile, perms);
//set owner
UserPrincipal owner = newFile.getFileSystem().getUserPrincipalLookupService().lookupPrincipalByName("desadams");
Files.setOwner(newFile, owner);
//set group
GroupPrincipal group = newFile.getFileSystem().getUserPrincipalLookupService().lookupPrincipalByGroupName("studen\
ts");
Files.getFileAttributeView(newFile, PosixFileAttributeView.class).setGroup(group);
}
I could not create the file and set the permissions at the same time, because the umask of the account would get in the way, so I did it in two steps and it works fine.
Now we get to my problem: I can compile and run this program just fine from my own account, but in anticipation of running it from other accounts, I changed the setOwner() method to use an arbitrary username instead of my own to better simulate the conditions of running the program from a different account. And it failed. Attempting to set the owner to anyone other than the person running the program results in an "operation not permitted" error.
I already knew that the UNIX command chown would not work because none of the network accounts have access to the root account, but I didn't expect it to prevent Java from doing the same thing, though I suppose it does make sense.
Is there anyway at all to get around this? The simplest way, of course, would be to run the program myself prior to anyone else each day, so that the attendance file is created in my own username and then I wouldn't have to worry about making sure to set the owner to my username within the program itself; it will always find the attendance file and thus not have to create it with the proper file attributes. However, though that may be exactly what I do, I would like to know if anyone has a solution that suggests how to set the owner within the program itself.
Thanks, and sorry this was a long post.

If the users are sudoers on your system, you could have them do sudo java attend desadams cheesecake. However, I expect they are not sudoers, and therefore your solution where you create the file first is probably the best one.

Related

showing error in java

I want to create an application that shows a user how many times he opened or used the software. For this I have created the code below. But it is not showing correct output: when I run the application first it is showing 1 and then the second time I run it it is also showing 1.
public Founder() {
initComponents();
int c=0;
c++;
jLabel1.setText(""+c);
return;
}
I’m unsure whether I’m helping you or giving you a load of new problems and unanswered questions. The following will store the count of times the class Founder has been constructed in a file called useCount.txt in the program’s working directory (probably the root binary directory, where your .class files are stored). Next time you run the program, it will read the count from the file, add 1 and write the new value back to the file.
static final Path counterFile = FileSystems.getDefault().getPath("useCount.txt");
public Founder() throws IOException {
initComponents();
// read use count from file
int useCount;
if (Files.exists(counterFile)) {
List<String> line = Files.readAllLines(counterFile);
if (line.size() == 1) { // one line in file as expected
useCount = Integer.parseInt(line.get(0));
} else { // not the right file, ignore lines from it
useCount = 0;
}
} else { // program has never run before
useCount = 0;
}
useCount++;
jLabel1.setText(String.valueOf(useCount));
// write new use count back to file
Files.write(counterFile, Arrays.asList(String.valueOf(useCount)));
}
It’s not the most elegant nor robust solution, but it may get you started. If you run the program on another computer, it will not find the file and will start counting over from 0.
When you are running your code the first time, the data related to it will be stored in your system's RAM. Then when you close your application, all the data related to it will be deleted from the RAM (for simplicity let's just assume it will be deleted, although in reality it is a little different).
Now when you are opening your application second time, new data will be stored in the RAM. This new data contains the starting state of your code. So the value of c is set to 0 (c=0).
If you want to remember the data, you have to store it in the permanent storage (your system hard drive for example). But I think you are a beginner. These concepts are pretty advanced. You should do some basic programming practice before trying such things.
Here you need to store it on permanent basic.
Refer properties class to store data permanently: https://docs.oracle.com/javase/7/docs/api/java/util/Properties.html
You can also use data files ex. *.txt, *.csv
Serialization also provide a way for persistent storage.
You can create a class that implements Serializable with a field for each piece of data you want to store. Then you can write the entire class out to a file, and you can read it back in later.Learn about serialization here:https://www.tutorialspoint.com/java/java_serialization.htm

Java simulation of multiple client login

i have written a client Java program supposed to get userid and password , then pass them to an external program to do some processing. Basically doing some I/O. Below is rough sketch of my program.
public class Client{
/* this part gets userid and password*/
....
String userid = <...>
String password = <...>
/* this part do I/O */
cmd = ".... -u userid -p password ..."; //this external command will write some files to disk
ProcessBuilder pb = .......;
pb.start() ; //run external program
....
}
Many users will be using this program so I just want to know, if I don't want performance issues when many users connect at the same time( thereby doing a lot of I/O ), I would need to use JAva threading? How can I change the code to take care of many users. Also on a Windows system, how can i simulate many users , for testing this program. thanks
If I were you, I would replicate your environment into a test environment and in my local environment I would write two test-codes:
a code which would generate many users
another code which would use your code as if there were many users
Now, I would test this algorithm with the mentioned approaches and would measure the response time in the test code. After enough tests I would have an answer whether a single-threaded solution is ok or multi-threading approach should be preferred. Cheers.

Temporary URLs that expire after download

I'd like to create a launcher for a Java game I'm developing that will require the user to log in before the game itself can be downloaded. My idea was to have the launcher send the credentials to my webserver, and the webserver would output the location of a temporary file given the credentials were correct. However, this would be a bit tricky/inefficient, given:
The server would need to copy the game file every time someone updates, and
The webserver wouldn't know when the file was finished downloading.
Perhaps the launcher could send a request to a separate script to delete a file of the given temporary name? The problem with that is that the launcher could easily be decompiled and modified to not send the request, defeating the purpose of creating a new file.
Any suggestions as to this idea and its issues?
I would use a database, like this:
urlgenerator.php
<?php
// generate code
$code = uniqid();
// save code to database
db_save($code);
// write link
echo 'Download';
download.php
<?php
// get code from url
$single_use_code = $_GET['code'];
// check if the code is in the db
if(db_get_code($single_use_code)) {
// remove code from database as it is single use only
db_remove($single_use_code);
// start download
start_download();
} else {
// the code is not valid
die('BAD code');
}
Try something like this:
// Define a random key
$key = 'kgjiowtjiohgjiut09ig90im09yig90mi903i490ti209tgwgt';
$secondsValid = 300;
if($_GET['action'] == 'download')
{
$time = $_GET['time'];
if(time() - $time > $secondsValid)
die('Code has expired, please try again');
if($_GET['validation'] != md5($time.$key))
die('Invalid validation code');
DownloadFile();
die;
}
elseif(CredentialsAreCorrect())
{
$time = time();
header('Location: '.$_SERVER['REQUEST_URI'].'?action=download&time='.$time.'&validation='.md5($time.$key));
die;
}
else
die('Invalid credentials');
This is an easy way to give a validated user a timebombed URL (valid for 5 minutes in this case) without any nasty copying/symlinking/whatever involved, no databases, just using basic facilities that cannot be hacked as long as the key is secure. Just make sure your key has enough entropy (40+ random keypresses should do it) so no rainbow table or brute force attack is feasible.
Simple workaround: on a unix system, you can remove a file while it's in use without affecting currently-open file handles on that file. so
user requests download
script makes a symlink in the documentroot somewhere that points at wherever the file is really stored (somewhere outside of the document root)
URL to the symlink is send out as a parameter to the user.
User clicks on the donwload link, e.g. http://example.com?get=path/of/symlink
The download script fopen()'s the symlink and starts dishing out the file's contents
script REMOVES the symlink after it's been fopen()'d
Now the symlink is gone and can't be reused anymore, but the download script will still be sending data to the user because it opened the symlink/file before it was removed.

Java Create Undeletable File

Is there any method to create a file in java that cannot be deleted.
I have googled it and found processes involving the cmd.
However, I require a pure "java" way that can be done on any platform.
Thanks in advance.
Thank you for your help.
I finally got it right.
I used the following code to deny access to user
public static void main() throws IOException
{
Path file = Paths.get("c:/b.txt");
AclFileAttributeView aclAttr = Files.getFileAttributeView(file, AclFileAttributeView.class);
//System.out.println();
UserPrincipalLookupService upls = file.getFileSystem().getUserPrincipalLookupService();
UserPrincipal user = upls.lookupPrincipalByName(System.getProperty("user.name"));
AclEntry.Builder builder = AclEntry.newBuilder();
builder.setPermissions(EnumSet.of(AclEntryPermission.APPEND_DATA, AclEntryPermission.DELETE, AclEntryPermission.DELETE_CHILD, AclEntryPermission.EXECUTE, AclEntryPermission.READ_ACL, AclEntryPermission.READ_ATTRIBUTES, AclEntryPermission.READ_DATA, AclEntryPermission.READ_NAMED_ATTRS, AclEntryPermission.SYNCHRONIZE, AclEntryPermission.WRITE_ACL, AclEntryPermission.WRITE_ATTRIBUTES, AclEntryPermission.WRITE_DATA, AclEntryPermission.WRITE_NAMED_ATTRS, AclEntryPermission.WRITE_OWNER));
builder.setPrincipal(user);
builder.setType(AclEntryType.DENY);
aclAttr.setAcl(Collections.singletonList(builder.build()));
}
Try the method setPosixFilePermissions() and set the permissions to read only for all the classes of users. Refer this - http://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#setPosixFilePermissions%28java.nio.file.Path,%20java.util.Set%29
If you want to create a file that can't be accidentally overwritten, then look at the various answers to this: How do i programmatically change file permissions?
If you want to create a file that the current program cannot delete at all (but a privileged one could), it might be possible by setting permissions appropriately on the parent directory, or possibly using SELinux Mandatory Access Control cleverness.
If you want to create a truly undeleteable file, then you are out of luck. I am not aware of any operating system that supports creation of files that can never be deleted. It would be an "anti-feature".
I would also agree with #Teifi's comment. Create a file that cannot ever be deleted on the user's machine is not acceptable ... unless done by, or with the authorization of the system's administrators. I would call any software that did that "malicious" too.

Change file owner group under Linux with java.nio.Files

I have a Linux server and I'm running an image resize job in Java for multiple websites on my server. The website files are owned by different OS users/groups. Newly created thumbnails/previews are owned by the user running the resize job. Now I was googleing around how to change the file owner of newly created previews/thumbnails in my resize program and came across this:
java.nio.file.Files.setOwner(Path path, UserPrincipal owner);
This would really solve my problem if it was Windows, but since a Linux file has a user and a group as owner I'm a bit in trouble. Unfortunately given method seems to only change the user ownership of the file. The group ownership remains with the group of the user running my Java resize job.
The websites are owned by different groups, so adding my resize job user to one group is no option. I also want to avoid system calls with ProcessBuilder and execute a chown on my files.
I do need to point out that the created files (preview/thumbnail) can be accessed via the website and it is not mission critical to change the group ownership, but I wanted it to be as clean as possible.
Any suggestions how I can change the group ownership of a file in Linux only using Java?
Thanks Jim Garrison for pointing me in the correct direction. Here the code, which finally solved the problem for me.
Retrieve the group owner of a file
File originalFile = new File("original.jpg"); // just as an example
GroupPrincipal group = Files.readAttributes(originalFile.toPath(), PosixFileAttributes.class, LinkOption.NOFOLLOW_LINKS).group();
Set the group owner of a file
File targetFile = new File("target.jpg");
Files.getFileAttributeView(targetFile.toPath(), PosixFileAttributeView.class, LinkOption.NOFOLLOW_LINKS).setGroup(group);
I missed a complete solution, here it comes (combination of other answers and comments):
Path p = Paths.get("your file's Path");
String group = "GROUP_NAME";
UserPrincipalLookupService lookupService = FileSystems.getDefault()
.getUserPrincipalLookupService();
GroupPrincipal group = lookupService.lookupPrincipalByGroupName(group);
Files.getFileAttributeView(p, PosixFileAttributeView.class,
LinkOption.NOFOLLOW_LINKS).setGroup(group);
Be aware that only the owner of a file can change its group and only to a group he is a member of...
Take a look at the package java.nio.file.attributes and classPosixFilePermissions. This is where you can manipulate group permissions.
The purpose of this response is to elevate one of the comments received in response to the original posting to a full response level, so that it is more prominently displayed.
Here is what worked for us:
// newUser and newGroup are strings.
UserPrincipalLookupService lookupService = FileSystems.getDefault().getUserPrincipalLookupService();
UserPrincipal userPrincipal = lookupService.lookupPrincipalByName(newUser);
GroupPrincipal groupPrincipal = lookupService.lookupPrincipalByGroupName(newgroup);
Files.setAttribute(filePath, "posix:owner", userPrincipal, LinkOption.NOFOLLOW_LINKS);
Files.setAttribute(filePath, "posix:group", groupPrincipal, LinkOption.NOFOLLOW_LINKS);
Additionally we had to run the Java program as a superuser.

Categories

Resources