Based on this Jaspic Example I wrote the following validateRequest method for a ServerAuthModule:
public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject,
Subject serviceSubject) throws AuthException {
boolean authenticated = false;
final HttpServletRequest request =
(HttpServletRequest) messageInfo.getRequestMessage();
final String token = request.getParameter("token");
TokenPrincipal principal = (TokenPrincipal) request.getUserPrincipal();
Callback[] callbacks = new Callback[] {
new CallerPrincipalCallback(clientSubject, (TokenPrincipal) null) };
if (principal != null) {
callbacks = new Callback[] {
new CallerPrincipalCallback(clientSubject, principal) };
authenticated = true;
} else {
if (token != null && token.length() == Constants.tokenLength) {
try {
principal = fetchUser(token);
} catch (final Exception e) {
throw (AuthException) new AuthException().initCause(e);
}
callbacks = new Callback[]
{
new CallerPrincipalCallback(clientSubject, principal),
new GroupPrincipalCallback(clientSubject,
new String[] { "aRole" })
};
messageInfo.getMap().put("javax.servlet.http.registerSession", "TRUE");
authenticated = true;
}
}
if (authenticated) {
try {
handler.handle(callbacks);
} catch (final Exception e) {
throw (AuthException) new AuthException().initCause(e);
}
return SUCCESS;
}
return AuthStatus.SEND_FAILURE;
}
This works as expected, for the first call of an ejb with #RolesAllowed("aRole") but for the next call this does not work at all. Wildfly denies it with this error message:
ERROR [org.jboss.as.ejb3.invocation] (default task-4) WFLYEJB0034: EJB Invocation
failed on component TestEJB for method public java.lang.String
com.jaspic.security.TestEJB.getPrincipalName():
javax.ejb.EJBAccessException: WFLYSEC0027: Invalid User
If I guess right, the error occures in:
org.jboss.as.security.service.SimpleSecurityManager line 367 of wilfly's source code, due to line 405, in which credential is checked, but seems to be null.
This seems equal in Wildfly 8/9/10CR (other versions not tested).
Again I'm not sure, if I'm doing it wrong, or if this is the same bug as
https://issues.jboss.org/browse/WFLY-4626 ? And is it a bug at all, or is it expected behavior?
This sounds like a bug to me as well, as the caller identity (caller / group Principals) appears to be retained in subsequent calls to the web, yet not to the EJB container. My own JASPIC classes (which function properly on GlassFish 4.1) fail for the same reason on WildFly 9.0.2.Final and 10.0.0.CR4 when used along with a plain Servlet and an SLSB, even with the latter marked #PermitAll.
As I'm myself unfamiliar with WildFly security internals I can not assist you in that respect. Unless you can get this patched, the sole SAM-level workaround I can think of for the time being would be to not use the javax.servlet.http.registerSession callback property that seemingly triggers the problem, but instead have the CallbackHandler register both the caller Principal and its groups on every validateRequest(...) invocation. If applicable to your use case, you may wish to attach that information to the HttpSession so as to speed up the process a bit; otherwise repeat from scratch. So, for example:
public class Sam implements ServerAuthModule {
// ...
#Override
public AuthStatus validateRequest(MessageInfo mi, Subject client, Subject service) throws AuthException {
boolean authenticated = false;
boolean attachAuthnInfoToSession = false;
final String callerSessionKey = "authn.caller";
final String groupsSessionKey = "authn.groups";
final HttpServletRequest req = (HttpServletRequest) mi.getRequestMessage();
TokenPrincipal tp = null;
String[] groups = null;
String token = null;
HttpSession hs = req.getSession(false);
if (hs != null) {
tp = (TokenPrincipal) hs.getAttribute(callerSessionKey);
groups = (String[]) hs.getAttribute(groupsSessionKey);
}
Callback[] callbacks = null;
if (tp != null) {
callbacks = new Callback[] { new CallerPrincipalCallback(client, tp), new GroupPrincipalCallback(client, groups) };
authenticated = true;
}
else if (isValid(token = req.getParameter("token"))) {
tp = newTokenPrincipal(token);
groups = fetchGroups(tp);
callbacks = new Callback[] { new CallerPrincipalCallback(client, tp), new GroupPrincipalCallback(client, groups) };
authenticated = true;
attachAuthnInfoToSession = true;
}
if (authenticated) {
try {
handler.handle(callbacks);
if (attachAuthnInfoToSession && ((hs = req.getSession(false)) != null)) {
hs.setAttribute(callerSessionKey, tp);
hs.setAttribute(groupsSessionKey, groups);
}
}
catch (IOException | UnsupportedCallbackException e) {
throw (AuthException) new AuthException().initCause(e);
}
return AuthStatus.SUCCESS;
}
return AuthStatus.SEND_FAILURE;
}
// ...
#Override
public void cleanSubject(MessageInfo mi, Subject subject) throws AuthException {
// ...
// just to be safe
HttpSession hs = ((HttpServletRequest) mi.getRequestMessage()).getSession(false);
if (hs != null) {
hs.invalidate();
}
}
private boolean isValid(String token) {
// whatever
return ((token != null) && (token.length() == 10));
}
private TokenPrincipal newTokenPrincipal(String token) {
// whatever
return new TokenPrincipal(token);
}
private String[] fetchGroups(TokenPrincipal tp) {
// whatever
return new String[] { "aRole" };
}
}
I tested the above on the aforementioned WildFly versions and in the aforementioned fashion (i.e. with a single Servlet referencing a single SLSB marked #DeclareRoles / method-level #RolesAllowed) and it seems to work as expected. Obviously I cannot guarantee that this approach will not fail in other unexpected ways.
See also:
[WFLY-4625]
[SECURITY-744]
[SECURITY-745]
Related
#Test
public void testBatchFailClientBatchSyncCallIllegalArgumentExceptions() throws Exception {
Map<String, String> singletonMap = Collections.singletonMap(ACCEPT_STRING_ID, defaultLocalizationMap.get(ACCEPT_STRING_ID));
StringRequest[] requests = stringRequestFactory.createRequests(singletonMap);
when(lmsClient.batchSyncCall(requests)).thenThrow(new IllegalArgumentException());
List<Backend.Response> responses = callLms(new StringRequest[] {requests[0]});
Assert.assertNotNull(responses);
assertEquals(EntityDescriptors.ERROR_V1, responses.get(0).entityDescriptor());
assertEquals(Http.Status.SERVICE_UNAVAILABLE, responses.get(0).status());
}
#Test
public void testBatchFailClientBatchSyncCallIOException() throws Exception {
Map<String, String> singletonMap = Collections.singletonMap(ACCEPT_STRING_ID, defaultLocalizationMap.get(ACCEPT_STRING_ID));
StringRequest[] requests = stringRequestFactory.createRequests(singletonMap);
when(lmsClient.batchSyncCall(requests)).thenThrow(new IOException());
List<Backend.Response> responses = callLms(new StringRequest[] {requests[0]});
Assert.assertNotNull(responses);
assertEquals(EntityDescriptors.ERROR_V1, responses.get(0).entityDescriptor());
assertEquals(Http.Status.SERVICE_UNAVAILABLE, responses.get(0).status());
}
Source Code -
#Override
public List<Backend.Response> handleRequests(BackendRequestContext context, List<Backend.Request> requests, Metrics metrics) {
StringRequest[] stringRequests = new StringRequest[requests.size()];
final String language = context.locale().toLanguageTag().replace("-", "_");
for (int i = 0; i < requests.size(); i++) {
final Backend.Request request = requests.get(i);
final String id = request.requiredPathParam(STRING_ID_PATH_PARAM);
final Optional<String> marketplaceDisplayName = request.queryParam(MARKETPLACE_NAME_QUERY_PARAM);
final Optional<String> stage = request.queryParam(STAGE_QUERY_PARAM);
final StringRequest stringRequest = new StringRequest(id);
stringRequest.setLanguage(language);
marketplaceDisplayName.ifPresent(stringRequest::setMarketplaceName);
stage.map(Stage::getStage).ifPresent(stringRequest::setStage);
stringRequests[i] = stringRequest;
}
StringResultBatch batchResult = invokeBatchSync(stringRequests);
return IntStream.of(requests.size()).mapToObj(i -> {
final Backend.Request request = requests.get(i);
try {
return transform(request, batchResult.get(i), language);
} catch (IOException e) {
LOGGER.error("", e);
return Backend.Response.builder()
.withRequest(request)
.withEntityDescriptor(EntityDescriptors.ERROR_V1)
.withStatus(Http.Status.SERVICE_UNAVAILABLE)
.withBody(ErrorResponses.ServerError.serviceUnavailable(ErrorResponse.InternalInfo.builder()
.withMessage("Error retrieving ["
+ request.requiredPathParam(STRING_ID_PATH_PARAM)
+ "]")
.build())
.tokens())
.build();
}
}
).collect(Collectors.toList());
}
private StringResultBatch invokeBatchSync(StringRequest[] stringRequests) {
try {
// LMS Client has an async batch call,
// but it returns a proprietary class (StringResultBatchFuture) which eventually wraps a BSFFutureReply.
// Neither of which provide access to anything like a Java-standard Future.
return client.batchSyncCall(stringRequests);
} catch (IllegalArgumentException | IOException e) {
//
return null;
}
}
I have two test cases here for the source file. I'm getting the Error N/A. It says null pointer exception. Can someone please review this and help me with this. It will be really appreciated. Thank you in advance
P.S - The source file takes input request as string and performs string translation and returns us that string.
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");
}
...
}
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.
I want to create ec2 instances when ever the new user arrive. I created a servlet class to do this. When User arrive i check DB that is user new or not if new then create the instance and send back his/her IP. When i send http request to this servlet one by one for users i get the IP correctly. But when i send HTTP Call in parallel (for user1 send request in tab1, for user2 send request in tab2 simultaneously before getting response from user1 HTTP call). When i do this i got error. Sometimes user1 said
"The instance ID 'i-0b79495934c3b5459' does not exist (Service:
AmazonEC2; Status Code: 400; Error Code: InvalidInstanceID.NotFound;
Request ID: e18a9eaa-cb1b-4130-a3ee-bf1b19fa184c) "
And user2 send IP in response. Kindly help me What is the issue and how to resolve this.
This is the Servlet Class which i created.
public class GateKeeperController extends HttpServlet {
private static final long serialVersionUID = 1L;
BasicAWSCredentials awsCreds = new BasicAWSCredentials(credentials);
AmazonEC2Client ec2Client = new AmazonEC2Client(awsCreds);
RunInstancesRequest runInstancesRequest;
RunInstancesResult runInstancesResult;
Reservation reservation;
Instance intstance;
DescribeInstancesRequest describeInstanceRequest;
DescribeInstancesResult describeInstanceResult;
GatekeeperModal gateKeepermodal;
String sourceAMI = null;
String destinationAMI = null;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpSession s = request.getSession();
String userID = (String) request.getParameter("userID");
Double lattitude = Double.parseDouble((String) request.getParameter("lat"));
Double lonitude = Double.parseDouble((String) request.getParameter("long"));
if (userID != null) {
Pair coordinates = new Pair(lattitude, lonitude);
RegionSelection targetRegion = new RegionSelection();
String regionResult = targetRegion.getRegion(coordinates);
String instanceIP = null;
gateKeepermodal = new GatekeeperModal();
try {
if (gateKeepermodal.checkUserIsNew(userID)) {
instanceIP = startInstance(userID, regionResult);
if (instanceIP != null) {
response.getWriter().write(instanceIP);
}
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
}
}
private String startInstance(String userID, String region) {
String ami_id = new AMI().getAMI_ID(region);
ec2Client.setEndpoint(region);
runInstancesRequest = new RunInstancesRequest();
runInstancesRequest.withImageId(ami_id).withInstanceType("t2.micro").withMinCount(1).withMaxCount(1)
.withKeyName("GateKeeper_User").withSecurityGroups("GateKeeper User");
runInstancesResult = ec2Client.runInstances(runInstancesRequest);
reservation = runInstancesResult.getReservation();
intstance = reservation.getInstances().get(0);
String s1 = intstance.getState().getName();
String s2 = InstanceStateName.Running.name();
while (!s1.toLowerCase().equals(s2.toLowerCase())) {
describeInstanceRequest = new DescribeInstancesRequest();
describeInstanceRequest.withInstanceIds(intstance.getInstanceId());
ec2Client.setEndpoint(region);
describeInstanceResult = ec2Client.describeInstances(describeInstanceRequest);
reservation = describeInstanceResult.getReservations().get(0);
intstance = reservation.getInstances().get(0);
s1 = intstance.getState().getName();
s2 = InstanceStateName.Running.name();
}
GateKeeperUser user = new GateKeeperUser(userID, intstance.getInstanceId(), intstance.getPublicIpAddress(),
region);
Boolean result;
try {
result = gateKeepermodal.createUser(user);
if (result) {
return intstance.getPublicIpAddress();
} else {
return null;
}
} catch (SQLException e) {
}
return null;
}
}
According to the documentation:
"If you successfully run the RunInstances command, and then
immediately run another command using the instance ID that was
provided in the response of RunInstances, it may return an
InvalidInstanceID.NotFound error. This does not mean the instance does
not exist. Some specific commands that may be affected are:
DescribeInstances: To confirm the actual state of the instance, run
this command using an exponential backoff algorithm.
TerminateInstances: To confirm the state of the instance, first run
the DescribeInstances command using an exponential backoff algorithm."
I have a method to unit test called addSong(song,userId) in service class. I am calling three methods inside it from Dao class. I am using Easy mock to mock dao class. In the setup I first mock all the methods I am calling in addSong(song,userId), and then calling the service.addsong(song,userId) method fot test.
But I am getting the following error:
Java.lang.IllegalStateException: missing behavior definition for the preceding method call:
MusicPlayerDao.addSong(song)
Usage is: expect(a.foo()).andXXX()
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:42)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:94)
at org.easymock.internal.ClassProxyFactory$MockMethodInterceptor.intercept(ClassProxyFactory.java:97)
at service.MusicPlayerDao$$EnhancerByCGLIB$$45bc3ca1.addSong(<generated>)
at service.MusicPlayerServiceImpl.addSong(MusicPlayerServiceImpl.java:43)
at AddSongTest.addSongs(AddSongTest.java:90)
Here is my code:
private void addSongSetup() throws SQLException{
this.album = new Album();
album.setAlbumName("album");
this.genre = new Genre();
genre.setGenreName("genre");
this.song = new Song("song",this.album,3,"artist","composer",this.genre);
EasyMock.expect(this.dao.addSong(song)).andReturn(1).anyTimes();
EasyMock.expect(this.dao.addGenre(genre, 1)).andReturn(1).anyTimes();
EasyMock.expect(this.dao.addAlbum(album, 1)).andReturn(1).anyTimes();
EasyMock.expect(this.dao.userIdSongsMapping(1,1)).andReturn(1).anyTimes();
}
#Test
public void addSongs(){
this.album = new Album();
album.setAlbumName("album");
this.genre = new Genre();
genre.setGenreName("genre");
this.song = new Song("song",this.album,3,"artist","composer",this.genre);
try {
System.out.println(this.dao.addSong(song));
boolean status = this.service.addSong(song, 1);
assertEquals(true,status);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
My addSong method in service class:
public boolean addSong(Song song, int userId) throws Exception {
MusicPlayerDaoInterface musicPlayerDao = MusicPlayerDao.getInstance();
boolean status = false;
int songId = 0;
TransactionManager transactionManager = TransactionManagerImpl
.getInstance();
try {
if (song != null) {
if (song.getTitle() != null) {
transactionManager.begin();
songId = musicPlayerDao.addSong(song);
song.setSongId(songId);
if (song.getGenre() != null
&& song.getGenre().getGenreName() != null) {
musicPlayerDao.addGenre(song.getGenre(),
song.getSongId());
}
if (song.getAlbum() != null
&& song.getAlbum().getAlbumName() != null) {
musicPlayerDao.addAlbum(song.getAlbum(),
song.getSongId());
}
if (userId != 0 && songId != 0) {
musicPlayerDao.userIdSongsMapping(userId,
song.getSongId());
}
transactionManager.commit();
status = true;
}
}
} catch (SQLException | RollbackException | HeuristicMixedException
| HeuristicRollbackException e) {
transactionManager.rollback();
status = false;
throw e;
}
return status;
}
I don't know were I am going wrong. Please help.
I think you are missing a EasyMock.replay statement after you record the expected behaviour. Something like
EasyMock.replay(this.dao);
From the EasyMock guide:
To get a Mock Object, we need to
create a Mock Object for the interface we would like to simulate
record the expected behavior
switch the Mock Object to replay state
try removing the following lines from the addSongs test case:
this.album = new Album();
album.setAlbumName("album");
this.genre = new Genre();
genre.setGenreName("genre");
this.song = new Song("song",this.album,3,"artist","composer",this.genre);
I assume that addSongSetup is invoked before addSongs (e.g.; #Before). You are reassigning values to your variables album, genre and song in addSong, which, I suppose, EasyMock cannot match to your mock setup in addSongSetup as (depending on how EasyMock implemented this)
you forgot to implement hashcode or equals in Song, Album, Genre or,
EasyMock uses Object identity (i.e., reference comparison)
I guess it's 1.