Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CP-50426: Trace external authentication modules #5901

Merged
merged 3 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 35 additions & 14 deletions ocaml/tests/testauthx.ml
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,22 @@ let usage () =
exit 1

let _ =
let __context = Context.make __MODULE__ in
if Array.length Sys.argv <> 3 then usage () ;
let username = Sys.argv.(1) and password = Sys.argv.(2) in
let hr x = print_endline ("-----------------------------\n" ^ x) in
(* should return 2037 *)
hr ("TEST 1a. Authx.get_subject_identifier " ^ username) ;
let userid = AuthX.methods.get_subject_identifier username in
let userid = AuthX.methods.get_subject_identifier ~__context username in
print_endline ("userid=" ^ userid) ;
hr
("TEST 1b. AuthX.methods.get_subject_identifier "
^ username
^ "_werq (unknown subject)"
) ;
try print_endline (AuthX.methods.get_subject_identifier (username ^ "_werq"))
try
print_endline
(AuthX.methods.get_subject_identifier ~__context (username ^ "_werq"))
with Not_found -> (
print_endline "subject Not_found, as expected" ;
(* should return a list of groups that subjectid 1000 (a user) belongs to *)
Expand All @@ -42,7 +45,7 @@ let _ =
^ " (a user subject)"
) ;
let conc x y = x ^ "," ^ y in
let groupid_list = AuthX.methods.query_group_membership userid in
let groupid_list = AuthX.methods.query_group_membership ~__context userid in
print_endline (List.fold_left conc "" groupid_list) ;
(* should return a list of groups that subjectid 10024 (a group) belongs to *)
let agroup = List.hd groupid_list in
Expand All @@ -52,23 +55,31 @@ let _ =
^ " (a group subject)"
) ;
print_endline
(List.fold_left conc "" (AuthX.methods.query_group_membership agroup)) ;
(List.fold_left conc ""
(AuthX.methods.query_group_membership ~__context agroup)
) ;
hr "TEST 2c. AuthX.methods.query_group_membership u999 (unknown subject)" ;
try
print_endline
(List.fold_left conc "" (AuthX.methods.query_group_membership "u999"))
(List.fold_left conc ""
(AuthX.methods.query_group_membership ~__context "u999")
)
with Not_found -> (
print_endline "subject Not_found, as expected." ;
hr "TEST 2d. AuthX.methods.query_group_membership a999 (unknown subject)" ;
try
print_endline
(List.fold_left conc "" (AuthX.methods.query_group_membership "a999"))
(List.fold_left conc ""
(AuthX.methods.query_group_membership ~__context "a999")
)
with Not_found -> (
print_endline "subject Not_found, as expected." ;
hr "TEST 2e. AuthX.methods.query_group_membership 999 (unknown subject)" ;
try
print_endline
(List.fold_left conc "" (AuthX.methods.query_group_membership "999"))
(List.fold_left conc ""
(AuthX.methods.query_group_membership ~__context "999")
)
with Not_found -> (
print_endline "subject Not_found, as expected." ;
(* should return a list with information about subject_id 1000 (a user)*)
Expand All @@ -77,7 +88,9 @@ let _ =
^ userid
^ " (a user)"
) ;
let infolist1 = AuthX.methods.query_subject_information userid in
let infolist1 =
AuthX.methods.query_subject_information ~__context userid
in
for i = 0 to List.length infolist1 - 1 do
let print_elems (e1, e2) = print_endline (e1 ^ ": " ^ e2) in
print_elems (List.nth infolist1 i)
Expand All @@ -88,7 +101,9 @@ let _ =
^ agroup
^ " (a group)"
) ;
let infolist1 = AuthX.methods.query_subject_information agroup in
let infolist1 =
AuthX.methods.query_subject_information ~__context agroup
in
for i = 0 to List.length infolist1 - 1 do
let print_elems (e1, e2) = print_endline (e1 ^ ": " ^ e2) in
print_elems (List.nth infolist1 i)
Expand All @@ -98,7 +113,9 @@ let _ =
"TEST 3c. AuthX.methods.query_subject_information u999 (unknown \
subject)" ;
try
let infolist1 = AuthX.methods.query_subject_information "u999" in
let infolist1 =
AuthX.methods.query_subject_information ~__context "u999"
in
for i = 0 to List.length infolist1 - 1 do
let print_elems (e1, e2) = print_endline (e1 ^ ": " ^ e2) in
print_elems (List.nth infolist1 i)
Expand All @@ -110,7 +127,9 @@ let _ =
"TEST 3d. AuthX.methods.query_subject_information a999 (unknown \
subject)" ;
try
let infolist1 = AuthX.methods.query_subject_information "a999" in
let infolist1 =
AuthX.methods.query_subject_information ~__context "a999"
in
for i = 0 to List.length infolist1 - 1 do
let print_elems (e1, e2) = print_endline (e1 ^ ": " ^ e2) in
print_elems (List.nth infolist1 i)
Expand All @@ -122,7 +141,9 @@ let _ =
"TEST 3e. AuthX.methods.query_subject_information 999 (unknown \
subject)" ;
try
let infolist1 = AuthX.methods.query_subject_information "999" in
let infolist1 =
AuthX.methods.query_subject_information ~__context "999"
in
for i = 0 to List.length infolist1 - 1 do
let print_elems (e1, e2) = print_endline (e1 ^ ": " ^ e2) in
print_elems (List.nth infolist1 i)
Expand All @@ -134,8 +155,8 @@ let _ =
^ username
) ;
print_endline
(AuthX.methods.authenticate_username_password username
password
(AuthX.methods.authenticate_username_password ~__context
username password
)
)
)
Expand Down
20 changes: 11 additions & 9 deletions ocaml/xapi/auth_signature.ml
Original file line number Diff line number Diff line change
Expand Up @@ -67,20 +67,21 @@ type t = {
the auth module/service itself -- e.g. maybe a SID or something in the AD case).
Raises auth_failure if authentication is not successful
*)
authenticate_username_password: string -> string -> string
authenticate_username_password:
__context:Context.t -> string -> string -> string
; (* subject_id Authenticate_ticket(string ticket)

As above but uses a ticket as credentials (i.e. for single sign-on)
*)
authenticate_ticket: string -> string
authenticate_ticket: __context:Context.t -> string -> string
; (* subject_id get_subject_identifier(string subject_name)

Takes a subject_name (as may be entered into the XenCenter UI when defining subjects --
see Access Control wiki page); and resolves it to a subject_id against the external
auth/directory service.
Raises Not_found if authentication is not succesful.
*)
get_subject_identifier: string -> string
get_subject_identifier: __context:Context.t -> string -> string
; (* ((string*string) list) query_subject_information(string subject_identifier)

Takes a subject_identifier and returns the user record from the directory service as
Expand All @@ -91,15 +92,16 @@ type t = {
it's a string*string list anyway for possible future expansion.
Raises Not_found if subject_id cannot be resolved by external auth service
*)
query_subject_information: string -> (string * string) list
query_subject_information:
__context:Context.t -> string -> (string * string) list
; (* (string list) query_group_membership(string subject_identifier)

Takes a subject_identifier and returns its group membership (i.e. a list of subject
identifiers of the groups that the subject passed in belongs to). The set of groups returned
_must_ be transitively closed wrt the is_member_of relation if the external directory service
supports nested groups (as AD does for example)
*)
query_group_membership: string -> string list
query_group_membership: __context:Context.t -> string -> string list
; (*
In addition, there are some event hooks that auth modules implement as follows:
*)
Expand All @@ -118,26 +120,26 @@ type t = {
explicitly filter any one-time credentials [like AD username/password for example] that it
does not need long-term.]
*)
on_enable: (string * string) list -> unit
on_enable: __context:Context.t -> (string * string) list -> unit
; (* unit on_disable()

Called internally by xapi _on each host_ when a client disables an auth service via the XenAPI.
The hook will be called _before_ the Pool configuration fields relating to the external-auth
service are cleared (i.e. so you can access the config params you need from the pool metadata
within the body of the on_disable method)
*)
on_disable: (string * string) list -> unit
on_disable: __context:Context.t -> (string * string) list -> unit
; (* unit on_xapi_initialize(bool system_boot)

Called internally by xapi whenever it starts up. The system_boot flag is true iff xapi is
starting for the first time after a host boot
*)
on_xapi_initialize: bool -> unit
on_xapi_initialize: __context:Context.t -> bool -> unit
; (* unit on_xapi_exit()

Called internally when xapi is doing a clean exit.
*)
on_xapi_exit: unit -> unit
on_xapi_exit: __context:Context.t -> unit -> unit
}

(* Auth modules must implement this signature:*)
Expand Down
49 changes: 28 additions & 21 deletions ocaml/xapi/authx.ml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ module D = Debug.Make (struct let name = "extauth_plugin_PAM_NSS" end)

open D

let ( let@ ) = ( @@ )

module AuthX : Auth_signature.AUTH_MODULE = struct
(*
* External Authentication Plugin component
Expand Down Expand Up @@ -113,7 +115,8 @@ module AuthX : Auth_signature.AUTH_MODULE = struct
auth/directory service.
Raises Not_found if authentication is not succesful.
*)
let get_subject_identifier subject_name =
let get_subject_identifier ~__context subject_name =
let@ __context = Context.with_tracing ~__context __FUNCTION__ in
try (* looks up list of users*)
"u" ^ getent_idbyname "passwd" subject_name
with Not_found ->
Expand All @@ -131,15 +134,16 @@ module AuthX : Auth_signature.AUTH_MODULE = struct
Raises auth_failure if authentication is not successful
*)

let authenticate_username_password username password =
let authenticate_username_password ~__context username password =
let@ __context = Context.with_tracing ~__context __FUNCTION__ in
(* we try to authenticate against our user database using PAM *)
let () =
try
Pam.authenticate username password
(* no exception raised, then authentication succeeded *)
with Failure msg -> raise (Auth_signature.Auth_failure msg)
in
try get_subject_identifier username
try get_subject_identifier ~__context username
with Not_found ->
raise
(Auth_signature.Auth_failure
Expand All @@ -155,7 +159,7 @@ module AuthX : Auth_signature.AUTH_MODULE = struct
*)
(* not implemented now, not needed for our tests, only for a *)
(* future single sign-on feature *)
let authenticate_ticket _tgt =
let authenticate_ticket ~__context:_ _tgt =
failwith "authx authenticate_ticket not implemented"

(* ((string*string) list) query_subject_information(string subject_identifier)
Expand All @@ -168,7 +172,8 @@ module AuthX : Auth_signature.AUTH_MODULE = struct
it's a string*string list anyway for possible future expansion.
Raises Not_found if subject_id cannot be resolved by external auth service
*)
let query_subject_information subject_identifier =
let query_subject_information ~__context subject_identifier =
let@ __context = Context.with_tracing ~__context __FUNCTION__ in
(* we are expecting an id such as u0, g0, u123 etc *)
if String.length subject_identifier < 2 then raise Not_found ;
match subject_identifier.[0] with
Expand Down Expand Up @@ -246,7 +251,8 @@ module AuthX : Auth_signature.AUTH_MODULE = struct
*)
(* in unix, groups cannot contain groups, so we just verify the groups a user *)
(* belongs to and, if that fails, if some group has the required identifier *)
let query_group_membership subject_identifier =
let query_group_membership ~__context subject_identifier =
let@ __context = Context.with_tracing ~__context __FUNCTION__ in
(* 1. first we try to see if our subject identifier is a user id...*)
let sanitized_subject_id = String.escaped subject_identifier in
(* we are expecting an id such as u0, g0, u123 etc *)
Expand Down Expand Up @@ -303,7 +309,7 @@ module AuthX : Auth_signature.AUTH_MODULE = struct
explicitly filter any one-time credentials [like AD username/password for example] that it
does not need long-term.]
*)
let on_enable _config_params =
let on_enable ~__context:_ _config_params =
(* nothing to do in this unix plugin, we always have /etc/passwd and /etc/group *)
()

Expand All @@ -314,7 +320,7 @@ module AuthX : Auth_signature.AUTH_MODULE = struct
service are cleared (i.e. so you can access the config params you need from the pool metadata
within the body of the on_disable method)
*)
let on_disable _config_params =
let on_disable ~__context:_ _config_params =
(* nothing to disable in this unix plugin, we should not disable /etc/passwd and /etc/group:) *)
()

Expand All @@ -323,29 +329,30 @@ module AuthX : Auth_signature.AUTH_MODULE = struct
Called internally by xapi whenever it starts up. The system_boot flag is true iff xapi is
starting for the first time after a host boot
*)
let on_xapi_initialize _system_boot =
let on_xapi_initialize ~__context:_ _system_boot =
(* again, nothing to be initialized here in this unix plugin *)
()

(* unit on_xapi_exit()

Called internally when xapi is doing a clean exit.
*)
let on_xapi_exit () =
let on_xapi_exit ~__context:_ () =
(* nothing to do here in this unix plugin *)
()

(* Implement the single value required for the module signature *)
let methods =
{
Auth_signature.authenticate_username_password
; Auth_signature.authenticate_ticket
; Auth_signature.get_subject_identifier
; Auth_signature.query_subject_information
; Auth_signature.query_group_membership
; Auth_signature.on_enable
; Auth_signature.on_disable
; Auth_signature.on_xapi_initialize
; Auth_signature.on_xapi_exit
}
Auth_signature.
{
authenticate_username_password
; authenticate_ticket
; get_subject_identifier
; query_subject_information
; query_group_membership
; on_enable
; on_disable
; on_xapi_initialize
; on_xapi_exit
}
end
Loading
Loading