BartOtten

BartOtten

Currently I am writing a small macro which adds authorization functionality to a function. Rewrite ‘def’ to ‘defprot’ and add the rules to an Authorization module. Too easy it is.

defmodule One do
defprot bar(), do: :result
end

defmodule Two do
def foo(), do: One.bar()
end

In order to write nice debug information and add the possibility to create enforced boundaries between (context) modules, I need the names of the ‘calling’ Function and Module in a macro. Saving them as an environment variable and retrieving them in the macro does work while developing but A: ain’t nice and B: would be useless in case of concurrency.

The output should become:
iex > Two.foo()
[debug] "Two.foo() requested authorization to access One.bar()"

Is there a good way to retrieve the information of the calling Module (Two) and calling Function (foo) while in One.bar()?

Showing Posts 1 to 10

OvermindDL1

OvermindDL1

For Macro’s you can access the callers environment inside the __CALLER__ argument (implicitly passed in to every macro call). :slight_smile:

It’s just a normal Macro.Env struct.

BartOtten

BartOtten OP

__CALLER__ returns the current calling environment as a Macro.Env struct; a struct that holds compile time environment information. That’s why you can’t use it in a quote block.

However, I need run time information. When function Two.foo/1 is calling function One.bar/1 as in the example, the caller should be Two.foo/1 (info might be split in callingMod en callingFunc)

NobbZ

NobbZ

A function should not care from where it is called. It should return the same regardless the caller.

If though you really have to, you could do it similar how logger injects metadata.

Use a macro which takes the arguments defined by your API, injects code to collect metadata and then delegates to a function that takes the actual arguments AND the collected metadata.

michalmuskala

michalmuskala

You can use Process.info(self(), :current_stacktrace) to get the current stacktrace (and the calling function should be there), but this is generally considered a debugging utility, not something to be used in production.

Additionally, be aware that tail calls don’t produce stack entries, so in a code like this:

def foo(), do: bar()
def bar(), do: Process.info(self(), :current_stacktrace)

The stacktrace information won’t include foo().

BartOtten

BartOtten OP

Figured it out myself, but michalmuska was right. Code became:

{callingMod, callingFunc, callingFuncArity, [file: _file, line: _line]} =
      Process.info(self(), :current_stacktrace) |> elem(1) |> Enum.fetch!(2)

Now I need to handle the ‘no stack entry’ situation, somehow :slight_smile:

@NobbZ Will have a look at the proposal. Thanks for the input.

OvermindDL1

OvermindDL1

I’m exceptionally curious, “what” are you trying to accomplish? o.O

BartOtten

BartOtten OP

An insane experiment to build a non plug-based authorization code which can easily applied on existing modules, functions, con(n/text) and even arguments. In Phoenix applications authorization is quite easy as Phoenix uses conventions and does normalize a lot to (conn, object map, current_user). Custom applications don’t have such thing. I doubt I will even succeed, but it’s worth to spend a week of my free time on as I learn a lot :slight_smile:

Had a working POC which needed environment variables to work; which would make it useless. If I can take those out, it might actually be a nice solution to secure an otherwise insecure codebase without much rewrites.

  @doc """
  Replace a function definition, adding a call to the authorization function.
  Example:
      defprot function(arg1, arg2), do: IO.inspect({arg1,arg2})
      function(1,2)

  will call authorize with this parameters

      authorizate(__MODULE__, :function, [arg1: 1, arg2: 2], nil)

  """

If there is a solid way to track which Module and Function did the request, it will also be possible to pattern match at those. Allowing (for example) to mimic defp (just because we can…) or reject all calls to functions which write to the database unless the authorization rules allow them to.

Edit: If I ever find a good solution, I will try to have it added to GitHub - arjan/decorator: Function decorators for Elixir · GitHub, as that lib is doing 90% of what I wrote already. Now only those few last bits…

Edit2: Gotta get some sleep…sigh

BartOtten

BartOtten OP

Status update:

It is possible, yet it has so many side effects it will never see an open repository for too many good reasons.

iex(auth@127.0.0.1)1> Example.test3
Example.test3() was called when no origin was set. Are you using IEX?
Example.test1() was called by an anonymous function with was called by Example.test3()
Example.test0() was called by Example.test1()
[info] Example.test0 requested authorization to access Example.test/3
[info] Access granted
#PID<0.421.0>
iex(auth@127.0.0.1)2>

Conclusion: Meta programming and message passing between processes rock. Never underestimate their power, but use them wisely.

Read before complains

I did not bother to print arity instead of () anymore as the experiment has ended.

OvermindDL1

OvermindDL1

Hah, interesting though. ^.^

BartOtten

BartOtten OP

I’ll clean up the POC one day and request some additional functionality in Elixir to make it possible without a zillion hacks. Now I have a demo to demonstrate the concept :slight_smile:

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews