Graphql-ruby Bulding Mutation/query Without the need of user authentication - ruby

Hey I have a graphql mutation which needs to be implemented before user logs in. Till now I have been using graphql endpoints only after User is fully authenticated. Since graphql controller inherits application controller which implements a before_action :authenticate_user! callback I always need a valid user inorder to use the graphql endpoints. Is there a way to configure certain graphql endpoint to not have a valid user.
How should I go about it?

You can add logic in execute method of GraphQlController that checks for exceptions.
For example, we want to skip authorization on "createSession" query that is supposed to generate JWT token for valid username/password combination. Trick is to create "Query" object where you can easily get to the query being invoked and determine if it's in skip list. Pardon the code it is first pass, just as proof of concept.
#class GraphqlController < Application Controller
#skips_authorization = ["createSession"]
def execute
variables = prepare_variables(params[:variables])
query = params[:query]
operation_name = params[:operationName]
current_user = AuthorizeApiRequest.call(request.headers).result
context = {
current_user: current_user,
}
query_parse = GraphQL::Query.new(ApiSchema, query_string = params[:query])
result = ApiSchema.execute(query, variables: variables, context: context, operation_name: operation_name)
if current_user.present? || #skips_authorization.include?(query_parse.selected_operation.selections[0].name)
render json: result
else
render json: {}, status: 401
end
rescue StandardError => e
raise e unless Rails.env.development?
handle_error_in_development(e)
end

Related

DRF clear m2m field via patch with SlugRelatedField

Currently i'm using a SlugRelatedField in my ModelSerializer to represent a m2m-Field.
manager = serializers.SlugRelatedField(
many=True,
queryset=get_user_model().objects.all(),
slug_field='username',
required=False,
allow_null=True)
if i send now a patch request with e.g.
'manager': 'user1' or 'manager: ['user1', 'user2'] it works as expected.
but how can i achieve, that the m2m field gets cleared, when i send an empty array 'manager': []
at the moment nothing happens, when i do so.
If i try to clear it with 'manager': '' i get an "object with username= does not exist" error.
maybe the SlugRelatedField isn't the right choice for me? i just wanted to achieve handling users with their username instead of their pk.
Edit:
I can do a PUT Request with not sending 'manager' at all to clear it.
Is there maybe no way to clear a m2m field with a PATCH Request?
r = requests.patch('http://localhost:8000/api/projects/dfo2/',
auth = ('lala','lilu'),
data = {'developer': []}
)
result:
{'manager': ['peter', 'fllow'], 'description': '','name_short': 'dfo2', 'developer': ['peter'], 'name': 'asdf'}
what works is:
r = requests.patch('http://localhost:8000/api/projects/dfo2/',
auth = ('lala','lilu'),
data = '{"developer": []}',
headers = {'Content-type': 'application/json'}
)
but why does this not work:
r = requests.patch('http://localhost:8000/api/projects/dfo2/',
auth = ('lala','lilu'),
data = {"developer": []},
headers = {'Content-type': 'application/x-www-form-urlencoded'}
)
Without seeing your full code it is hard to know, but generally it seems like you are not dealing with the data properly. One piece of advice I always give people with DRF is to think about serializers as a tool for giving and validating data, but not necessarily describing what to do with it.
With that in mind, I suggest you try something like the following, although again it is hard without seeing the models and full serializer code:
class Projects(models.Model):
managers = models.ManyToManyField('auth.Users')
class ProjectSerializer(serializers.Serializer):
managers = serializers.SlugRelatedField(
many=True,
source='managers.all',
queryset=get_user_model().objects.all(),
slug_field='username',
required=False,
allow_null=True)
def update(self, instance, validated_data):
[instance.managers.add(manager) for manager in validated_data['managers']
return instance

Django rest framework - Raising exception / Handling empty results while filtering

I have a user profile class and am checking if a user exists and if not want to create that user.
Am using the filter class for userprofile so that the client can call :
http://localhost:8000/users/?email=a#b.com
and if the result is empty will create a user with the email address.
Is there a way to intercept the query result and raise an exception when its empty and handle that to create the user.
If there is a better way would like to be corrected as well.
class UserQueryFilter(django_filters.rest_framework.FilterSet):
email = django_filters.CharFilter(name="user__email")
username = django_filters.CharFilter(name="user__username")
class Meta:
model = UserProfile
fields = ['email', 'username']
class UserViewSet(viewsets.ReadOnlyModelViewSet):
queryset = UserProfile.objects.all()
serializer_class = UserSerializer
filter_class = UserQueryFilter
Any help is appreciated.
Thanks
Anand
Django Rest Framework provide a functionality that is disabled by default. Maybe it could give you another approach to resolve your problem: PUT as create
In other hand, if you really need to create the user through a GET request with a querystring, you can use a MethodFilter from django-filters, for example:
class UserFilters(FilterSet):
user = MethodFilter(action='filter_user')
class Meta:
model = User
fields = ['user']
def filter_user(self, queryset, value):
if not value:
# Here Raise Exception
else:
# Check if the user exists, if not create it
users = queryset.filter(Q(username=value) | Q(email=value))
if not users.exists:
user = User.objects.create(...)
return queryset.filter(pk=user.id)
else:
return users
Hope this can help you. I'm not pretty sure about it works in that exact way but it's the idea.
Personally, I recommend you that try to execute that tasks through a more appropriate request like POST or PUT and manage in the corresponding method.

django rest framework 3.1 Handle create/update in ModelSerializer where do i validate the nested data?

I try to create and update resources using a nested representation.
I took a look here -> http://www.django-rest-framework.org/api-guide/serializers/#writable-nested-representations. But i don't know where to put the validation to verify that my nested resources, which is not defined by its id, exists.
json
{
"name": "this is my name"
"network": {
"code": "existing_code",
"operator": "existing_op"
},
}
create method of my serializer
def create(self, validated_data):
network = validated_data.pop("network")
#this could throw a DoesNotExist exception !!!!
validated_data["network"] = Network.objects.get(operator=network["operator"], code=network["code"])
instance = manny.common.models.DeliveryPoint.objects.create(**validated_data)
return instance
Is it okay to check this in the validator directly on the ModelSerializer:
validate method of my serializer
def validate(self, data):
#some code here....
if not Network.objects.filter(operator=data["operator"], code=data["code"]).exists():
raise serializers.ValidationError("network doesn't exist")
return data
Or do i have to rewrite the create method of the ModelViewSet ?
Thanks for your help !
Why don't you use a get_object_or_404 (https://docs.djangoproject.com/fr/1.8/topics/http/shortcuts/#get-object-or-404) instead of Network.objects.get ?
If the Network object doesn't exist, the raised exception will make a 404 error, and you won't need to handle it manually.

SonarQube Single Sign On Plugin, update groups from HttpServletRequest

I am writing a single sign on plugin for SonarQube.
All my user information are located in the UserPrincipal of the HttpRequest (not the password ;-) ).
For the moment, I can sign on and provide the UserDetails to Sonar through the method
public UserDetails doGetUserDetails(Context context);
Now I would like to provide the groups of the user to SonarQube but the method
public Collection<String> doGetGroups(String username)
does not provide the context.
To solve my issue, I have modified the code of need_authentication.rb of Sonar (located in sonarqube-4.4\web\WEB-INF\lib)
to call a method
public Collection<String> doGetGroups(String username, HttpServletRequest)
I have written.
But I don't like modyfing source code; I would prefer overide it by extending the class PluginRealm of the file need_authentication.rb.
Basically, how do I extend a class in SonarQube ?
I know how to overide a controller but not class located in lib folder.
Also (I don't know Ruby at all).
Thank you for your help
SONAR-5430 will allow to natively implement such a feature.
For people who cannot use the solution of Fabrice, here is what I did until the new feature is released.
I create a controller in ruby I add to my plugin realm.
This controller extends ApplicationController controller.
It redefines authenticate method wich calls the default method of Sonar
def authenticate
begin
self.current_user = User.authenticate(nil, nil, servlet_request)
if self.current_user
my_synchronize_groups(servlet_request)
end
rescue Exception => e
self.current_user = nil
Rails.logger.error(e.message)
end
redirect_back_or_default(home_url)
end
When you use a User.authenticate(nil, nil, servlet_request) it calls the synchronize_groups(user) in need_authorization.rb and synchronize the groups.
What I do is calling my_synchronize_groups after User.authenticate(nil, nil, servlet_request) to override the groups synchronization done by sonar.
Here is the code of my_synchronize_groups
def my_synchronize_groups(servlet_request)
Rails.logger.debug("My synchronize_groups method")
myRealm = RealmFactory.realm
Rails.logger.debug("My realm #{myRealm}")
if myRealm
myGroupsProvider = myRealm.instance_variable_get(:#java_groups_provider)
Rails.logger.debug("My group provider #{myGroupsProvider}")
if myGroupsProvider
begin
groups = myGroupsProvider.doGetGroups(servlet_request)
rescue Exception => e
Rails.logger.error("Error from my groups provider: #{e.message}")
else
if groups
self.current_user.groups = []
for group_name in groups
group = Group.find_by_name(group_name)
if group
self.current_user.groups << group
end
end
end
end
end
end
end
The method myGroupsProvider.doGetGroups(servlet_request) is a method located in MyGroupsProvider.java which extends ExternalGroupsProvider.java and return a collection of String.
I am not saying it is the best solution but with my knowledge of Ruby ...
Hoping it can help
Simon

activemodel return json with associated model data

in rails 4, I can not figure out how to fetch model and associated models together.
For example, I have a User model which has_many Message
The following code work properly.
#user = User.find 29, include: :messages
#messages = #user.messages
However, when I try to return #user with #messages in json
render :json #user
The returned result does not contain #messages, only the #user data is returned.
One workaround I can do is to construct a hash with
{user: #user, messages: #messages}
But the problem is messages are not nested or associated to user.
Is there a activemodel/activerecord buildin method to render associated data in a easier way?
as always, the documentation on the rails side is pretty bad. what you need to do is call either call to_json with options or override the models to_json method: http://apidock.com/rails/ActiveRecord/Serialization/to_json

Resources