How to accept buddy request properly in Android smack? - java

There is a openfire server and Android clients (smack). All clients can add each other to buddy/roster list (without authorization, I want user can see each other without accept buddy request). I have some problems of getting the Presence information of the buddy request sender.
Assume there are 2 users - User A, User B.
I can add User B to User A's Roster by:
Roster roster = xmppManager.connection.getRoster();
roster.setSubscriptionMode(Roster.SubscriptionMode.accept_all);
roster.createEntry("userB", "userB#abc.com", null);
I can see User B at User A's roster list. Everything is fine so far
There are few problems with User B. I state what is the problem at the code below:
//I have set the available and status of User A by:
//xmppManager.setStatus(available, bundle.getString("new_status"));
...
// That's how I get Roster and Presence of user A
Roster roster = connection.getRoster();
Collection<RosterEntry> entries = roster.getEntries();
for (RosterEntry entry : entries) {
Presence presence = roster.getPresence(entry.getUser());
// User A always not available even I set User A to available
Log.e(TAG, "presence.isAvailable() = " + presence.isAvailable());
// User A's status always empty
Log.e(TAG, "presence.getStatus() = " + presence.getStatus());
// User A's getName() always null
if (entry.getName() != null)
{
name = entry.getName();
}
else
Log.e(TAG, "GetName is null");
}
Do I need to createEntry() at User A? Or do I need to do something with the buddy request like this?
#Override
public void entriesAdded(Collection<String> collection) {
String user = "";
Iterator<String> it = collection.iterator();
if(it.hasNext()){
user=it.next();
}
Presence presence = new Presence(Presence.Type.subscribe);
presence.setTo(user);
connection.sendPacket(presence);
}
But it does not work. It seems that I need to do something to user B first. Any idea is welcome, thanks!

Okay, I toiled hard at this for a couple of days and finally got things working. I have implemented it with a manual subscription mode (ie. user needs to accept another user's request manually). There is no need to create entries yourself as the server handles this automatically according to the presences sent/received.
For your case (automatic subscription), simply send a subscribe and subscribed presence immediately instead of saving the request locally.
This is how it works:
User1 sends subscribe presence to User2.
Roster entry gets automatically created in User1's roster (but not in User2's roster).
User2 receives subscribe request from User1.
User2 sends back a subscribed presence to User2 (User2 > User1 subscription complete).
User2 checks if User1 is in User2's roster. User1 is not in User2's roster. User2 sends back a subscribe presence to User1.
Roster entry gets automatically created in User2's roster.
User1 receives subscribe presence from User2.
User1 checks if User2 is in User1's roster. User2 is in User1's roster. User1 sends back a subscribed presence to User2 (User2 > User1 subscription complete).
final Presence newPresence = (Presence) packet;
final Presence.Type presenceType = newPresence.getType();
final String fromId = newPresence.getFrom();
final RosterEntry newEntry = getRoster().getEntry(fromId);
if (presenceType == Presence.Type.subscribe)
{
//from new user
if (newEntry == null)
{
//save request locally for later accept/reject
//later accept will send back a subscribe & subscribed presence to user with fromId
//or accept immediately by sending back subscribe and unsubscribed right now
}
//from a user that previously accepted your request
else
{
//send back subscribed presence to user with fromId
}
}

Related

How can I get user roles from current session?

After login in my system with Keycloak I want to get the user role-mappings(defined on keycloak admin console) from the user I'm logged in.
I'm able to get the First Name, Last Name, Id, token-id, but when trying to get the roles I get an empty array:
private List<KeycloakOidcProfile> getUserData() {
final PlayWebContext context = new PlayWebContext(ctx(), playSessionStore);
final ProfileManager<KeycloakOidcProfile> profileManager = new ProfileManager(context);
System.out.println("Roles->>>"+ profileManager.get(true).get().getRoles()); //here i get -> []
System.out.println("FirstName->>>"+ profileManager.get(true).get().getFirstName());
System.out.println("Last Name->>>"+ profileManager.get(true).get().getFamilyName());
System.out.println("ID->>>"+ profileManager.get(true).get().getId());
return profileManager.getAll(true);
I believe that, you should implement your own AuthorizationGenerator and attach it to the client (to Keycloak client in this case) in order to map token roles to
KeycloakOidcProfile, but be sure that OIP sends the roles in his token.
There is some glue http://www.pac4j.org/docs/clients.html

Adding roles to a mentioned user in JDA

I want to be able to run a command that begins with !suspend, mentions a user, then determines a time length and adds a role named 'Suspended' to the mentioned user for the specified time length.
Message message = event.getMessage(); //Sets the message equal to the variable type 'Message' so it can be modified within the program.
String content = message.getContentRaw(); //Gets the raw content of the message and sets it equal to a string (best way to convert types Message to String).
MessageChannel channel = event.getChannel(); //Gets the channel the message was sent in (useful for sending error messages).
Guild guild = event.getGuild();
//Role group = content.matches("((Suspended))") ? guild.getRolesByName("Suspended", true).get(0) : null;
if(content.startsWith("!suspend"))//Pretty self explanatory. Enters the loop if the message begins with !suspend.
{
String[] spliced = content.split("\\s+"); //Splits the message into an array based on the spaces in the message.
TextChannel textChannel = event.getGuild().getTextChannelsByName("ranked-ms_punishments",true).get(0); //If there is a channel called ranked-ms_punishments which there should be set the value of it equal to the variable.
int length = spliced.length;//Sets 'length' equal to the number of items in the array.
if(length == 3)//If the number of items in the array is 3 then...
{
if(spliced[1].startsWith("<"))
{
list.add(spliced[1]);
String tempuser = spliced[1];
textChannel.sendMessage(tempuser + " you have been suspended for " + spliced[2] + " days.").queue();//Sends the message in the quotations.
//add role to the member
}
}else {
channel.sendMessage("Please use the following format for suspending a user: '!suspend' <#user> (length)").queue(); //If length doesn't equal 3 then it sends the message in quotations.
}
}
Not sure how to do this, as I'm too unfamiliar with JDA to make it work. I have everything working except for the actual addition of the role named 'Suspended'.
Adding a role to the mentioned members in a message can be done as follows:
Role role = event.getGuild().getRolesByName("Suspended", false).get(0);
List<Member> mentionedMembers = message.getMentionedMembers();
for (Member mentionedMember : mentionedMembers) {
event.getGuild().getController().addRolesToMember(mentionedMember, role).queue();
}
Note that your bot will need the MANAGE_ROLES permission to add roles.
You can do this: 👍
event.getGuild().addRoleToMember(memberId, jda.getRoleById(yourRole));
event.getGuild().addRoleToMember(member, event.getGuild().getRoleById(yourRole)).queue();

Synchronize REST endpoint to wait for 2 calls with same parameter

I am creating an application in which two players should have an opportunity to compete with each other in writing code.
For example, for now one player can initiate a session creation:
#PostMapping("/prepareSession")
public UUID prepareSession(#RequestParam("taskName") String taskName) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String currentPrincipalName = authentication.getName();
User playerOne = userService.findOne(currentPrincipalName);
Task task = taskService.findOne(taskName);
UUID sessionId = UUID.randomUUID();
sessionService.save(new Session(sessionId, playerOne, null, task));
return sessionId;
}
Then, this session id he needs to send to a player who he wants to compete.
And then second player inputs sessionId and gets a task description.
#GetMapping("/connect")
public Task connect(#RequestParam("sessionId") String sessionId) throws InterruptedException {
Session session = sessionService.findOne(sessionId);
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String currentPrincipalName = authentication.getName();
User playerSecond = userService.findOne(currentPrincipalName);
session.setPlayerSecond(playerSecond);
sessionService.save(session);
return session.getTask();
}
I wonder how to make the rest endpoint to wait until both players with same sessionId calls it and then notify them with the task description.
I want them to write code within a one session, with a one code timer.
Please suggest how I should do that
What you are looking for can be achieve like this
You could use a DeferredResult result and store it in a map until a user with the same sessionId joins. Ex
Map<String, DeferredResult<ResponseEntity<?>>> unconnected = new HashMap<String, DeferredResult<ResponseEntity<?>>>();
User one would call the connect prepareSessionAPI to receive the sessionId
User one would then call the connect API. The connet api would store this request/defferred result in the hashmap until user 2 joins. Ex
DeferredResult<Task> unconnectedTask = new DeferredResult<Task>();
unconnected.put(sessionId, unconnectedTask);
Now, user one's request would be stored in an in memory map until user two joins
User one would send the sessionId to user two
User two would call the connect API. The connect API would look for the session in the HashMap, and if it exists, it would perform the operations needed, then set the result of the deferred result. Ex
DeferredResult<Task> unconnectedTask = unconnected.get(sessionId);
if(unconnectedTask != null) {
// Do work
unconnectedTask.setResult(task);
} else {
// This is user one's request
}
Please note, this is pseudo code.
put this code both of method,
please import spring transnational jar
#Transactional(propagation =Propagation.REQUIRED,isolation=Isolation.SERIALIZABLE,readOnly=false,transactionManager="transactionManager")
if any issue inform

Kick out the user automatically when the same account logs in again?

Thanks for all your replies! Here is my code! Please give your comments!
I created a column to save the present status of USER
ShareHelper.USER = nhanVien;//use static variable to save USER
if (ShareHelper.USER.getStatus() == 0) {//first login -> change status from 0 to 1
DialogHelper.alert(this, "Đăng nhập thành công!");
nvd.updateStatus(1, nhanVien.getMaNV());
this.dispose();
EduSys edu = new EduSys();
edu.setVisible(true);
} else if (ShareHelper.USER.getStatus() == 1) { //if someone logged before -> allow to login again
DialogHelper.alert(this, "Đăng nhập thành công!");
EduSys edu = new EduSys();
edu.setVisible(true);
nvd.updateStatus(2, nhanVien.getMaNV());//then update status to 2
this.dispose();
}
And in MainFrame I use a thread to update the status consecutively
Thread t1 = new Thread(new Runnable() {//use Thread to check if status change to 2-> someone logs in with same account
#Override
public void run() {
while (true) {
model.NhanVien nhanVien = nvd.findById(ShareHelper.USER.getMaNV());
ShareHelper.USER = nhanVien;
if (ShareHelper.USER.getStatus() == 2) {
DialogHelper.alert(EduSys.this, "Có người đã đăng nhập tài khoản này!");
ShareHelper.logoff();//log out the present session
nvd.updateStatus(1, ShareHelper.USER.getMaNV());//change status to 1 again
EduSys.this.dispose();
dn.setVisible(true);
}
}
}
});
t1.start();
But I got a trouble! When I log in the second time, I got kicked in both previous session and present session because Status has been changed to 2 by previous session then present session receives that value too. Can someone please help me to fix this! Thank you in advance!
You can have a Simple approach. First create application level simple cache(a simple has map will work) which will maintain the status of the user. If the user Loggs in for first time create entry for the User with the Logged in value (i.e USER -> 1) .and Update the Field accordingly in DB.
Now if the user tries to log in again Check in map for the entry if its already logged-in change the value in map as well as DB and kill all the session of that user
Thanks
TG

Parse Local Datastore: I can't access ACL secured objects from the network after force closing the app? (Android)

After a successful login I'm calling:
ParseUser currentUser = ParseUser.getCurrentUser();
currentUser.isAuthenticated()
Now if I switch to another app via the home button or multitasking and return to my app the currentUser is still authenticated.
But if I force close the app and then reopen it the currentUser is not authenticated. Therefore it seems that I can't access any objects from the network which have the default Access Control List (ACL) added to them via:
Parse.enableLocalDatastore(this);
ParseACL.setDefaultACL(new ParseACL(), true);
Update with sample code:
// Pinning
ParseObject gameScore = new ParseObject("GameScore");
gameScore.put("score", 1337);
gameScore.put("playerName", "Sean Plott");
gameScore.put("cheatMode", false);
gameScore.pinInBackground("NEW_GAMESCORES", null);
// Syncing Local Changes
ParseQuery<ParseObject> localQueryNewScores = ParseQuery
.getQuery("GameScore");
localQueryNewScores.fromPin("NEW_GAMESCORES");
localQueryNewScores.findInBackground(new FindCallback<ParseObject>() {
public void done(List<ParseObject> scores, ParseException e) {
Log.d("score", "New scores = " + scores.size());
for (ParseObject score : scores) {
score.saveInBackground();
score.unpinInBackground("NEW_GAMESCORES", null);
score.pinInBackground("GAMESCORES", null);
}
}
});
// Syncing Network Changes
ParseQuery<ParseObject> networkQueryScores = ParseQuery
.getQuery("GameScore");
// Query for new results from the network.
networkQueryScores.findInBackground(new FindCallback<ParseObject>() {
public void done(final List<ParseObject> scores, ParseException e) {
Log.d("score", "Network scores = " + scores.size());
// Remove the previously cached results.
ParseObject.unpinAllInBackground("GAMESCORES",
new DeleteCallback() {
public void done(ParseException e) {
// Cache the new results.
ParseObject.pinAllInBackground("GAMESCORES",
scores);
}
});
}
});
// Querying the Local Datastore
ParseQuery<ParseObject> localQueryScores = ParseQuery
.getQuery("GameScore");
localQueryScores.fromPin("GAMESCORES");
localQueryScores.findInBackground(new FindCallback<ParseObject>() {
public void done(List<ParseObject> scores, ParseException e) {
Log.d("score", "Local scores = " + scores.size());
}
});
Log output just after I've ran the code several times:
New scores = 2
Local scores = 0
Network scores = 0
New scores = 0
Local scores = 0
Network scores = 2
New scores = 2
Local scores = 2
Network scores = 2
New scores = 1
Local scores = 2
Network scores = 4
Log output just after I've force closed the app:
New scores = 0
Local scores = 4
Network scores = 0
New scores = 2
Local scores = 0
Network scores = 0
As you can see at Network scores = 0 after the force close I am unable to query any results from the network where I // Query for new results from the network to update the pinned objects in the Local Datastore with new results from the network. This happens even though I am constantly connected to the internet after the first login.
But as I need to sync back changes from the network to the Local Datastore I'm depending on this query.
So how can I still query the network for objects that are stored with ACL added to the currentUser, after I force close the app?
Update 2
I found others with the same problem which has been reported here:
developers.facebook.com/bugs/702967266408226
It seems to be a bug in the new Parse Android SDK 1.5. I will update this post as soon as it's clear that my problem was related to the reported bug.
Maybe save password to file, and if u reopen app after force close, log in with this saved password ?
You could save the username and password in a SharedPreferences and log in again when the user re opens your app.
I think you could just check to see whether getCurrentUser is null or not instead of using isAuthenticated.
boolean isLoggedIn = ParseUser.getCurrentUser() != null;
isAuthenticated states:
Whether the ParseUser has been authenticated on this device. This will
be true if the ParseUser was obtained via a logIn or signUp method.
Only an authenticated ParseUser can be saved (with altered attributes)
and deleted.
getCurrentUser is always the user that has signed up or logged in. So if it is not null, the user is logged in. isAuthenticated is just to see if the user object was retrieved from a login/sign up or not. For example, if you queried for other ParseUser objects, they would not be authenticated.
Also notice that when you do a logOut on the current user, getCurrentUser will be null.
I also noticed that if you were not using the Local Datastore, currentUser.isAuthenticated() would always return true even when force closing the application.
It seems that when you are using the Local Datastore, where the current user is retrieved locally, the object is no longer seen as "authenticated" as in the source is not from a login or sign up.
Parse Android Changelog
v1.5.1 — May 30, 2014
Fixed various bugs with Local Datastore.
The problem was indeed a Local Datastore bug which has been fixed today with the release of the new Parse Android SDK version 1.5.1.

Categories

Resources