So, testing in Play is causing me a headache and I'm hoping someone out there will have an answer to my problem.
I need to test my registration system (among many other things), which of course involves the submission of a form on behalf of the user.
My Stripped Down Version of Controller Action - add()
public static Result add() {
String email = form().bindFromRequest().get("email");
String name = form().bindFromRequest().get("name");
String password = form().bindFromRequest().get("password");
String password_confirm = form().bindFromRequest().get("password-confirm");
[stripped out code]
if(!check){
flash("error", "Not a valid email, please use the email address provided by your employer");
return redirect(
routes.UserController.registration()
);
}
else {
String passwordHash = BCrypt.hashpw(form().bindFromRequest().get("password"), BCrypt.gensalt());
// Create unverified User
User newUser = User.create(
form().bindFromRequest().get("email"),
form().bindFromRequest().get("name"),
passwordHash
);
// Generate verification key
String key = newUser.verification_key;
// Send verification email
sendVerificationLink(key);
flash("success", "Thanks for registering! We have sent you an email with a verification link.");
return redirect(
routes.Application.login()
);
}
Here's the JUnit Test I've written.
#Test
public void registerTest() {
running(fakeApplication(), new Runnable() {
public void run() {
String registeredUserName = "bob";
String registeredUserEmail = "bob#gmail.ac.uk";
String registeredUserPass = "secret";
String registeredUserPassConfirm = "secret";
Map<String, String> userData = new HashMap<String, String>();
userData.put("name", registeredUserName);
userData.put("email", registeredUserEmail);
userData.put("password", registeredUserPass);
userData.put("passwordconfirm", registeredUserPassConfirm);
Result r = callAction(routes.ref.UserController.add(), fakeRequest()
.withFormUrlEncodedBody(Form.form(User.class).bind(userData).data()));
assertEquals(r, 200);
}
});
}
Given suitably correct details within the HashMap, r, in my mind, should return OK or 200?
However, I am getting the following... "expected: play.test.Helpers$1#29cd761a but was:<200>"
What is this "play.test.Helpers$1#29cd761a"? It looks like it referencing an object or memory address but I don't know why??
If this is vague in anyway, please just say so and I'll try to elaborate.
Thanks in advance
Yo!
Sussed it!
For anyone else having a momentary brain fart... use the status() method to read the returned result!
Result r = callAction(routes.ref.UserController.add(), fakeRequest()
.withFormUrlEncodedBody(Form.form(User.class).bind(userData).data()));
assertEquals(200, status(r));
Related
I know that in Java a method can return only one return type... But if there is any possiblity to this, kindly let me know. From the below method I am trying to return a list if condition satisfies else i am trying to return an error message.
Here is my code:
#RequestMapping(value = "/getcompanies", method = RequestMethod.POST)
public List<CompanyMaster> getCompanies(#RequestBody UserDetails user) {
String OrgLoginId = user.getOrgLoginId();
String password = user.getuPassword();
String checkLoginId = null;
String uPassword = null;
String encPassword = null;
String loginId = null;
String checkAuthorized = null;
// String loginId=userService.getLoginId(OrgLoginId);
List<Object[]> CheckIdPassword = userService.checkLoginId(OrgLoginId);
List<Object[]> results = CheckIdPassword;
for (Object[] obj : results) {
checkLoginId = obj[0].toString();
if (null == obj[1]) {
uPassword = "";
} else {
uPassword = obj[1].toString();
}
loginId = obj[2].toString();
}
checkAuthorized = loginId.substring(0, 3);
if (null != password) {
MD5 md5 = new MD5();
encPassword = md5.getPassword(password);
}
if (checkLoginId == null) {
return "Incorrect loginId..Please enter valid loginId";
} else if (encPassword.equals(uPassword)) {
if (checkAuthorized.equals("STE")) {
List<CompanyMaster> companyList = userService.getCompanyList(OrgLoginId);
return companyList;
} else {
return "You are not Authorized";
}
} else {
return "Incorrect Password";
}
Yes its possible, create a custom Exception say 'MyAppException' and throw that exception with the error message you want.
Write your logic in a try{}catch block and throw the exception in catch so that the response has the error message
public List<CompanyMaster> getCompanies(#RequestBody UserDetails user) throws MyAppppException
{
try
{
//your logic which throws error
return companyList;
}
catch( final MyAppException we )
{
throw new MyAppException("User not found", HttpStatus.NOT_FOUND);
}
}
Refer this link
https://www.codejava.net/java-core/exception/how-to-create-custom-exceptions-in-java
You can achieve this by creating a new presenter Class which contains List and status of type String and change the return type of getCompanies method to presenter class like
public CompaniesPresenter getCompanies()
And your CompaniesPresenter class should look like
public class CompaniesPresenter {
private List<CompanyMaster> companyMaster;
private string status;
//default constructor
public CompaniesPresenter(){
}
//parameterized constructor to return only string in exception case
public CompaniesPresenter(Stirng status){
this.status = status;
}
//parametirized constructor to return success case
public CompaniesPresenter(List<CompanyMaster> companyMaster, Stirng status){
this.companyMaster = companyMaster;
this.status = status;
}
//getters and setters
}
This is how your updated method lokks like
#RequestMapping(value = "/getcompanies", method = RequestMethod.POST)
public CompaniesPresenter getCompanies(#RequestBody UserDetails user) {
String OrgLoginId = user.getOrgLoginId();
String password = user.getuPassword();
String checkLoginId = null;
String uPassword = null;
String encPassword = null;
String loginId = null;
String checkAuthorized = null;
// String loginId=userService.getLoginId(OrgLoginId);
List<Object[]> CheckIdPassword = userService.checkLoginId(OrgLoginId);
List<Object[]> results = CheckIdPassword;
for (Object[] obj : results) {
checkLoginId = obj[0].toString();
if (null == obj[1]) {
uPassword = "";
} else {
uPassword = obj[1].toString();
}
loginId = obj[2].toString();
}
checkAuthorized = loginId.substring(0, 3);
if (null != password) {
MD5 md5 = new MD5();
encPassword = md5.getPassword(password);
}
if (checkLoginId == null) {
return new CompaniesPresenter("Incorrect loginId..Please enter valid loginId");
} else if (encPassword.equals(uPassword)) {
if (checkAuthorized.equals("STE")) {
List<CompanyMaster> companyList = userService.getCompanyList(OrgLoginId);
return new CompaniesPresenter(companyList,"success");
} else {
return new CompaniesPresenter("You are not Authorized");
}
} else {
return new CompaniesPresenter("Incorrect Password");
}
This is not tested please make sure for any compilation errors
vavr's Either class would be a good choice.
The usage of custom exception is most reasonable solution. However, creating custom exception for just one case is not ideal always.
Another solution is to return empty List from your method, check if the List is empty in your servlet (or wherever you are invoking this method from), and show error message there.
It seems like you want to return multiple error messages for different cases. In this case, custom exception is recommended solution. If you don't like custom exceptions, you can return List<Object> and populate error message as the first element in the list. In the place where this List is obtained, check if the first element is instanceOf String or CompanyMaster. Based on what it is, you can perform your operations. This is a weird but possible solution (only if you don't like custom exceptions).
You need to understand the problem first. You are mixing two things here, first authorization, does the user has correct privileges to get company details, second giving the company details itself. Let's understand the first problem when a user tries to access "/getcompanies" endpoint will you let him in if does not have access, in REST world your security model should take care of it. I would use spring security to achieve this. My recommendation would be to explore on "interceptor" and solve the problem of invalid user. This will make your other problem easy as your "/getcompanies" endpoint can focus only on getting the details and return it (SRP).
why nothing happened when i put the correct email, but whatevet i put correct or incorrect password the program still not doing anything. It's like the program not checked the password, can you help me ?
This my login.php
<?php
if ($_SERVER['REQUEST_METHOD']=='POST') {
$email = $_POST['email'];
$password = $_POST['password'];
require_once 'connect.php';
$sql = "SELECT * FROM user WHERE email='$email' ";
$response = mysqli_query($conn, $sql);
$result = array();
$result['login'] = array();
if ( mysqli_num_rows($response) === 1 ) {
$row = mysqli_fetch_assoc($response);
if ( password_verify($password, $row['password']) ) { // I Think The Problem At This but i still don't know.
echo $password;
$index['name'] = $row['name'];
$index['email'] = $row['email'];
$index['id'] = $row['id'];
array_push($result['login'], $index);
$result['success'] = "1";
$result['message'] = "success";
echo json_encode($result);
mysqli_close($conn);
} else {
$result['success'] = "0";
$result['message'] = "error";
echo json_encode($result);
mysqli_close($conn);
}
}
}
?>
This my SignInActivity.java // or at this the problem is ?
public class SignInActivity extends AppCompatActivity {
private EditText email,password;
private Button login;
private TextView link_regist;
private static String URL_LOGIN = "https://awalspace.com/app/imbalopunyajangandiganggu/login.php";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sign_in);
email = findViewById(R.id.titEmail);
password = findViewById(R.id.titPassword);
login = findViewById(R.id.btnSignIn);
link_regist = findViewById(R.id.tvToSignUp);
login.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String mEmail = email.getText().toString().trim();
String mPassword = password.getText().toString().trim();
if(!mEmail.isEmpty() || !mPassword.isEmpty())
{
login(mEmail,mPassword);
}
else{
email.setError("Silahkan Masukkan Email");
password.setError("Silahkan Masukkan Password");
}
}
});
}
private void login(final String email, final String password) {
StringRequest stringRequest = new StringRequest(Request.Method.POST, URL_LOGIN,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jsonObject = new JSONObject(response);
String success = jsonObject.getString("success");
JSONArray jsonArray =jsonObject.getJSONArray("login");
if (success.equals("1")){
for (int i = 0; i < jsonArray.length(); i++){
JSONObject object = jsonArray.getJSONObject(i);
String name = object.getString("name").trim();
String email = object.getString("email").trim();
Toast.makeText(SignInActivity.this, "Success Login. \n Your Name : "+name+"\nYour Email : "+email,Toast.LENGTH_SHORT).show();
}
}
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(SignInActivity.this, "Error "+e.toString(),Toast.LENGTH_SHORT).show();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(SignInActivity.this, "Error "+error.toString(),Toast.LENGTH_SHORT).show();
}
})
{
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<>();
params.put("email",email);
params.put("password",password);
return params;
}
};
RequestQueue requestQueue = Volley.newRequestQueue(this);
requestQueue.add(stringRequest);
}
}
Partial answer:
First off
You are open to SQL injection. You should parameterize your query.
Parameterized queries in PHP with MySQL connection
Second
You can add the password to your query so you don't have to do a 2nd check, if you store the passed password in the DB, or you can hash your password first then use it in your query. That avoids getting more user data than necessary (with the associated possible leaking of data) and avoids needing a second method to find the correct user.
This is shown in the link above.
If you store a salt in the DB, I can understand why you need the 2nd method, but you might be able to salt the password in the SQL, via a SQL function. Since you don't include the code for password_verify, we have no way to know what you're actually doing there, so I'm keeping this as basic as I can. (My philosophy is to keep things simple until complications are required.)
Third
Even if you are getting all the columns in that table, specify the column names you need. You might end up adding to that table later, which would cause this query to pull more data than it needs, again.
Fourth
Since you already have the email, which is one of the parameters of the query, you don't need to get it from the DB.
FYI, the link above adds each parameter individually, but mysqli_stmt_bind_param can do them all in one shot.
Object oriented style
mysqli_stmt::bind_param ( string $types , mixed &$var1 [, mixed &$... ] ) : bool
Procedural style
mysqli_stmt_bind_param ( mysqli_stmt $stmt , string $types , mixed &$var1 [, mixed &$... ] ) : bool
...
types
A string that contains one or more characters which specify the types for the corresponding bind variables:
...
https://www.php.net/manual/en/mysqli-stmt.bind-param.php
$stmt = mysqli_prepare($dbc, "SELECT name, id FROM users WHERE email= ? AND password = ?");
mysqli_stmt_bind_param($stmt, "ss", $email, $password); // or use a hash from a method instead of $password
mysqli_stmt_execute($stmt);
$row = mysqli_stmt_fetch($stmt);
This should pull just the one user, unless it doesn't pull any users, so you should have a clear indication of whether this user has access to your site/data/whatever or not. I would suggest sending an actual success message that you can recognize as something a little more specific to you, rather than the generic message you have right now. I understand you're still in testing phase, so it's something to think about later, if you hadn't already.
I would also suggest sending an HTTP 401 message back if $row is null. That way it's 100% guaranteed that your client software understands what happened as well as not giving any specifics as to why it failed. You can still tell the user something more meaningful, such as "Email and Password Combination Not Recognized". Also, don't specify if the email or the password is wrong, since this can lead to easier brute force hacking. There's a lot of contention around this idea of prompts, so I'll let you do your own research and make up your mind about it.
Whether your Java code is correctly sending the login credentials to your PHP server, IDK. I'm rusty on that, so I'll let someone else chime in, and why I'm saying this is a partial answer. At least this answer should get your PHP on the right track.
I want to make an insert operation in an import set table through the web service from ServiceNow with axis2 version 1.6.4
I used wsdl2java with the wsdl file to create classes in my java project.
After it, i created a new class Request on which i would build my soap request to the webservice.
I believe I have 2 (might be more) major problems:
Unserstanding the difference between a stub and a proxy, respectively, the classes ServiceNowSoapStub and ServiceNowSoapProxy and what is the purpose of each of them.
The existing insert method needs a lot of arguments and i wish to make inserts with a selected number of arguments. do i need to add that specific insert method to the architecture?
Here is what I have:
public class Request {
public static void main(String[] args) throws RemoteException {
try{
HttpTransportProperties.Authenticator basicAuthentication = new HttpTransportProperties.Authenticator();
basicAuthentication.setUsername("xxxx");
basicAuthentication.setPassword("xxxx");
ServiceNowSoapStub proxy = new ServiceNowSoapStub();
proxy._setProperty(org.apache.axis2.transport.http.HTTPConstants.CHUNKED, Boolean.FALSE);
proxy._setProperty(org.apache.axis2.transport.http.HTTPConstants.AUTHENTICATE, basicAuthentication);
BigDecimal actuals = new BigDecimal("0.04");
BigInteger appId = new BigInteger("3495766");
String appNonApp = "ApNon";
BigInteger productId = new BigInteger("704217");
BigInteger serviceId = new BigInteger("1537");
String serviceName = "IT";
String bpName = "BNK-RSK";
String method = "N";
String bsCode = "ITDV";
String customerCostCode = "30973250";
String customerCountry = "US";
String customerGroup = "Wealth";
String customerLegalEntity = "HB";
String dsId = "EU56";
BigInteger supplierCostCode = new BigInteger("675136");
String supplierCountry = "UK";
String supplierLegalEntity = "BR";
BigInteger total = new BigInteger ("411");
ServiceNowSoapProxy request = new ServiceNowSoapProxy("https://dev34363.service-now.com/u_it_actuals_import_set");
request.insertTest(actuals, appId, appNonApp, productId, serviceId, serviceName, bpName, method, bsCode, customerCostCode,
customerCountry, customerGroup, customerLegalEntity, dsId, supplierCostCode, supplierCountry, supplierLegalEntity, total);
} catch (org.apache.axis.AxisFault e) {
e.printStackTrace();
}
}
}
What am I doing wrong? or can please anyone refer me to some helpful link?
The wiki page of servicenow addressing this subject is a bit out of date so i can't solve my problem through it.
Thanks!
First of all, I apology for my bad english. I'm brazilian, so if there is any mistakes at the text, please, just disconsidered.
I read a lot of articles here about retrieving the attribute "unicodePwd" in Active Directory, but none of then actually helped me out.
Well, why do I need that information? I'll explain:
I have here some java routines that unify user information from differents systems one to another.
This routines get the information needed in a main Oracle Database and set the information in another Databases (Oracle and MySQL, basically).
For example: We have a private cloud system, that runs in a CentOS Linux OS, that has it own MySQL Database. To unify the users informations, including the users passwords, we get the information from the main Oracle Database and set do this system's MySQL Database, to unify user details and login information.
All the routines that i have here are working and there's no problems, but now we have a new challenge.
We need to do the same unification with ours Active Directory users, getting the information needed in this main Oracle Database and then setting all the information into Active Directory users, including the users passwords.
I already updated the password succesfully in Active Directory users, but I don't want that the password get updated everytime that this java routine runs, but only when the password changes in the main Oracle Database.
Example: When one of the users change the password in the main Oracle Database, the java routine gets this user information to set then in the same user in Active Directory. To do that properly, the routine gets the same information in Active Diretory, then it compares both passwords (Oracle's password and Active Diretory's password) and finally, if the password is different, the routine will update it, but if the password is not different, the routine will do nothing.
That is why i need to retrieve the attribute "unicodePwd" in Active Directory.
Here is some of my code:
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.*;
import org.apache.commons.mail.EmailException;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
public class ldapQuery {
String distinguishedName = "";
String department = "";
String physicalDeliveryOfficeName = "";
String telephoneNumber = "";
String mobile = "";
String title = "";
String sAMAccountName = "";
String unicodePwd = "";
public ldapQuery(String mail) {
try {
final Hashtable<String, String> env = new Hashtable<String, String>();
final String adminName = "CN=MY DOMAIN ADMIN,CN=MY DOMAIN ADMIN FOLDER LOCALIZATION,DC=MY DOMAIN,DC=MY DOMAIN,DC=MY DOMAIN";
final String adminPasswd = "MY DOMAIN ADMIN PASSWORD";
final String ldapUrl = "ldaps://MY ACTIVE DIRECTORY SERVER:636";
final String factory = "com.sun.jndi.ldap.LdapCtxFactory";
final String authType = "simple";
final String protocol = "ssl";
env.put(Context.INITIAL_CONTEXT_FACTORY, factory);
env.put(Context.SECURITY_AUTHENTICATION, authType);
env.put(Context.SECURITY_PRINCIPAL, adminName);
env.put(Context.SECURITY_CREDENTIALS, adminPasswd);
env.put(Context.SECURITY_PROTOCOL, protocol);
env.put(Context.PROVIDER_URL, ldapUrl);
DirContext ctx = new InitialLdapContext (env,null);
SearchControls searchCtls = new SearchControls();
String returnedAtts[] = {"sAMAccountName", "distinguishedName","department", "physicalDeliveryOfficeName", "telephoneNumber", "mobile", "title", "unicodePwd"};
searchCtls.setReturningAttributes(returnedAtts);
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String searchFilter = "(&(objectClass=user)(mail=" + mail +"))";
String searchBase = "DC=MY DOMAIN,DC=MY DOMAIN,DC=MY DOMAIN";
int totalResults = 0;
NamingEnumeration<SearchResult> answer =ctx.search(searchBase, searchFilter, searchCtls);
while (answer.hasMoreElements()) {
SearchResult sr = (SearchResult)answer.next();
totalResults++;
Attributes attrs = sr.getAttributes();
if (attrs != null) {
distinguishedName = (String) attrs.get("distinguishedName").get();
department = (String) attrs.get("department").get();
physicalDeliveryOfficeName = (String) attrs.get("physicalDeliveryOfficeName").get();
telephoneNumber = (String) attrs.get("telephoneNumber").get();
mobile = (String) attrs.get("mobile").get();
title = (String) attrs.get("title").get();
sAMAccountName = (String) attrs.get("sAMAccountName").get();
Attribute passwd = attrs.get("unicodePwd");
unicodePwd = unicodePwd + passwd;
if (department == null) {
department = "";
}
if (physicalDeliveryOfficeName == null) {
physicalDeliveryOfficeName = "";
}
if (telephoneNumber == null) {
telephoneNumber = "";
}
if (mobile == null) {
mobile = "";
}
if (title == null) {
title = "";
}
}
}
}
catch (NamingException e){
System.err.println("FAIL MESSAGE: " + e);
}
}
public String ldapSearchResultDistinguishedName() {
return distinguishedName;
}
public String ldapSearchResultDepartment() {
return department;
}
public String ldapSearchResultPhysicalDeliveryOfficeName() {
return physicalDeliveryOfficeName;
}
public String ldapSearchResultTelephoneNumber() {
return telephoneNumber;
}
public String ldapSearchResultMobile() {
return mobile;
}
public String ldapSearchResultTitle() {
return title;
}
public String ldapSearchResultUnicodePwd() {
return unicodePwd;
}
public String ldapSearchResultSAMAccountName() {
return sAMAccountName;
}
}
After running the code, all the variables return the correct information but the variable "unicodePwd", that returns "null", even though the user has a password.
I know about the byte UTF-16LE thing and that the "unicodePwd" field in Active Directory is encrypted, but, as I explained earlier, i need that information decrypted in a String variable.
Any ideias?
Thank you!
I know this is an old question but I stumbled across it as I was also looking for an answer to the same question. I found the answer and thought it might help anybody else who lands here.
According to Microsoft Documentation it would appear that the unicodePwd attribute is NEVER returned by an LDAP search.
In my case, I need to validate that the credentials received are correct. So my plan is to use the username/password received and create a custom LdapContextFactory on the fly with those credentials. If I can contact the server successfully by doing an LdapContextFactory.get and get back an LdapContext then I can be certain that the password supplied was correct. If you don't get it back then you know it's wrong and can take it from there.
I have entity for reporting. I wonna know who filled up report. I would like to put id of user from session to form class.
I've tried already methods like: bind, fill; but no working solution found.
Ofcorse I mean form class: play.data.Form.form
How can I achive this?
Please help.
Here is my approach (when I wrote this Post):
static Form<Registry> modelForm = form(Registry.class);
Registry registry = new Registry();
registry.creationUser = User.getCurrentUser();
registry.test="tt";
modelForm.fill(registry);
modelForm.bind(data, allowedFields)
My submit method
#Transactional
public static Result submit() {
modelForm = modelForm.bindFromRequest();
if (modelForm.hasErrors()) {
return badRequest(views.html.Registry.form.render(modelForm));
} else {
modelForm.get();
}
registry.creationUser = User.getCurrentUser();
modelForm.fill(registry);
if (modelForm.hasErrors()) {
Logger.debug(modelForm.toString());
return badRequest(views.html.Registry.form.render(modelForm));
} else {
modelForm.get().toDataBase();
toLog("success", "Succefully added new Report");
flash("success", "Pomyślnie dodano.");
return redirect(routes.Index.index());
}
}
Let's say you have a Report model like:
public Date date;
public User user;
public String message;
You need to create and fill an object first (without saving to DB!) and then fill the form with it, like:
Report report = new Report(); // constructors params are welcome here
report.user = loggedUser;
report.date = new Date();
Form<Report> reportForm = Form.form(Report.class).fill(report);
// OR
Form<Report> reportForm = Form.form(Report.class);
reportForm = reportForm().fill(report);
// NOT
Form<Report> reportForm = Form.form(Report.class);
reportForm().fill(report); // Wrong!
return ok(reportCreatingView.render(reportForm));
Edit: You don't need to fill User data at first step, as actually you can add it after binding, you have too much lines in your submit() action, keep it simple :)
public static Result submit() {
User user = User.getCurrentUser();
if (user == null) return unauthorized("You must log in");
modelForm = modelForm.bindFromRequest();
if (modelForm.hasErrors()) {
return badRequest(views.html.Registry.form.render(modelForm));
}
// At this point you have a logged User obj which is not null,
// you have modelForm without errors (checked previously)
// so you need to only add the user to form and save it.
Registry registry = modelForm.get();
registry.creationUser = user;
registry.save();
flash("success", "Twój log został zapisany w bazie.");
return redirect(routes.Index.index());
}