I've just created my own custom authentication on my google app engine Java app. And it wasn't that much of a trouble as is the next thing I'm trying to do.
Authentication works fine but now I'm trying to add some additional fields to the default User object so that I wouldn't have to make so many calls to the server.
So what I've done so far is created a custom class that implements Authenticator. Based on whether the user is authenticated or not the authenticate method returns the User object or null. User object is then accessible to my API endpoints.
To extend my app functionality I've tried extending the default User object, making some new fields, and then passing it to endpoints. However, since the User object accessible by endpoints is not the same kind as the one I extended from I can't get the extra fields.
MyAuthenticator.java
import com.google.api.server.spi.auth.common.User;
public class MyAuthenticator implements Authenticator {
#Override
public User authenticate(HttpServletRequest request) {
// some code
return new AuthUser(...)
}
AuthUser.java
import com.google.api.server.spi.auth.common.User;
public class AuthUser extends User {
private String newToken;
public AuthUser(String email) {
super(email);
}
public AuthUser(String id, String email) {
super(id, email);
}
public AuthUser(String id, String email, String newToken) {
super(id, email);
this.newToken = newToken;
}
public String getNewToken() {
return newToken;
}
}
UserEndpoint.java
import com.google.appengine.api.users.User;
#Api(authenticators = MyAuthenticator.class)
public class UserEndpoint {
#ApiMethod(httpMethod = "GET")
public final Response sth(User user)
throws UnauthorizedException {
EndpointUtil.throwIfNotAuthenticated(user);
// ...
}
Notice different class imports.
I can't use AuthUser in UserEndpoint sth method because then API expects me to post that object with my call to server.
How can I pass extra data from authenticator to my endpoint method?
AppEngine docs say the injected types are the following:
com.google.appengine.api.users.User
javax.servlet.http.HttpServletRequest
javax.servlet.ServletContext
However, it doesn't mention com.google.api.server.spi.auth.common.User, but it works for sure. I just tried with AppEngine Java SDK 1.9.32. I don't know if it's a bug or feature.
So in UserEndpoint.java, you have to import com.google.api.server.spi.auth.common.User, then you can cast it to AuthUser.
import com.google.api.server.spi.auth.common.User;
#Api(authenticators = MyAuthenticator.class)
public class UserEndpoint {
#ApiMethod(httpMethod = "GET")
public final Response sth(User user)
throws UnauthorizedException {
EndpointUtil.throwIfNotAuthenticated(user);
((AuthUser)user).getNewToken();
// ...
}
Related
I'm trying to get UserDetails object like below. But, I have some difficulties and impossible to get UserDetails object, so there is only JSONObject in authentication.getAttributes(). Is there any alternative way in micronaut to get UserDetails object?
Custom UserDetails object:
public class MyUserPrincipal implements UserDetails {
private Account account;
public MyUserPrincipal(Account account) {
this.account = account;
}
public Account getAccount() {
return getAccount();
}
}
Rest api:
//micronaut
#Post(value = "/echo")
#Status(HttpStatus.OK)
public Long echo(#Nullable Authentication authentication) {
Long accountId = (Long)((JSONObject)authentication.getAttributes().get("account")).get("id");
return accountId;
}
For example in Spring Security it is easy with #AuthenticationPrincipal annotation in parameter.
Rest api:
#GET
public ResponseEntity<?> echo(#AuthenticationPrincipal MyUserPrincipal user) {
return new ResponseEntity<>(user.getAccount().getAccountId(), HttpStatus.OK);
}
If you are still looking for a solution, here is what works.
You have to provide an implementation of JwtAuthenticationFactory and replace default DefaultJwtAuthenticationFactory.
Something like this (code below is in Kotlin):
#Singleton
#Replaces(bean = DefaultJwtAuthenticationFactory::class)
class CustomJwtAuthenticationFactory() : JwtAuthenticationFactory {
override fun createAuthentication(token: JWT?): Optional<Authentication> {
try {
val builder = JWTClaimsSet.Builder()
builder.claim("username", token?.jwtClaimsSet?.getStringClaim("username"))
return Optional.of(AuthenticationJWTClaimsSetAdapter(jwtClaims))
} catch (e: Exception) {
throw RuntimeException("ParseException creating authentication", e)
}
}
}
All claims added using the builder will get added in the Authentication object and can be accessed in any controller eg:
#Get("/hello-world")
fun hello(authentication: Authentication): String =
authentication["username"] as String
If you are using Kotlin, use could also add extension methods on Authentication method to fetch attributes that you add to Authentication class eg:
fun Authentication.username(): String = this.attributes["username"]
Note: username is just an example. It is available as name instance variable on instance of Authentication.
UserDetails does not exist after authentication. The only object available is the Authentication. If you want to standardize the code you have above you could create a bean that handles injection of that specific property.
You could use an annotation to designate the injection by creating an annotation along with an implementation of AnnotatedRequestArgumentBinder. Something like the following:
public class Temp implements AnnotatedRequestArgumentBinder<YourAnnotation, Long> {
#Override
public Class<YourAnnotation> getAnnotationType() {
return YourAnnotation.class;
}
#Override
public BindingResult<Long> bind(ArgumentConversionContext<Long> context, HttpRequest<?> source) {
if (source.getAttributes().contains(OncePerRequestHttpServerFilter.getKey(SecurityFilter.class))) {
final Optional<Authentication> authentication = source.getUserPrincipal(Authentication.class);
if (authentication.isPresent()) {
return () -> (Long)((JSONObject)authentication.getAttributes().get("account")).get("id");
}
}
return ArgumentBinder.BindingResult.EMPTY;
}
}
Use Case:
let's design a RESTful create operation using POST HTTP verb - creating tickets where creator (assigner) specifies a ticket assignee
we're creating a new "ticket" on following location: /companyId/userId/ticket
we're providing ticket body containing assigneeId:
{
"assigneeId": 10
}
we need to validate that assigneeId belongs to company in URL - companyId path variable
So far:
#RequestMapping(value="/{companyId}/{userId}/ticket", method=POST)
public void createTicket(#Valid #RequestBody Ticket newTicket, #PathVariable Long companyId, #PathVariable Long userId) {
...
}
we can easily specify a custom Validator (TicketValidator) (even with dependencies) and validate Ticket instance
we can't easily pass companyId to this validator though! We need to verify that ticket.assigneeId belongs to company with companyId.
Desired output:
ability to access path variables in custom Validators
Any ideas how do I achieve the desired output here?
If we assume that our custom validator knows desired property name, then we can do something like this:
Approach one:
1) We can move this getting path variables logic to some kind of a base validator:
public abstract class BaseValidator implements Validator {
#Override
public boolean supports(Class<?> clazz)
{
// supports logic
}
#Override
public void validate(Object target, Errors errors)
{
// some base validation logic or empty if there isn't any
}
protected String getPathVariable(String name) {
// Getting current request (Can be autowired - depends on your implementation)
HttpServletRequest req = HttpServletRequest((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
if (req != null) {
// getting variables map from current request
Map<String, String> variables = req.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
return variables.get(name);
}
return null;
}
}
2) Extend it with your TicketValidator implementation:
public class TicketValidator extends BaseValidator {
#Override
public void validate(Object target, Errors errors)
{
// Getting our companyId var
String companyId = getPathVariable("companyId");
...
// proceed with your validation logic. Note, that all path variables
// is `String`, so you're going to have to cast them (you can do
// this in `BaseValidator` though, by passing `Class` to which you
// want to cast it as a method param). You can also get `null` from
// `getPathVariable` method - you might want to handle it too somehow
}
}
Approach two:
I think it worth to mention that you can use #PreAuthorize annotation with SpEL to do this kind of validation (You can pass path variables and request body to it). You'll be getting HTTP 403 code though if validation woudnt pass, so I guess it's not exaclty what you want.
You could always do this:
#Controller
public class MyController {
#Autowired
private TicketValidator ticketValidator;
#RequestMapping(value="/{companyId}/{userId}/ticket", method=POST)
public void createTicket(#RequestBody Ticket newTicket,
#PathVariable Long companyId, #PathVariable Long userId) {
ticketValidator.validate(newTicket, companyId, userId);
// do whatever
}
}
Edit in response to the comment:
It doesn't make sense to validate Ticket independently of companyId when the validity of Ticket depends on companyId.
If you cannot use the solution above, consider grouping Ticket with companyId in a DTO, and changing the mapping like this:
#Controller
public class MyController {
#RequestMapping(value="/{userId}/ticket", method=POST)
public void createTicket(#Valid #RequestBody TicketDTO ticketDto,
#PathVariable Long userId) {
// do whatever
}
}
public class TicketDTO {
private Ticket ticket;
private Long companyId;
// setters & getters
}
I am using jersey 1.9.1. I have rest method like following where
Authorization header contained encoded credentials such as username
and password and it is parsed in a method and mapped local values.
#PUT
#Path(SystemConstants.REST_MESSAGE_SENDSMS)
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.APPLICATION_JSON})
public Response sendSms(#HeaderParam("Authorization") String authorization, String param) {
String[] credentials = ImosUtils.getUserCredentials(authorization);
String username = credentials[0];
String password = credentials[1];
}
I am trying to design a way to make this process automatically, without writing same parsing code in each method. I mean I would like to know if writing a special annotation such as HeaderParamExtended to this is used to parse this credentials.
I am using jersey 1.9.1 version as rest api. Where I have to edit a class in that life cycle?
#PUT
#Path(SystemConstants.REST_MESSAGE_SENDSMS)
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.APPLICATION_JSON})
public Response sendSms(#HeaderParamExtended("Authorization","username") String username, #HeaderParamExtended("Authorization","password") String password, , String param) {
}
Normally you need an InjectableProvider to support the custom injection, and also an Injectable to provide the value.
Here's an example
#BasicAuth
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
public #interface BasicAuth {
}
InjectableProvider
#Provider
public class BasicAuthInjectionProvider
implements InjectableProvider<BasicAuth, Parameter> {
#Override
public ComponentScope getScope() {
return ComponentScope.PerRequest;
}
#Override
public Injectable getInjectable(ComponentContext cc, BasicAuth a, Parameter c) {
return new BasicAuthInjectable();
}
}
Injectable
public class BasicAuthInjectable extends AbstractHttpContextInjectable<User>{
#Override
public User getValue(HttpContext hc) {
String authHeaderValue = hc.getRequest()
.getHeaderValue(HttpHeaders.AUTHORIZATION);
String[] credentials = ImosUtils.getUserCredentials(authHeaderValue);
return new User(credentials[0], credentials[1]);
}
}
One thing you'll notice is that I have a User class. This is to wrap the username and password, and just have one injection point. i.e.
public Response getSomething(#BasicAuth User user) {
}
I actually tried to do it your way, with
public Response getSomething(#BasicAuth("username") String username,
#BasicAuth("password") String password) {
And in the InjectableProvider get the annotation value from the annotation passed to the getInjectable, then pass that value onto the BasicAuthInjectable. From there check to see if the value is "username" or "password" and return the corresponding value. But for some reason the injection providers were not even called. You can play around with it to see if you can get it to work. But to me the User looks cleaner anyway, and with the two strings, the injection providers are called twice and you need to parse the headers twice. Seems unnecessary.
I would like to encapsulate Apache Shiro in a Servlet environment. I want to create MySecurityUtils and use Shiro SecurityUtils.getSubject in a static method. My question is whether this is a correct way to use SecurityUtils.getSubject method in a static method. Can this cause any problems in multithreaded servlet environment?
MySecurityUtils.java
import org.apache.shiro.subject.Subject;
import org.apache.shiro.SecurityUtils;
public class MySecurityUtils {
public static MyUser getUser() {
Subject currentUser = SecurityUtils.getSubject();
MyUser myUser = new MyUser(currentUser);
...
}
}
MyUser.java
public class MyUser {
// ... constructors
public boolean isPermitted(..) {subject.isPermitted(...)}
}
I don't see why you would want to do this, but for your question's sake, this would be fine.
In a web context, Shiro's SecurityUtils#getSubject() returns a different Subject instance per request. Obviously if the subject is logged in, the credentials will be copied over (from session) to the new Subject instance. You are pretty much doing the same thing by returning a new MyUser instance on each call to getUser().
Careful though, if you call getUser() twice in the same request, you will get a different MyUser instance. However, the internal Subject will be the same. It can be problematic if you are doing logic other than delegating in your MyUser class.
After feedback of Sotirios I changed my code as follows
public class SecurityHelper {
public static boolean isAuthenticated(){
Subject currentUser = SecurityUtils.getSubject();
return currentUser.isAuthenticated();
}
public static void checkPermission(String permissionCode){
Subject currentUser = SecurityUtils.getSubject();
currentUser.checkPermission(permissionCode);
}
public static void checkPermission(String... permissionCodes){
Subject currentUser = SecurityUtils.getSubject();
currentUser.checkPermissions(permissionCodes);
}
... and so on
I encapsulate all application logic in a Helper class.
UserService.java
...
public class UserService {
public static User getUser(String username, String password) {
...
}
public static User getUser2(String username, String password) {
...
}
}
login.mxml
private function loginUser() : void {
lostPassword = false;
// this works fine
UserService.getUser(username.text, password.text);
// this fails !
UserService.getUser2(username.text, password.text);
}
getUser was already in UserService.java. I just created getUser2 and it's identical to getUser. When I try to call getUser2, i get the "Cannot invoke method" error.
question: Do I need to specify getUser2 in some other file? like in some configuration file? if so, which one and how do I do it.
Thanks.
think problem is JAVA static method according to Remoting Service definition
The Remoting Service lets a client application access the methods of server-side Java objects
In java/oops static methods are not associated to Object/instance its depends-upon/associated to class
your method should be like this to accept call from flex
public class UserService {
public User getUser(String username, String password) {
...
}
public User getUser2(String username, String password) {
...
}
}
Hopes that helps