Currently upgrading play framework to 2.7.* I'm getting an error response due to deprecation in the security authenticator class of HTTP.Context class
The application was on Play 2.6.* and auth was working as designed. If I roll back to 2.6.* the authentication works well. Essentially I'm hoping to return the auth token as a String.
#Override
public String getUsername(Http.Request ctx) {
Optional token = getTokenFromHeader(ctx);
if(token.isPresent()){
UserAccount userAccount = UserAccount.find.query().where().eq("authtoken",token.toString()).findOne();
if (userAccount != null){
//ctx.args.put("userAccount", userAccount);
//String resp = Optional.<String>ofNullable(null).orElse(orelesMethod());
String resp = Optional.<String>ofNullable(null).orElse(userAccount.authtoken);
return resp;
}
}
return null;
}
#Override
public Result onUnauthorized(Http.Request ctx) {
Logger.info("onUnauthorized");
ObjectNode result = Json.newObject();
result.put("error","Unauthorized, Please login");
return status(401,result);
}
private Optional getTokenFromHeader(Http.Request ctx) {
return ctx.header("X-AUTH-TOKEN");
}
}
Original Code is as below
public class Secured extends Security.Authenticator{
#Override
public String getUsername(Http.Context ctx) {
String token = getTokenFromHeader(ctx);
if(token != null){
UserAccount userAccount = UserAccount.find.query().where().eq("authtoken",token).findOne();
if (userAccount != null){
ctx.args.put("userAccount", userAccount);
return userAccount.authtoken;
}
}
return null;
}
#Override
public Result onUnauthorized(Http.Context ctx) {
Logger.info("onUnauthorized");
ObjectNode result = Json.newObject();
result.put("error","Unauthorized, Please login");
return status(401,result);
}
private String getTokenFromHeader(Http.Context ctx) {
String[] authTokenHeaderValues = ctx.request().headers().get("X-AUTH-TOKEN");
if ((authTokenHeaderValues != null) && (authTokenHeaderValues.length == 1) && (authTokenHeaderValues[0] != null)) {
return authTokenHeaderValues[0];
}
return null;
}
}
Error response
return type java.lang.String is not compatible with java.util.Optional<java.lang.String>
Play 2.7 has some changes in Security.Authenticator class. Now it has two methods named getUsername.
You override method with Request param, so you should return Optional not String.
Take a look on Authenticator code:
/**
* Handles authentication.
*/
public static class Authenticator extends Results {
#Deprecated
public String getUsername(Context ctx) {
return ctx.session().get("username");
}
// You override this method
public Optional<String> getUsername(Request req) {
return req.session().getOptional("username");
}
...
}
Related
I want to use this webflux client code to send POST requests with reply and without reply. I tried this code implementation:
public class RestClientBuilder {
private String token;
private String username;
private String password;
private URL gatewayUrl;
private SslContextBuilder sslContextBuilder;
public static RestClientBuilder builder() {
return new RestClientBuilder();
}
public RestClientBuilder token(String token) {
this.token = validateAndTrim(token, "Token");
return this;
}
public RestClientBuilder usernamePassword(String username, String password) {
this.username = validateAndTrim(username, "Username");
this.password = validateAndTrim(password, "Password");
return this;
}
private String validateAndTrim(String value, final String parameter) {
if (value == null || value.trim().isEmpty()) {
throw new IllegalArgumentException(parameter + " is empty");
}
return value.trim();
}
public RestClientBuilder gatewayUrl(String gatewayUrl) {
String urlSt = validateAndTrim(gatewayUrl, "Gateway URL");
try {
this.gatewayUrl = new URL(urlSt);
} catch (MalformedURLException e) {
throw new IllegalArgumentException("Malformed URL: " + urlSt, e);
}
return this;
}
public RestClientBuilder truststore(File truststoreFile) {
getSslContextBuilder().trustManager(truststoreFile);
return this;
}
public RestClientBuilder sslCertificate(File keyCertChainFile, File keyFile, String keyPassword) {
getSslContextBuilder().keyManager(keyCertChainFile, keyFile, keyPassword);
return this;
}
public RestClient build() throws SSLException {
SslContext sslContext = sslContextBuilder != null ? sslContextBuilder.build() : null;
return new RestClient(gatewayUrl.toString(), token, username, password, sslContext);
}
private SslContextBuilder getSslContextBuilder() {
if (sslContextBuilder == null) {
sslContextBuilder = SslContextBuilder.forClient();
}
return sslContextBuilder;
}
}
Implementation of the rest client:
public class RestClient {
private WebClient client;
private String gatewayUrl;
public RestClient(String gatewayUrl, String token, String username, String password, SslContext sslContext) {
this.gatewayUrl = gatewayUrl;
WebClient.Builder builder = WebClient.builder().baseUrl(gatewayUrl);
if (sslContext != null) {
HttpClient httpClient = HttpClient.create().secure(sslContextSpec -> sslContextSpec.sslContext(sslContext));
ClientHttpConnector httpConnector = new ReactorClientHttpConnector(httpClient);
builder.clientConnector(httpConnector);
}
if (username != null && password != null) {
builder.filter(basicAuthentication(username, password));
}
client = builder.build();
}
public Mono<Void> executeOnly(ReportRequest transaction) {
Mono<ReportRequest> transactionMono = Mono.just(transaction);
return client.post().uri(gatewayUrl)
.accept(MediaType.APPLICATION_XML)
.contentType(MediaType.APPLICATION_XML)
.body(transactionMono, ReportRequest.class)
.retrieve()
.bodyToMono(Void.class);
}
}
Make remote calls:
public class ReportingProcessor {
private String URL2 = "......";
public void collectEnvironmentData() throws JAXBException {
ReportRequest report = new ReportRequest();
report.setVersion("1.0");
RestClient client = null;
try {
client = RestClientBuilder.builder()
.gatewayUrl(URL2)
// .token(contract.getTerminal_token())
// .usernamePassword("user", "password")
// .truststore(new File("server.pem"))
// .sslCertificate(new File("client.pem"), new File("clientKey.p8"), "secret")
.build();
} catch (SSLException e) {
e.printStackTrace();
}
Mono<Void> result = client.executeOnly(report);
Void response = result.block();
}
When I remove Void response = result.block(); the request is not send. I Can't find why. Can you give me some advice how to make the client code working without using block().
Whenever you work with Spring-webflux you have to keep one thing in mind. i.e You don't have to break your chain. because it is necessary to, someone should call subscribe on your chain. as it works on RXJava specification.
if you break the chain then you have to call block() which not recommended.
you have to modify your code in the below manner.
Let's Consider you have a handler which is making a call to your collectEnvironmentData() method and your method is making a call to remote service.
public Mono<ServerResponse> handelerMethod(ServerRequest request){
return collectEnvironmentData().flatMap(aVoid -> ServerResponse.ok().build());
}
your method should be modified to
public Mono<Void> collectEnvironmentData() throws JAXBException {
ReportRequest report = new ReportRequest();
report.setVersion("1.0");
RestClient client = null;
try {
client = RestClientBuilder.builder()
.gatewayUrl(URL2)
// .token(contract.getTerminal_token())
// .usernamePassword("user", "password")
// .truststore(new File("server.pem"))
// .sslCertificate(new File("client.pem"), new File("clientKey.p8"),
//"secret").build();
} catch (SSLException e) {
e.printStackTrace();
}
return client.executeOnly(report);
}
Change your implementation in the above manner, hope it will work.
How I would implement your method is:
public Mono<Void> executeOnly(ReportRequest transaction) {
Mono<ReportRequest> transactionMono = Mono.just(transaction);
return client.post().uri(gatewayUrl)
.accept(MediaType.APPLICATION_XML)
.contentType(MediaType.APPLICATION_XML)
.body(transaction, ReportRequest.class)
.exchange()
.then();
}
And then I would use it as follows:
client.executeOnly(report).subscribe()
Change the method return type to Mono<Void> for end to end streaming.
public void collectEnvironmentData() throws JAXBException {
ReportRequest report = new ReportRequest();
report.setVersion("1.0");
RestClient client = null;
try {
client = RestClientBuilder.builder()
.gatewayUrl(URL2)
.build();
} catch (SSLException e) {
e.printStackTrace();
}
return client.executeOnly(report);
}
Or you can also subscribe the Mono
client.executeOnly(report).subscribe();
This is my problem:
I have a web application that worked well until Saturday. But then it can not load my entitlements anymore.
Here is the piece of code in my shiro.ini file
entityRealm = com.cagecfi.shiro.EntityRealm
securityManager.authorizer = $entityRealm
authentif = com.cagecfi.Entities.Utilisateur
and the java class :
public class EntityRealm extends AuthorizingRealm{
protected UtilisateurFacadeLocal utifl;
protected ProfilRoleFacadeLocal prfl;
protected static Utilisateur utilisateur;
protected static Profil profil;
protected static List<ProfilRole> profilRoles;
public EntityRealm() throws NamingException {
System.out.println("enter entity realm");
this.setName("entityRealm");
CredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher("SHA-256");
this.setCredentialsMatcher(credentialsMatcher);
InitialContext context = new InitialContext();
this.utifl = (UtilisateurFacadeLocal) context.lookup("java:global/DOLEANCESAPPLI/UtilisateurFacade");
this.prfl = (ProfilRoleFacadeLocal) context.lookup("java:global/DOLEANCESAPPLI/ProfilRoleFacade");
System.out.println("out entity realm");
}
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
final UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
utilisateur = utifl.getOneBy("login", token.getUsername());
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo();
try {
if (utilisateur != null) {
simpleAuthenticationInfo = new SimpleAuthenticationInfo(utilisateur.getLogin(), utilisateur.getPassword(), getName());
} else {
simpleAuthenticationInfo = null;
throw new UnknownAccountException("Utilisateur inconnu");
}
} catch (Exception e) {
e.printStackTrace();
}
return simpleAuthenticationInfo;
}
#Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String userId = (String) principals.fromRealm(this.getName()).iterator().next();
utilisateur = utifl.getOneBy("login", userId);
if (utilisateur != null) {
final SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
profilRoles = this.prfl.getBy("profil", utilisateur.getProfil());
final List<String> roles = new ArrayList<>();
profilRoles.stream().forEach((proRole) -> {
roles.add(proRole.getRole().getNom());
});
info.addRoles(roles);
return info;
} else {
return null;
}
}
public static Utilisateur getUser() {
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.isAuthenticated()) {
return utilisateur;
}
return null;
}
public static Subject getSubject() {
return SecurityUtils.getSubject();
}
#Override
public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
super.clearCachedAuthorizationInfo(principals);
}
}
n a bizarre way, shiro has been sending me this since Saturday.
Exception during lifecycle processing
java.lang.Exception: java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: org.apache.shiro.config.ConfigurationException: Unable to instantiate class [com.cagecfi.shiro.EntityRealm] for object named 'entityRealm'. Please ensure you've specified the fully qualified class name correctly.
To clarify just after a clean and build it began to send me this error.
I even recovered the previous backup of the project and still nothing.
Can someone help me and tell me where I missed something?
Thank you.
In Android is there a better way than using a single AsyncTask with a parameter to work out which REST endpoint to call?
e.g. I need to call:
www.test.com/api/room/id
www.test.com/api/room/id/booking
AsyncTask is designed for a single doInBackground() method that does a single thing, e.g. call:
www.test.com/api/room/id
I don't want to create multiple AsyncTasks instances, one for each REST endpoint.
The back end would use:
RoomClient = new RoomClient();
roomClient.getID()
roomClient.getBookingForRoom()
In Android it looks like I'd need
class RoomFromId extends AsyncTask
...
call www.test.com/api/room/id
class BookingForRoom extends AsyncTask
..
call www.test.com/api/room/id/booking
What I'd ideally like in the Android app is the idiom of writing a rest client that can call all REST endpoints in the background, without having to do each one in its own AsyncTask. I'd prefer to use what Android has, rather than a 3rd party library.
Create a generic Class extends from AsyncTask that return response in a generic type that extends from YourBaseModel (I called it M)
public class HttpRequest<M extends BaseModel> extends AsyncTask<Object, Integer, M> {
public enum RequestMethod {
GET("GET"), POST("POST");
private final String requestMethod;
RequestMethod(String requestMethod) {
this.requestMethod = requestMethod;
}
public String getValue() {
return requestMethod;
}
}
private Context context = null;
private String url;
private OnResponseCallback onResponseCallback;
private OnFailureCallback onFailureCallback;
private RequestMethod method;
private int statusCode;
private String message;
private Class<M> responseModel;
private Object body = null;
private String token;
private HttpRequest() {
}
#Override
protected M doInBackground(Object... voids) {
try {
HttpURLConnection connection = getHttpConnection();
connection.connect();
int statusCode = connection.getResponseCode();
if (connection.getResponseCode() / 100 != 2) {
this.statusCode = statusCode;
this.message = connection.getResponseMessage();
return JsonParser.getErrorBodyAs(responseModel, statusCode,
message);
}
InputStreamReader streamReader = new
InputStreamReader(connection.getInputStream());
return JsonParser.getErrorBodyAs(responseModel, statusCode,
convertInputStreamToString(streamReader));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private HttpURLConnection getHttpConnection() throws IOException {
URL url = new URL(this.url);
HttpURLConnection connection = (HttpURLConnection)
url.openConnection();
connection.setRequestMethod(method.getValue());
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Authorization", "Bearer " + token);
connection.setReadTimeout(30000);
connection.setConnectTimeout(30000);
if (method == RequestMethod.POST) {
connection.setDoInput(true);
connection.setDoOutput(true);
if (body != null) {
OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
writer.write(new Gson().toJson(body));
writer.flush();
}
}
return connection;
}
#Override
protected void onPostExecute(M m) {
if (m == null) {
if ((message != null && !message.equals("") && statusCode != 0)) {
HttpException httpException = new HttpException(statusCode, message);
onFailureCallback.onFailure(httpException);
} else {
onFailureCallback.onFailure("unknown error");
}
} else {
onResponseCallback.onResponse(m);
}
}
public static String convertInputStreamToString(InputStreamReader inputStreamReader) throws IOException {
if (inputStreamReader == null) {
return "";
}
BufferedReader reader = new BufferedReader(inputStreamReader);
StringBuilder stringBuilder = new StringBuilder();
String inputLine;
String result;
while ((inputLine = reader.readLine()) != null) {
stringBuilder.append(inputLine);
}
reader.close();
inputStreamReader.close();
return stringBuilder.toString();
}
static public class Builder {
HttpRequest t = new HttpRequest();
public Builder setContext(Context context) {
t.context = context;
return this;
}
public Builder setUrl(String url) {
t.url = url;
return this;
}
public Builder setRequestMethod(RequestMethod method) {
t.method = method;
return this;
}
public Builder setBody(Object body) {
t.body = body;
return this;
}
public Builder setToken(String token) {
t.token = token;
return this;
}
public HttpRequest get() {
return t;
}
public HttpRequest run(Class<?> responseTypeClass,
OnResponseCallback onResponseCallback,
OnFailureCallback onFailureCallback) {
t.responseModel = responseTypeClass;
t.onResponseCallback = onResponseCallback;
t.onFailureCallback = onFailureCallback;
t.execute();
return t;
}
public Builder() {
}
}
}
You can use it like this:
HttpRequest.Builder builder = new HttpRequest.Builder();
builder.setContext(context)
.setRequestMethod(HttpRequest.RequestMethod.POST)
.setBody(body)
.setUrl("http://url")
.run(YourResponeModel.class, new OnResponseCallback() {
#Override
public void onResponse(Object response) {
},
new OnFailureCallback() {
#Override
public void onFailure(Object throwable) {
}
});
In the class you create that extends AsyncTask you can create a constructor and pass whatever you want/need.
In this case you can define a class ApiManager that extends AsyncTask and pass a constant that defines the method to call.
In that constructor you can save the variable to your ApiManager object and then check it in the doInBackground method.
So, to call the room/id you could do something like:
new ApiManager(ROOM_FROM_ID).execute(...
And to call the room/id/booking:
new ApiManager(BOOKING_FOR_ROOM).execute(...
And the ApiManager class should be something like:
class ApiManager extends AsyncTask... {
private int method;
public ApiManager(int method) {
this.method = method;
}
...
}
Why wont this compile:
MyOkHttpClient okClient = new MyOkHttpClient.Builder()
.addInterceptor(new AddCookiesInterceptor())
.addInterceptor(new ReceivedCookiesInterceptor()).build();
Incompatible types.
Required:
my.path.util.auth.MyOkHttpClient
Found:
okhttp3.OkHttpClient
This is MY class:
public class MyOkHttpClient extends okhttp3.OkHttpClient implements Authenticator {
private static int MAX_AUTHENTICATE_TRIES = 3;
#Override
public Request authenticate(Route route, Response response) throws IOException {
if (responseCount(response) >= MAX_AUTHENTICATE_TRIES) {
return null; // If we've failed 3 times, give up. - in real life, never give up!!
}
String credential = Credentials.basic(AUTHTOKEN_USERNAME, AUTHTOKEN_PASSWORD);
return response.request().newBuilder().header("Authorization", credential).build();
}
private int responseCount(Response response) {
int result = 1;
while ((response = response.priorResponse()) != null) {
result++;
}
return result;
}
}
Based on your comments, you incorrectly believe that you are decorating OkHttpClient with custom authentication logic.
Instead, you are unnecessarily extending OkHttpClient and implementing the Authenticator interface. You can simply build the standard OkHttpClient with any custom authenticator you would like.
As such, this is more like what you actually want
public class MyAuthenticator implements Authenticator {
private static int MAX_AUTHENTICATE_TRIES = 3;
#Override
public Request authenticate(Route route, Response response) throws IOException {
if (responseCount(response) >= MAX_AUTHENTICATE_TRIES) {
return null; // If we've failed 3 times, give up. - in real life, never give up!!
}
String credential = Credentials.basic(AUTHTOKEN_USERNAME, AUTHTOKEN_PASSWORD);
return response.request().newBuilder().header("Authorization", credential).build();
}
private int responseCount(Response response) {
int result = 1;
while ((response = response.priorResponse()) != null) {
result++;
}
return result;
}
}
And then when you build your client
OkHttpClient okClient = new OkHttpClient.Builder()
.addInterceptor(new AddCookiesInterceptor())
.addInterceptor(new ReceivedCookiesInterceptor())
.authenticator(new MyAuthenticator())
.build();
When i deploy my code I get error
Caused by: java.lang.IllegalStateException: Ambiguous mapping found. Cannot map 'programController' bean method
public java.lang.String com.quizapp.beans.controller.panal.ProgramController.editProgram(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
to {[],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}: There is already 'programController' bean method
public java.lang.String com.quizapp.beans.controller.panal.ProgramController.setupProgram(javax.servlet.http.HttpSession,org.springframework.ui.Model) mapped.
Below is my programController class
#Controller
public class ProgramController {
#RequestMapping(name = "/editProgram", method = RequestMethod.GET)
public String editProgram(HttpServletRequest request, HttpServletResponse response) {
if (request.getSession().getAttribute(Constants.LOGGED_IN_USER) != null) {
ProgramEntity program = new ProgramEntity();
if (request.getParameter("id") == null) {// create
program.setType("create");
} else {// edit
program.setType("edit");
program.setCode(request.getParameter("id"));
}
request.setAttribute("programBean", program);
return "editProgram";
} else {
return "redirect:/login";
}
}
#RequestMapping(name = "/program", method = RequestMethod.GET)
public String setupProgram(HttpSession session, Model model) {
if (session.getAttribute(Constants.LOGGED_IN_USER) != null) {
model.addAttribute("programBean", new ProgramEntity());
// if (request.getSession().getAttribute(Constants.LOGGED_IN_USER) != null) {
// request.setAttribute("programBean", new ProgramEntity());
return "viewProgram";
} else {
return "redirect:/login";
}
}
}
When i make the method editProgram as POST then it deploy perfectly.
Why is there problem with adding two GET method.
There are many similar questions available on StackOverflow but i couldn't find right answer.
Because you are specifying different names of RequestMapping and not values. Currently, value of both the methods are same ("" - empty) which is not allowed. Your URL part of request mapping should be specified in value and not name attribute of #RequestMapping.
Try This:
#Controller
public class ProgramController {
#RequestMapping(value = "/editProgram", method = RequestMethod.GET)
public String editProgram(HttpServletRequest request, HttpServletResponse response) {
if (request.getSession().getAttribute(Constants.LOGGED_IN_USER) != null) {
ProgramEntity program = new ProgramEntity();
if (request.getParameter("id") == null) {// create
program.setType("create");
} else {// edit
program.setType("edit");
program.setCode(request.getParameter("id"));
}
request.setAttribute("programBean", program);
return "editProgram";
} else {
return "redirect:/login";
}
}
#RequestMapping(value= "/program", method = RequestMethod.GET)
public String setupProgram(HttpSession session, Model model) {
if (session.getAttribute(Constants.LOGGED_IN_USER) != null) {
model.addAttribute("programBean", new ProgramEntity());
// if (request.getSession().getAttribute(Constants.LOGGED_IN_USER) != null) {
// request.setAttribute("programBean", new ProgramEntity());
return "viewProgram";
} else {
return "redirect:/login";
}
}
}