From e37ac34629260f4a269c386d2ebca3106a1677d9 Mon Sep 17 00:00:00 2001 From: Correl Roush Date: Tue, 26 Apr 2016 17:21:43 -0400 Subject: [PATCH] Users & Groups --- priv/dispatch.conf | 4 ++- rebar.config | 7 ++-- src/elmdap-entry.lfe | 17 ++++++++++ src/elmdap-groups.lfe | 45 +++++++++++++++++++++++++ src/{elmdap-uid.lfe => elmdap-user.lfe} | 25 +++----------- src/elmdap-users.lfe | 45 +++++++++++++++++++++++++ src/elmdap.app.src | 1 + src/elmdap.lfe | 26 +++++++++++--- 8 files changed, 141 insertions(+), 29 deletions(-) create mode 100644 src/elmdap-entry.lfe create mode 100644 src/elmdap-groups.lfe rename src/{elmdap-uid.lfe => elmdap-user.lfe} (51%) create mode 100644 src/elmdap-users.lfe diff --git a/priv/dispatch.conf b/priv/dispatch.conf index 96b2dfc..db86337 100644 --- a/priv/dispatch.conf +++ b/priv/dispatch.conf @@ -1,4 +1,6 @@ %% -*- mode: erlang -*- {["static", '*'], 'elmdap-static', []}. -{["uid", uid], 'elmdap-uid', []}. +{["users", uid], 'elmdap-user', []}. +{["users"], 'elmdap-users', []}. +{["groups"], 'elmdap-groups', []}. diff --git a/rebar.config b/rebar.config index 797f801..bb3a519 100644 --- a/rebar.config +++ b/rebar.config @@ -3,11 +3,12 @@ {deps, [ {lfe, {git, "git://github.com/rvirding/lfe", {tag, "1.0"}}}, + {calrissian, {git, "git://github.com/correl/calrissian", {branch, "rebar3"}}}, {webmachine, ".*", {git, "git://github.com/basho/webmachine.git", {branch, "master"}}} ]}. {plugins, [ - {'lfe-compile', {git, "https://github.com/lfe-rebar3/compile.git", {tag, "0.3.0"}}} + {'lfe-compile', {git, "https://github.com/lfe-rebar3/compile.git", {tag, "0.4.0"}}} ]}. {provider_hooks, [ @@ -30,8 +31,8 @@ {profiles, [ {dev, [ {plugins, [ - {'lfe-version', ".*", {git, "https://github.com/lfe-rebar3/version.git", {tag, "0.3.0"}}}, - {'lfe-repl', ".*", {git, "https://github.com/lfe-rebar3/repl.git", {tag, "0.2.0"}}}, + {'lfe-version', ".*", {git, "https://github.com/lfe-rebar3/version.git", {tag, "0.4.0"}}}, + {'lfe-repl', ".*", {git, "https://github.com/lfe-rebar3/repl.git", {tag, "0.2.1"}}}, {'lfe-clean', ".*", {git, "https://github.com/lfe-rebar3/clean.git", {tag, "0.2.0"}}} ]} ]}, diff --git a/src/elmdap-entry.lfe b/src/elmdap-entry.lfe new file mode 100644 index 0000000..1213499 --- /dev/null +++ b/src/elmdap-entry.lfe @@ -0,0 +1,17 @@ +(defmodule elmdap-entry + (export all)) + +(defun dn + ((`#(eldap_entry ,dn ,_)) + dn)) + +(defun attributes + ((`#(eldap_entry ,_ ,attributes)) + attributes)) + +(defun to_json (entry) + `#(struct [#(dn ,(dn entry)) + #(attributes + #(struct ,(lists:map + (match-lambda ((`#(,k ,v)) `#(,k #(array ,v)))) + (attributes entry))))])) diff --git a/src/elmdap-groups.lfe b/src/elmdap-groups.lfe new file mode 100644 index 0000000..664a7a5 --- /dev/null +++ b/src/elmdap-groups.lfe @@ -0,0 +1,45 @@ +(defmodule elmdap-groups + (export (init 1) + (service_available 2) + (is_authorized 2) + (content_types_provided 2) + (to_json 2))) + +(include-lib "webmachine/include/webmachine.hrl") +(include-lib "calrissian/include/monads.lfe") + +(defrecord state + connection) + +(defun service_available (req-data state) + (case (elmdap:open) + ((tuple 'ok connection) + `#(true ,req-data ,(set-state state connection connection))) + (_ + `#(false ,req-data ,state)))) + +(defun is_authorized (req-data state) + (elmdap:is_authorized (state-connection state) + req-data state)) + +(defun init (args) + `#(ok ,(make-state))) + +(defun content_types_provided (req-data state) + `#([#("application/json" to_json)] ,req-data ,state)) + +(defun to_json (req-data state) + (tuple (mochijson:encode + `#(array ,(lists:map (fun elmdap-entry to_json 1) + (groups (state-connection state))))) + req-data + state)) + +(defun groups (connection) + (let* ((base "ou=groups,dc=coredial,dc=com") + (filter (eldap:equalityMatch "objectClass" "groupOfNames")) + (`#(ok #(eldap_search_result ,results ,_)) + (eldap:search connection + `[#(base ,base) + #(filter ,filter)]))) + results)) diff --git a/src/elmdap-uid.lfe b/src/elmdap-user.lfe similarity index 51% rename from src/elmdap-uid.lfe rename to src/elmdap-user.lfe index e88ebf2..e6da194 100644 --- a/src/elmdap-uid.lfe +++ b/src/elmdap-user.lfe @@ -1,4 +1,4 @@ -(defmodule elmdap-uid +(defmodule elmdap-user (export (init 1) (service_available 2) (resource_exists 2) @@ -22,35 +22,20 @@ (defun resource_exists (req-data state) (let ((`[#(uid ,uid)] (wrq:path_info req-data))) - (case (elmdap:from-uid uid) + (case (elmdap:from-uid (state-connection state) uid) (`#(ok ,entry) `#(true ,req-data ,(set-state state entry entry))) (_ `#(false ,req-data ,state))))) (defun is_authorized (req-data state) - (let ((auth-header "Basic realm=Elmdap")) - (case (elmdap:basic-auth-credentials req-data) - (`#(ok ,uid ,password) - (case (elmdap:from-uid uid) - (`#(ok ,entry) - (case (eldap:simple_bind (state-connection state) - (elmdap:entry-dn entry) - password) - ('ok `#(true ,req-data ,state)) - (_ `#(,auth-header ,req-data ,state)))) - (_ `#(,auth-header ,req-data ,state)))) - (_ `#(,auth-header ,req-data ,state))))) + (elmdap:is_authorized (state-connection state) + req-data state)) (defun content_types_provided (req-data state) `#((#("application/json" to_json)) ,req-data ,state)) (defun to_json (req-data state) (let* ((entry (state-entry state))) - (tuple (mochijson:encode - `#(struct [#(dn ,(elmdap:entry-dn entry)) - #(attributes - #(struct ,(lists:map - (match-lambda ((`#(,k ,v)) `#(,k #(array ,v)))) - (elmdap:entry-attributes entry))))])) + (tuple (mochijson:encode (elmdap-entry:to_json entry)) req-data state))) diff --git a/src/elmdap-users.lfe b/src/elmdap-users.lfe new file mode 100644 index 0000000..eba1e54 --- /dev/null +++ b/src/elmdap-users.lfe @@ -0,0 +1,45 @@ +(defmodule elmdap-users + (export (init 1) + (service_available 2) + (is_authorized 2) + (content_types_provided 2) + (to_json 2))) + +(include-lib "webmachine/include/webmachine.hrl") +(include-lib "calrissian/include/monads.lfe") + +(defrecord state + connection) + +(defun service_available (req-data state) + (case (elmdap:open) + ((tuple 'ok connection) + `#(true ,req-data ,(set-state state connection connection))) + (_ + `#(false ,req-data ,state)))) + +(defun is_authorized (req-data state) + (elmdap:is_authorized (state-connection state) + req-data state)) + +(defun init (args) + `#(ok ,(make-state))) + +(defun content_types_provided (req-data state) + `#([#("application/json" to_json)] ,req-data ,state)) + +(defun to_json (req-data state) + (tuple (mochijson:encode + `#(array ,(lists:map (fun elmdap-entry to_json 1) + (users (state-connection state))))) + req-data + state)) + +(defun users (connection) + (let* ((base "ou=people,dc=coredial,dc=com") + (filter (eldap:equalityMatch "objectClass" "inetOrgPerson")) + (`#(ok #(eldap_search_result ,results ,_)) + (eldap:search connection + `[#(base ,base) + #(filter ,filter)]))) + results)) diff --git a/src/elmdap.app.src b/src/elmdap.app.src index 3cddf07..922ddcf 100644 --- a/src/elmdap.app.src +++ b/src/elmdap.app.src @@ -5,6 +5,7 @@ {applications, [kernel, stdlib, + calrissian, inets, crypto, mochiweb, diff --git a/src/elmdap.lfe b/src/elmdap.lfe index 76d2992..9cef84f 100644 --- a/src/elmdap.lfe +++ b/src/elmdap.lfe @@ -1,6 +1,8 @@ (defmodule elmdap (export all)) +(include-lib "calrissian/include/monads.lfe") + ;; Public API (defun entry-dn @@ -12,6 +14,11 @@ attributes)) (defun from-uid (uid) + (do-m (monad 'error) + (connection <- (open)) + (from-uid connection uid))) + +(defun from-uid (connection uid) (let* ((`#(ok ,connection) (open)) (base "ou=people,dc=coredial,dc=com") (filter (eldap:equalityMatch "uid" uid)) @@ -37,11 +44,20 @@ ((binary "Basic " (base64 binary)) (case (string:tokens (base64:mime_decode_to_string base64) ":") ((list uid password) - `#(ok ,uid ,password)) + `#(ok #(,uid ,password))) (_ #(error bad_data)))) (_ #(error bad_method)))))) -(defun valid-auth? (connection dn password) - (case (eldap:simple_bind connection dn password) - ('ok 'true) - (_ 'false))) +(defun is_authorized (connection req-data state) + (let-function ((fst (match-lambda ((`#(,a ,_)) a))) + (snd (match-lambda ((`#(,_ ,b)) b)))) + (let ((result + (do-m (monad 'error) + (credentials <- (elmdap:basic-auth-credentials req-data)) + (entry <- (elmdap:from-uid (fst credentials))) + (eldap:simple_bind connection + (elmdap:entry-dn entry) + (snd credentials))))) + (case result + ('ok (tuple 'true req-data state)) + (_ (tuple "Basic realm=Elmdap" req-data state))))))