• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  * Copyright 2015 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #include <ruby/ruby.h>
20 
21 #include "rb_call_credentials.h"
22 #include "rb_grpc_imports.generated.h"
23 
24 #include <ruby/thread.h>
25 
26 #include <grpc/grpc.h>
27 #include <grpc/grpc_security.h>
28 #include <grpc/support/alloc.h>
29 #include <grpc/support/log.h>
30 
31 #include "rb_call.h"
32 #include "rb_event_thread.h"
33 #include "rb_grpc.h"
34 
35 /* grpc_rb_cCallCredentials is the ruby class that proxies
36  * grpc_call_credentials */
37 static VALUE grpc_rb_cCallCredentials = Qnil;
38 
39 /* grpc_rb_call_credentials wraps a grpc_call_credentials. It provides a mark
40  * object that is used to hold references to any objects used to create the
41  * credentials. */
42 typedef struct grpc_rb_call_credentials {
43   /* Holder of ruby objects involved in contructing the credentials */
44   VALUE mark;
45 
46   /* The actual credentials */
47   grpc_call_credentials* wrapped;
48 } grpc_rb_call_credentials;
49 
50 typedef struct callback_params {
51   VALUE get_metadata;
52   grpc_auth_metadata_context context;
53   void* user_data;
54   grpc_credentials_plugin_metadata_cb callback;
55 } callback_params;
56 
grpc_rb_call_credentials_callback(VALUE callback_args)57 static VALUE grpc_rb_call_credentials_callback(VALUE callback_args) {
58   VALUE result = rb_hash_new();
59   VALUE metadata = rb_funcall(rb_ary_entry(callback_args, 0), rb_intern("call"),
60                               1, rb_ary_entry(callback_args, 1));
61   rb_hash_aset(result, rb_str_new2("metadata"), metadata);
62   rb_hash_aset(result, rb_str_new2("status"), INT2NUM(GRPC_STATUS_OK));
63   rb_hash_aset(result, rb_str_new2("details"), rb_str_new2(""));
64   return result;
65 }
66 
grpc_rb_call_credentials_callback_rescue(VALUE args,VALUE exception_object)67 static VALUE grpc_rb_call_credentials_callback_rescue(VALUE args,
68                                                       VALUE exception_object) {
69   VALUE result = rb_hash_new();
70   VALUE backtrace =
71       rb_funcall(rb_funcall(exception_object, rb_intern("backtrace"), 0),
72                  rb_intern("join"), 1, rb_str_new2("\n\tfrom "));
73   VALUE rb_exception_info =
74       rb_funcall(exception_object, rb_intern("inspect"), 0);
75   (void)args;
76   gpr_log(GPR_INFO, "Call credentials callback failed: %s\n%s",
77           StringValueCStr(rb_exception_info), StringValueCStr(backtrace));
78   rb_hash_aset(result, rb_str_new2("metadata"), Qnil);
79   rb_hash_aset(result, rb_str_new2("status"),
80                INT2NUM(GRPC_STATUS_UNAUTHENTICATED));
81   rb_hash_aset(result, rb_str_new2("details"), rb_exception_info);
82   return result;
83 }
84 
grpc_rb_call_credentials_callback_with_gil(void * param)85 static void grpc_rb_call_credentials_callback_with_gil(void* param) {
86   callback_params* const params = (callback_params*)param;
87   VALUE auth_uri = rb_str_new_cstr(params->context.service_url);
88   /* Pass the arguments to the proc in a hash, which currently only has they key
89      'auth_uri' */
90   VALUE callback_args = rb_ary_new();
91   VALUE args = rb_hash_new();
92   VALUE result;
93   grpc_metadata_array md_ary;
94   grpc_status_code status;
95   VALUE details;
96   char* error_details;
97   grpc_metadata_array_init(&md_ary);
98   rb_hash_aset(args, ID2SYM(rb_intern("jwt_aud_uri")), auth_uri);
99   rb_ary_push(callback_args, params->get_metadata);
100   rb_ary_push(callback_args, args);
101   result = rb_rescue(grpc_rb_call_credentials_callback, callback_args,
102                      grpc_rb_call_credentials_callback_rescue, Qnil);
103   // Both callbacks return a hash, so result should be a hash
104   grpc_rb_md_ary_convert(rb_hash_aref(result, rb_str_new2("metadata")),
105                          &md_ary);
106   status = NUM2INT(rb_hash_aref(result, rb_str_new2("status")));
107   details = rb_hash_aref(result, rb_str_new2("details"));
108   error_details = StringValueCStr(details);
109   params->callback(params->user_data, md_ary.metadata, md_ary.count, status,
110                    error_details);
111   grpc_rb_metadata_array_destroy_including_entries(&md_ary);
112   gpr_free(params);
113 }
114 
grpc_rb_call_credentials_plugin_get_metadata(void * state,grpc_auth_metadata_context context,grpc_credentials_plugin_metadata_cb cb,void * user_data,grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],size_t * num_creds_md,grpc_status_code * status,const char ** error_details)115 static int grpc_rb_call_credentials_plugin_get_metadata(
116     void* state, grpc_auth_metadata_context context,
117     grpc_credentials_plugin_metadata_cb cb, void* user_data,
118     grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
119     size_t* num_creds_md, grpc_status_code* status,
120     const char** error_details) {
121   callback_params* params = gpr_malloc(sizeof(callback_params));
122   params->get_metadata = (VALUE)state;
123   params->context = context;
124   params->user_data = user_data;
125   params->callback = cb;
126 
127   grpc_rb_event_queue_enqueue(grpc_rb_call_credentials_callback_with_gil,
128                               (void*)(params));
129   return 0;  // Async return.
130 }
131 
grpc_rb_call_credentials_plugin_destroy(void * state)132 static void grpc_rb_call_credentials_plugin_destroy(void* state) {
133   (void)state;
134   // Not sure what needs to be done here
135 }
136 
137 /* Destroys the credentials instances. */
grpc_rb_call_credentials_free(void * p)138 static void grpc_rb_call_credentials_free(void* p) {
139   grpc_rb_call_credentials* wrapper;
140   if (p == NULL) {
141     return;
142   }
143   wrapper = (grpc_rb_call_credentials*)p;
144   grpc_call_credentials_release(wrapper->wrapped);
145   wrapper->wrapped = NULL;
146 
147   xfree(p);
148 }
149 
150 /* Protects the mark object from GC */
grpc_rb_call_credentials_mark(void * p)151 static void grpc_rb_call_credentials_mark(void* p) {
152   grpc_rb_call_credentials* wrapper = NULL;
153   if (p == NULL) {
154     return;
155   }
156   wrapper = (grpc_rb_call_credentials*)p;
157   if (wrapper->mark != Qnil) {
158     rb_gc_mark(wrapper->mark);
159   }
160 }
161 
162 static rb_data_type_t grpc_rb_call_credentials_data_type = {
163     "grpc_call_credentials",
164     {grpc_rb_call_credentials_mark,
165      grpc_rb_call_credentials_free,
166      GRPC_RB_MEMSIZE_UNAVAILABLE,
167      {NULL, NULL}},
168     NULL,
169     NULL,
170 #ifdef RUBY_TYPED_FREE_IMMEDIATELY
171     RUBY_TYPED_FREE_IMMEDIATELY
172 #endif
173 };
174 
175 /* Allocates CallCredentials instances.
176    Provides safe initial defaults for the instance fields. */
grpc_rb_call_credentials_alloc(VALUE cls)177 static VALUE grpc_rb_call_credentials_alloc(VALUE cls) {
178   grpc_rb_call_credentials* wrapper = ALLOC(grpc_rb_call_credentials);
179   wrapper->wrapped = NULL;
180   wrapper->mark = Qnil;
181   return TypedData_Wrap_Struct(cls, &grpc_rb_call_credentials_data_type,
182                                wrapper);
183 }
184 
185 /* Creates a wrapping object for a given call credentials. This should only be
186  * called with grpc_call_credentials objects that are not already associated
187  * with any Ruby object */
grpc_rb_wrap_call_credentials(grpc_call_credentials * c,VALUE mark)188 VALUE grpc_rb_wrap_call_credentials(grpc_call_credentials* c, VALUE mark) {
189   VALUE rb_wrapper;
190   grpc_rb_call_credentials* wrapper;
191   if (c == NULL) {
192     return Qnil;
193   }
194   rb_wrapper = grpc_rb_call_credentials_alloc(grpc_rb_cCallCredentials);
195   TypedData_Get_Struct(rb_wrapper, grpc_rb_call_credentials,
196                        &grpc_rb_call_credentials_data_type, wrapper);
197   wrapper->wrapped = c;
198   wrapper->mark = mark;
199   return rb_wrapper;
200 }
201 
202 /* The attribute used on the mark object to hold the callback */
203 static ID id_callback;
204 
205 /*
206   call-seq:
207     creds = Credentials.new auth_proc
208   proc: (required) Proc that generates auth metadata
209   Initializes CallCredential instances. */
grpc_rb_call_credentials_init(VALUE self,VALUE proc)210 static VALUE grpc_rb_call_credentials_init(VALUE self, VALUE proc) {
211   grpc_rb_call_credentials* wrapper = NULL;
212   grpc_call_credentials* creds = NULL;
213   grpc_metadata_credentials_plugin plugin;
214 
215   grpc_ruby_once_init();
216 
217   TypedData_Get_Struct(self, grpc_rb_call_credentials,
218                        &grpc_rb_call_credentials_data_type, wrapper);
219 
220   plugin.get_metadata = grpc_rb_call_credentials_plugin_get_metadata;
221   plugin.destroy = grpc_rb_call_credentials_plugin_destroy;
222   if (!rb_obj_is_proc(proc)) {
223     rb_raise(rb_eTypeError, "Argument to CallCredentials#new must be a proc");
224     return Qnil;
225   }
226   plugin.state = (void*)proc;
227   plugin.type = "";
228 
229   creds = grpc_metadata_credentials_create_from_plugin(plugin, NULL);
230   if (creds == NULL) {
231     rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why");
232     return Qnil;
233   }
234 
235   wrapper->mark = proc;
236   wrapper->wrapped = creds;
237   rb_ivar_set(self, id_callback, proc);
238 
239   return self;
240 }
241 
grpc_rb_call_credentials_compose(int argc,VALUE * argv,VALUE self)242 static VALUE grpc_rb_call_credentials_compose(int argc, VALUE* argv,
243                                               VALUE self) {
244   grpc_call_credentials* creds;
245   grpc_call_credentials* other;
246   grpc_call_credentials* prev = NULL;
247   VALUE mark;
248   if (argc == 0) {
249     return self;
250   }
251   mark = rb_ary_new();
252   creds = grpc_rb_get_wrapped_call_credentials(self);
253   for (int i = 0; i < argc; i++) {
254     rb_ary_push(mark, argv[i]);
255     other = grpc_rb_get_wrapped_call_credentials(argv[i]);
256     creds = grpc_composite_call_credentials_create(creds, other, NULL);
257     if (prev != NULL) {
258       grpc_call_credentials_release(prev);
259     }
260     prev = creds;
261   }
262   return grpc_rb_wrap_call_credentials(creds, mark);
263 }
264 
Init_grpc_call_credentials()265 void Init_grpc_call_credentials() {
266   grpc_rb_cCallCredentials =
267       rb_define_class_under(grpc_rb_mGrpcCore, "CallCredentials", rb_cObject);
268 
269   /* Allocates an object managed by the ruby runtime */
270   rb_define_alloc_func(grpc_rb_cCallCredentials,
271                        grpc_rb_call_credentials_alloc);
272 
273   /* Provides a ruby constructor and support for dup/clone. */
274   rb_define_method(grpc_rb_cCallCredentials, "initialize",
275                    grpc_rb_call_credentials_init, 1);
276   rb_define_method(grpc_rb_cCallCredentials, "initialize_copy",
277                    grpc_rb_cannot_init_copy, 1);
278   rb_define_method(grpc_rb_cCallCredentials, "compose",
279                    grpc_rb_call_credentials_compose, -1);
280 
281   id_callback = rb_intern("__callback");
282 }
283 
284 /* Gets the wrapped grpc_call_credentials from the ruby wrapper */
grpc_rb_get_wrapped_call_credentials(VALUE v)285 grpc_call_credentials* grpc_rb_get_wrapped_call_credentials(VALUE v) {
286   grpc_rb_call_credentials* wrapper = NULL;
287   TypedData_Get_Struct(v, grpc_rb_call_credentials,
288                        &grpc_rb_call_credentials_data_type, wrapper);
289   return wrapper->wrapped;
290 }
291