Check group membership for a Linux user using Java - java

Hi I can't figure out how to verify if a user belong to one o more group under Linux os using java 7 nio library.
Can anyone help me about this issue?

You can try to read the file /etc/group.
I have developed a class to easily query this file:
public class UserInfo {
public UserInfo() throws FileNotFoundException, IOException {
this.group2users = new HashMap<>();
FileReader fileReader = new FileReader(groupsFilePath);
BufferedReader groupsReader = new BufferedReader(fileReader);
while(groupsReader.ready())
{
try
{
String line = groupsReader.readLine();
String [] tokens = line.split(":");
String groupName = tokens[0];
Set<String> users = group2users.get(groupName);
if(users == null)
{
users = new HashSet<String>();
group2users.put(groupName, users);
}
if(tokens.length>3)
{
for(String uStr: tokens[3].split(","))
users.add(uStr);
}
} catch (Exception e) { continue; }
}
groupsReader.close();
fileReader.close();
}
public boolean belongs2group(String user, String group)
{
Set<String> groupRef = group2users.get(group);
if(groupRef == null) return false;
return groupRef.contains(user);
}
private String groupsFilePath = "/etc/group";
private Map<String, Set<String>> group2users;
}
This code maps the /etc/group file and keep a map of groups-their users set.
I have developed just one query method (belongs2group) but it is fairly easy to add methods to list all groups and/or all users.
This code is written using the old-fashioned-mainstream java io-api but I think it can be easily adapted to nio. Let me know if you need me to complete that step.

I do not think that reading local /etc/passwd or /etc/group could be good idea, because nis/ldap/ipa/pam can introduce other sources of infromation about group membership.
So, it depends on you environment and some other details. E.g.:
Groups for logged in (current) user
com.sun.security.auth.module.UnixSystem().getGroups()
Hadoop
org.apache.hadoop.security.UserGroupInformation.getBestUGI(null,"root").getGroupNames()
If neither is you case
You can create jna wrapper for getgroups(2).
Or improve UnixSystem and Java_com_sun_security_auth_module_UnixSystem_getUnixInfo from jdk to take user id/name parameter.
Or rewrite some implementation of org.apache.hadoop.security.GroupMappingServiceProvider interface to not depend on hadoop environment.

Related

Java comparing csv file values

I'm trying to create a csv file where only 1 team name is shown per row, so when you click the button twice it will only add the team name if its not already there. currently it adds the team "UWE" every single time you press the button. the code for this is below:
public void showStats(ActionEvent event){
try {
File matchFile = new File("src/sample/matchData.csv");
File scoreFile = new File("src/sample/scoreData.csv");
Scanner matchReader = new Scanner(matchFile);
Scanner scoreReader = new Scanner(scoreFile);
while (matchReader.hasNextLine()) {
String data = matchReader.nextLine();
List<String> matchList = Arrays.asList(data.split(","));
while (scoreReader.hasNextLine()) {
String dataScore = scoreReader.nextLine();
List<String> dataScoreList = Arrays.asList(dataScore.split(","));
if (dataScoreList.get(0).equals(matchList.get(0))) {
//
} else {
writeExcel("scoreData", matchList.get(0)) ;
}
System.out.println(dataScoreList);
}
System.out.println(matchList);
}
matchReader.close();
scoreReader.close();
} catch (FileNotFoundException e) {
System.out.println("An error occurred.");
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
The csv file "matchData" contains:
UWE,KCC,Jin,Julia,Chris,Ryan,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,5,0
The csv file "scoreData" has one empty line in it
You can first go through your source CSV file and put in a map only the lines that contain unique team key....
while (matchReader.hasNextLine()) {
String data = matchReader.nextLine();
String[] record = data.split(",", 2);
Map<String, String> matchList = new TreeMap<>();
matchList.putIfAbsent(record[0], record[1]); // only unique keys are entered.
}
// TODO write to Excel each entry in the map (you don't need to check for unique keys)
Notice that writing to Excel is done after the map is complete. This is the best approach; or at least better than what you showed in your original post. With this approach, you are letting the data structure simplify your process (and no nested loops).
UPDATE:
I forgot to mention that matchList.putIfAbsent(K, V) works with Java 8 and later. If using Java 7 or older (should upgrade Java ASAP), then you will have to do the following:
String value = matchList.get(record[0]);
if (value == null) {
matchList.put(record[0], record[1]);
}
This is because Map#get(K) returns null is no entry is found OR the map allowed for null values to be entered for a given key. Otherwise, it will return the previous value. The new method introduced in Java 8 does this check automatically.

System.getenv() used in spring boot is shown as a security vulnerability in sonarQube

I have method in my spring boot application which takes the data from system environment variable, the method works as intended but sonarQube says "Make sure that environment variables are used safely here",
I tried to find an alternative to fix this issue but I am unable to find a solution, here is the method:
How can I handle this security issue, I cannot use anything other than getting the values from environment variables.
public Map<String, Object> getConfigurations() {
Map<String, Object> result = new HashMap<>();
HttpResponse response = null;
try {
String xVaultToken = System.getenv("XVaultToken");
String cityAppConfig = System.getenv("CityApp_Config");
#SuppressWarnings("deprecation")
HttpClient client = HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier())
.setSslcontext(
new SSLContextBuilder().loadTrustMaterial(null, (x509Certificates, s) -> true).build())
.build();
Map<String, Object> headerDatas = new HashMap<>();
headerDatas.put("Content-Type", "application/json");
headerDatas.put("X-Vault-Token", xVaultToken);
HttpGet get = new HttpGet(cityAppConfig);
Set<String> keys = headerDatas.keySet();
for (String key : keys) {
get.setHeader(key, headerDatas.get(key).toString());
}
response = client.execute(get);
try(BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()))){
String responseData = rd.readLine();
result.put(Constants.RESPONSE, responseData);
}
int statusCode = response.getStatusLine().getStatusCode();
result.put(Constants.STATUS, statusCode);
} catch (Exception e) {
logger.info("error is local settings getConfigurations" + e);
}
return result;
}
}
Disclaimer !!!
First of all, as I mentioned in my comments, if someone requires you to read an ENV-Variable but also says that you can't mark the SonarQube warning as false-positive: Tell them (politely) that they'll have to live with that SonarQube warning.
However, if that is for any reason not an option, I will show you 2 ways that may not trigger the warning in SonarQube which are still much less verbose than calling cmd.exe for that purpose.
Example 1
Call the function via java.util.function.Function
private static String getenv(String variable) {
return ((Function<String, String>) System::getenv).apply(variable);
}
Example 2
Call the function via reflection
private static String getenv(String variable) {
try {
return (String) System.class.getMethod("getenv", String.class).invoke(null, variable);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
All these "security hotspot" warnings are just "TODO" notifications and should be solved in three steps:
Make sure that the value is really used securely.
In this particular case: you are invoking GET request to externally-supplied URL and then use the value from response. What happens when someone changes the environment variable value by some clandestine means and point it to their own web server? Is the response used for anything sensitive? Sometimes even the fact that the URL is invoked can be used for nefarious purposes (tracking). etc etc
Add #SuppressWarnings({"squid:S5304"}) annotation, with proper description in a comment.
Make sure that everything is properly reviewed, including the reasoning from step 1. Remember: Two heads are better than one (especially in security).
Its LOW-level issue can be suppressed using #SuppressWarnings({"squid:S5304"}).
Add this statement on class level.
After searching a lot I figured using environment variables to store important information is a bad idea, but if you no other option like me, here is the workaround:
Instead of using System.getenv("XVaultToken"), use a method like this
private String cmdUrl = "cmd.exe /c echo %wfManagement_Config%";
String cityAppConfig = getEnvironmentVariable(cmdUrl)
private String getEnvironmentVariable(String cmd){
String environmentVar = "";
Process p;
try {
p = Runtime.getRuntime().exec(cmd);
p.waitFor();
BufferedReader reader=new BufferedReader(new InputStreamReader(
p.getInputStream()));
String line;
while((line = reader.readLine()) != null) {
environmentVar = line;
}
return environmentVar;
} catch (Exception e) {
logger.info("Exception-->"+e);
}
return environmentVar;
}
The method runs a command and fetches the environment variable for you, for some reasons sonarQube ignores this and doesn't give a security issue.

Get Geolocation by IP (Spigot 1.8 & 1.13.2)

i Want to find out the geolocation by only providing the ip adress.
My Aim is to save city, country, postal code and other informations.
CraftPlayer cp = (CraftPlayer)p;
String adress = cp.getAddress();
Any short possibilities, to find out by only using ip?
I recommend using http://ip-api.com/docs/api:newline_separated
You can then chose what information you need and create your HTTP-link like:
http://ip-api.com/line/8.8.8.8?fields=49471
The result in this example would be:
success
United States
US
VA
Virginia
Ashburn
20149
America/New_York
So you can create a method in Java to read HTTP and split it at \n to get the lines:
private void whatever(String ip) {
String ipinfo = getHttp("http://ip-api.com/line/" + ip + "?fields=49471");
if (ipinfo == null || !ipinfo.startsWith("success")) {
// TODO: failed
return;
}
String[] lines = ipinfo.split("\n");
// TODO: now you can get the info
String country = lines[1];
/*
...
*/
}
private static String getHttp(String url) {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(new URL(url).openStream()));
String line;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line).append(System.lineSeparator());
}
br.close();
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
just make sure not to create to many querys in a short amount of time since ip-api.com will ban you for it.
There are a lot of websites that provide free databases for IP geolocation.
Examples include:
MaxMind
IP2Location
At the plugin startup you could download one of these databases and then query it locally during runtime.
If you choose do download the .bin format you will have to initialize a local database and then import the data. Otherwise you could just use the csv file with a Java library like opencsv.
From the documentation of opencsv:
For reading, create a bean to harbor the information you want to read,
annotate the bean fields with the opencsv annotations, then do this:
List<MyBean> beans = new CsvToBeanBuilder(FileReader("yourfile.csv"))
.withType(Visitors.class).build().parse();
Link to documentation: http://opencsv.sourceforge.net

How to build a java factory using keys

I am trying to build a command parser which receives a block of data and parses that data into an instance of a specific command. This is basically then a factory whereby the instance returned from the factory is based on a key.
So more specifically, say a receive a block of raw binary data:
0x02 0x23 0x01 0x00 0x00 0x1f
And the 3rd byte in this stream defines the command, I want to create an instance of CommandOne. Obviously there will be additional methods to handle parsing the rest of the data according to the command, but the first step is getting that instance of the command from the command number. This where I say this is a factory using a key.
There are a lot of discussions on how to build factories in java, some very direct, some using various bits of generics and reflections; but I have had no luck finding a specific implementation that fits what I am trying to accomplish. I did however find bits and pieces that relate to what I am trying to accomplish and so I have put those together into an answer which is the first response below. Is this a good response or am I overlooking something simpler or something more complete?
The following is a generic form of the solution. This is all built into a single package containing all the commands and the factory. Note that adding a new command does not require any changes to the factory itself.
This is the abstract class which all commands must implement. It also contains the factory method getInstance.
public abstract class Commands
{
private static Map<Integer,Constructor<?>> commandConstructorMap = null;
private static void loadMap()
{
commandConstructorMap = new HashMap<Integer,Constructor<?>>();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if(classLoader == null) return;
String myPathName = Commands.class.getName();
String myPath[] = myPathName.split("\\.");
if(myPath == null || myPath.length <= 1) return;
int pkgLen = myPath.length - 1;
StringBuilder pkgNm = new StringBuilder();
pkgNm.append(myPath[0]);
for(int i=1;i<pkgLen;i++)
pkgNm.append(".").append(myPath[i]);
String packageName = pkgNm.toString();
String path = packageName.replace('.', '/');
Enumeration<URL> resources;
try
{
resources = classLoader.getResources(path);
}
catch(IOException ioe)
{
return; // failure, just leave constructor empty
}
List<File> dirs = new ArrayList<File>();
while(resources.hasMoreElements())
dirs.add(new File(resources.nextElement().getFile()));
for(File dir:dirs)
{
if(!dir.exists() || !dir.isDirectory()) continue;
File[] files = dir.listFiles();
for(File file:files)
{
if(!file.isFile()) continue;
String fileName = file.getName();
if(!fileName.endsWith(".class")) continue;
try
{
String className = packageName+'.'+fileName.substring(0, fileName.length()-6);
Class<?> clazz = Class.forName(className);
Constructor<?> cons = clazz.getConstructor();
Object instance = cons.newInstance();
if(instance instanceof Commands)
{
commandConstructorMap.put(new Integer(((Commands) instance).getCommand()),cons);
}
}
catch(Exception e){} // do nothing special for exception, just don't add to map
}
}
}
public static Commands getInstance(Integer command) throws Exception
{
if(commandConstructorMap == null)
{
loadMap();
}
if(commandConstructorMap.containsKey(command))
return (Commands)commandConstructorMap.get(cmd).newInstance();
throw new Exception();
}
abstract Integer getCommand();
}
Note the return type of the abstracted getCommand is the same as the type used as the key, this is the relationship conrtol and can be almost any desired object type. One caveat of this mechanism is that this does create and ultimately discard one instance of every command object, but it does this only once, future calls will only create a single instance of the class indicated by the key.
This is an example of one of the command file implementations, obviously there would need to be other code related to building all the specifics of that command, the goal here is just to demonstrate how to build the part of the command class that is utilized in the factory.
public class CommandOne
extends Commands
{
Integer getCommand()
{
return new Integer(1);
}
}
And finally a demonstration of how this works
public static void main(String[] args)
{
try
{
Commands cmd = Commands.getInstance(new Integer(1));
if(cmd instanceof CommandOne)
System.out.println("Command using key 1 is: "+cmd.getClass().getSimpleName());
cmd = Commands.getInstance(new Integer(2));
if(cmd instanceof CommandTwo)
System.out.println("Command using key 2 is: "+cmd.getClass().getSimpleName());
cmd = Commands.getInstance(new Integer(3));
if(cmd instanceof CommandThree)
System.out.println("Command using key 3 is: "+cmd.getClass().getSimpleName());
}
catch(Exception e)
{
System.out.println("Command instantiation failed");
}
}
The results of running this (assuming there are definitions for commands CommandTwo and CommandThree as well as the demonstrated CommandOne) would be something like this:
Command using key 1 is: CommandOne
Command using key 2 is: CommandTwo
Command using key 3 is: CommandThree

how to prevent multiple clicks on an executable jar

I have an executable jar that runs a Java Swing application with an internal SqlLite db.
Users (by mistake) do more than a click on the jar, causing the db lock.
I'd like to prevent this behavior.
What can I do?
thank you very much
You need some kind of synchronization mechanism.
Either you need to code it yourself, or you can create a Java WebStart configuration for your application, where Java WebStart can handle the "only one invocation" through the Single Instance Service (which you must call explicitly in your code).
See http://docs.oracle.com/javase/6/docs/technotes/guides/javaws/developersguide/examples.html#SingleInstanceService for an example.
The first instances accessing the db should acquire a lock of some sort on the db and all further instances should first check if there is already such a lock. If there is one -> "I am not the first, show warning/error, quit.", if there is none "I am the first, get a lock, proceed."
You can use JPS or JNI (need to implement on different platform). The attached is the JPS code to check the Java application instance. You can modify it to more OO.
Using File, Socket or Registry as a lock is not perfect, since there are a lot of chance that a mis-operation can make your application can not start any more (for example, another program occupe the same port)
import java.io.*;
public class TestRun {
public TestRun() {}
public static void main(String args[]) {
String jpsApp = "jps -mlvV";
int count = 0;
try {
Process p = Runtime.getRuntime().exec(jpsApp);
//parser the result to check if TestAPP is running
InputStream is = p.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println();
System.out.println(line);
String[] pair = line.split(" ");
if (pair.length >= 2) {
System.out.println("name is " + pair[1]);
if (pair[1].trim().indexOf("TestRun") > -1) {
count++;
System.out.println("count is " + count);
}
}
}
//it is running, just exit the second instance
if(count>1){
System.out.println("Has run a application!");
return;
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

Categories

Resources