Commit 7fd440f1 authored by ale's avatar ale
Browse files

refactor and simplify 2.4 module structure

All tests pass now, group behavior has been restored to match previous
functionality.
parent 5cb573e5
...@@ -52,16 +52,37 @@ typedef struct { ...@@ -52,16 +52,37 @@ typedef struct {
// Note: public_key is a binary buffer (non zero-terminated). // Note: public_key is a binary buffer (non zero-terminated).
const unsigned char *public_key; const unsigned char *public_key;
// All known groups. // All known groups (2.4: unused).
apr_array_header_t *groups; apr_array_header_t *groups;
} modsso_config; } modsso_config;
typedef struct {
apr_array_header_t *user_groups;
} modsso_request_notes;
typedef const char *(*CMD_HAND_TYPE) (); typedef const char *(*CMD_HAND_TYPE) ();
static char *groups_array_to_commasep_string(apr_pool_t *p, apr_array_header_t *groups)
{
return apr_array_pstrcat(p, groups, ',');
}
static char *groups_charp_to_string(apr_pool_t *p, const char **groups) {
apr_array_header_t *arr = apr_array_make(p, 1, sizeof(char *));
const char **gptr;
for (gptr = groups; *gptr; gptr++) {
*(const char **)apr_array_push(arr) = *gptr;
}
return groups_array_to_commasep_string(p, arr);
}
static char **groups_array_to_charpp(apr_pool_t *p, apr_array_header_t *groups) {
int i;
char **pp, **ptr;
pp = (char **)apr_palloc(p, sizeof(char *) * (groups->nelts + 1));
for (ptr = pp, i = 0; i < groups->nelts; i++) {
*ptr++ = APR_ARRAY_IDX(groups, i, char *);
}
*ptr = NULL;
return pp;
}
/** /**
* Create a modsso_config structure. * Create a modsso_config structure.
* *
...@@ -155,25 +176,6 @@ static const char *set_modsso_public_key_file(cmd_parms *parms, void *mconfig, c ...@@ -155,25 +176,6 @@ static const char *set_modsso_public_key_file(cmd_parms *parms, void *mconfig, c
return NULL; return NULL;
} }
static apr_array_header_t *parse_commasep_groups(apr_pool_t *pool, const char *commaseplist) {
apr_array_header_t *arr = apr_array_make(pool, 1, sizeof(const char *));
char *tokenizerCtx = NULL, *group;
char *tmp = apr_pstrdup(pool, commaseplist);
group = apr_strtok(tmp, ",", &tokenizerCtx);
do {
*(const char **)apr_array_push(arr) = group;
group = apr_strtok(NULL, ",", &tokenizerCtx);
} while (group != NULL);
return arr;
}
static const char *set_modsso_groups(cmd_parms *parms, void *mconfig, const char *arg)
{
modsso_config *s_cfg = (modsso_config *)mconfig;
s_cfg->groups = parse_commasep_groups(parms->pool, arg);
return NULL;
}
static const command_rec mod_sso_cmds[] = static const command_rec mod_sso_cmds[] =
{ {
AP_INIT_TAKE1("SSOLoginServer", (CMD_HAND_TYPE) set_modsso_login_server, AP_INIT_TAKE1("SSOLoginServer", (CMD_HAND_TYPE) set_modsso_login_server,
...@@ -188,9 +190,6 @@ static const command_rec mod_sso_cmds[] = ...@@ -188,9 +190,6 @@ static const command_rec mod_sso_cmds[] =
AP_INIT_TAKE1("SSOPublicKeyFile", (CMD_HAND_TYPE) set_modsso_public_key_file, AP_INIT_TAKE1("SSOPublicKeyFile", (CMD_HAND_TYPE) set_modsso_public_key_file,
NULL, RSRC_CONF, NULL, RSRC_CONF,
"SSOPublicKeyFile (string) Location of the login server public key"), "SSOPublicKeyFile (string) Location of the login server public key"),
AP_INIT_TAKE1("SSOGroups", (CMD_HAND_TYPE) set_modsso_groups,
NULL, RSRC_CONF,
"SSOGroups (string) comma-separated list of all the groups that we might want to check membership for"),
{NULL} {NULL}
}; };
...@@ -427,9 +426,6 @@ static int mod_sso_method_handler(request_rec *r) ...@@ -427,9 +426,6 @@ static int mod_sso_method_handler(request_rec *r)
ap_get_module_config(r->per_dir_config, &sso_module); ap_get_module_config(r->per_dir_config, &sso_module);
uri = r->uri; uri = r->uri;
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"sso: handler \"%s\"", r->handler);
// Return immediately if there's nothing to do (check the AuthType) // Return immediately if there's nothing to do (check the AuthType)
type = ap_auth_type(r); type = ap_auth_type(r);
if (!type || strcasecmp(type, "SSO") != 0) { if (!type || strcasecmp(type, "SSO") != 0) {
...@@ -502,13 +498,13 @@ static int mod_sso_method_handler(request_rec *r) ...@@ -502,13 +498,13 @@ static int mod_sso_method_handler(request_rec *r)
return DECLINED; return DECLINED;
} }
#if MODULE_MAGIC_NUMBER_MAJOR < 20100714
struct modsso_auth_req { struct modsso_auth_req {
apr_array_header_t *groups; apr_array_header_t *groups;
apr_array_header_t *users; apr_array_header_t *users;
int any_user; int any_user;
}; };
#if MODULE_MAGIC_NUMBER_MAJOR < 20100714
static int array_contains(apr_array_header_t *arr, const char *s) static int array_contains(apr_array_header_t *arr, const char *s)
{ {
int i; int i;
...@@ -588,28 +584,11 @@ static void mod_sso_parse_requirements(request_rec *r, ...@@ -588,28 +584,11 @@ static void mod_sso_parse_requirements(request_rec *r,
} }
#endif #endif
static char *encode_groups(apr_pool_t *p, apr_array_header_t *groups)
{
/**
apr_array_header_t *arr = apr_array_make(p, (groups->nelts - 1) * 2 - 1, sizeof(const char *));
int i;
for (i = 0; i < groups->nelts; i++) {
if (i > 0) {
*(const char **)apr_array_push(arr) = ",";
}
*(const char **)apr_array_push(arr) = ((const char **)groups->elts)[i];
}
return apr_array_pstrcat(p, arr, 0);
**/
return apr_array_pstrcat(p, groups, ',');
}
static int redirect_to_login_server(request_rec *r, static int redirect_to_login_server(request_rec *r,
const char *login_server, const char *login_server,
const char *service_host, const char *service_host,
const char *service, const char *service,
apr_array_header_t *groups) const char **groups)
{ {
char *dest, *login_url; char *dest, *login_url;
dest = full_uri(r, service_host); dest = full_uri(r, service_host);
...@@ -620,11 +599,11 @@ static int redirect_to_login_server(request_rec *r, ...@@ -620,11 +599,11 @@ static int redirect_to_login_server(request_rec *r,
"&d=", "&d=",
modsso_url_encode(r->pool, dest), modsso_url_encode(r->pool, dest),
NULL); NULL);
if (!apr_is_empty_array(groups)) { if (groups) {
login_url = apr_pstrcat(r->pool, login_url = apr_pstrcat(r->pool,
login_url, login_url,
"&g=", "&g=",
encode_groups(r->pool, groups), groups_charp_to_string(r->pool, groups),
NULL); NULL);
} }
ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
...@@ -634,6 +613,7 @@ static int redirect_to_login_server(request_rec *r, ...@@ -634,6 +613,7 @@ static int redirect_to_login_server(request_rec *r,
return http_redirect(r, login_url); return http_redirect(r, login_url);
} }
#if 0
static char *pkey_to_string(const unsigned char *pkey, char *buf) { static char *pkey_to_string(const unsigned char *pkey, char *buf) {
static const char *hex = "0123456789ABCDEF"; static const char *hex = "0123456789ABCDEF";
char *o = buf; char *o = buf;
...@@ -646,6 +626,7 @@ static char *pkey_to_string(const unsigned char *pkey, char *buf) { ...@@ -646,6 +626,7 @@ static char *pkey_to_string(const unsigned char *pkey, char *buf) {
*o = '\0'; *o = '\0';
return buf; return buf;
} }
#endif
/** /**
* Apache authentication handler for mod_sso. * Apache authentication handler for mod_sso.
...@@ -691,23 +672,13 @@ static int mod_sso_check_access_ex(request_rec *r) ...@@ -691,23 +672,13 @@ static int mod_sso_check_access_ex(request_rec *r)
return DECLINED; return DECLINED;
} }
static char *parse_ticket_groups(apr_pool_t *pool, char **groups) {
apr_array_header_t *arr = apr_array_make(pool, 1, sizeof(char *));
if (groups) {
while (*groups) {
*(char **)apr_array_push(arr) = apr_pstrdup(pool, *groups);
groups++;
}
}
return apr_array_pstrcat(pool, arr, ',');
}
static int mod_sso_check_user_id(request_rec *r) static int mod_sso_check_user_id(request_rec *r)
{ {
const char *type, *sso_cookie_name, *sso_cookie; const char *type, *sso_cookie_name, *sso_cookie;
const char *service = NULL, *service_host = NULL, const char *service = NULL, *service_host = NULL,
*service_path = NULL; *service_path = NULL;
int retval, err, do_redirect = 1; int retval, err, do_redirect = 1;
const char **required_groups;
modsso_config *s_cfg = (modsso_config *) modsso_config *s_cfg = (modsso_config *)
ap_get_module_config(r->per_dir_config, &sso_module); ap_get_module_config(r->per_dir_config, &sso_module);
//apr_array_header_t *sso_validate_groups = NULL; //apr_array_header_t *sso_validate_groups = NULL;
...@@ -729,9 +700,6 @@ static int mod_sso_check_user_id(request_rec *r) ...@@ -729,9 +700,6 @@ static int mod_sso_check_user_id(request_rec *r)
} }
} }
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"sso (check_user_id): handler '%s' uri '%s'", r->handler, r->uri);
sso_cookie_name = get_cookie_name(r); sso_cookie_name = get_cookie_name(r);
// Check if the required parameters are defined. // Check if the required parameters are defined.
...@@ -746,11 +714,15 @@ static int mod_sso_check_user_id(request_rec *r) ...@@ -746,11 +714,15 @@ static int mod_sso_check_user_id(request_rec *r)
return HTTP_BAD_REQUEST; return HTTP_BAD_REQUEST;
} }
// Fetch the list of desired groups set (eventually) by group_check_authorization.
required_groups = (const char **)apr_table_get(r->notes, "SSO_REQUIRED_GROUPS");
// Test for valid cookie // Test for valid cookie
sso_cookie = get_cookie(r, sso_cookie_name); sso_cookie = get_cookie(r, sso_cookie_name);
if (sso_cookie != NULL) { if (sso_cookie != NULL) {
sso_ticket_t t; sso_ticket_t t;
#if 0
// Print some debugging information about the service // Print some debugging information about the service
{ {
char pkeybuf[512]; char pkeybuf[512];
...@@ -763,26 +735,20 @@ static int mod_sso_check_user_id(request_rec *r) ...@@ -763,26 +735,20 @@ static int mod_sso_check_user_id(request_rec *r)
r->uri, service, s_cfg->service, host_hdr, sso_cookie, r->uri, service, s_cfg->service, host_hdr, sso_cookie,
pkey_to_string(s_cfg->public_key, pkeybuf)); pkey_to_string(s_cfg->public_key, pkeybuf));
} }
#endif
err = sso_ticket_open(&t, sso_cookie, s_cfg->public_key); err = sso_ticket_open(&t, sso_cookie, s_cfg->public_key);
if (err != SSO_OK) { if (err != SSO_OK) {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
"sso: ticket decoding error: %s", sso_strerror(err)); "sso: ticket decoding error: %s", sso_strerror(err));
} else { } else {
// TODO: remove this so as to skip group membership check in sso_validate. err = sso_validate(t, s_cfg->service, s_cfg->domain, required_groups);
/* if (s_cfg->groups != NULL) { */
/* sso_validate_groups = apr_array_copy(r->pool, s_cfg->groups); */
/* *(const char **)apr_array_push(sso_validate_groups) = NULL; */
/* } */
err = sso_validate(t, s_cfg->service, s_cfg->domain,
//apr_is_empty_array(s_cfg->groups) ? NULL : (const char **)sso_validate_groups
NULL);
if (err != SSO_OK) { if (err != SSO_OK) {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
"sso: validation error: %s", sso_strerror(err)); "sso: validation error: %s", sso_strerror(err));
} else { } else {
apr_table_setn(r->notes, "SSO_GROUPS", parse_ticket_groups(r->pool, t->groups)); // Don't do this: t->groups wil be freed by sso_ticket_free
// apr_table_setn(r->notes, "SSO_GROUPS", (char *)(t->groups));
apr_table_setn(r->subprocess_env, "SSO_SERVICE", apr_table_setn(r->subprocess_env, "SSO_SERVICE",
apr_pstrdup(r->pool, service)); apr_pstrdup(r->pool, service));
r->user = apr_pstrdup(r->pool, t->user); r->user = apr_pstrdup(r->pool, t->user);
...@@ -801,7 +767,7 @@ static int mod_sso_check_user_id(request_rec *r) ...@@ -801,7 +767,7 @@ static int mod_sso_check_user_id(request_rec *r)
} }
// Redirect to login server // Redirect to login server
return redirect_to_login_server(r, s_cfg->login_server, service_host, service, s_cfg->groups); return redirect_to_login_server(r, s_cfg->login_server, service_host, service, required_groups);
} }
#else #else
static int mod_sso_check_user_id(request_rec *r) static int mod_sso_check_user_id(request_rec *r)
...@@ -912,7 +878,7 @@ static int mod_sso_check_user_id(request_rec *r) ...@@ -912,7 +878,7 @@ static int mod_sso_check_user_id(request_rec *r)
} }
// Redirect to login server // Redirect to login server
return redirect_to_login_server(r, s_cfg->login_server, service_host, service, auth.groups); return redirect_to_login_server(r, s_cfg->login_server, service_host, service, apr_is_empty_array(auth.groups) ? NULL : (const char **)auth.groups->elts);
} }
#endif /* apache 2.2 */ #endif /* apache 2.2 */
...@@ -920,78 +886,69 @@ static int mod_sso_check_user_id(request_rec *r) ...@@ -920,78 +886,69 @@ static int mod_sso_check_user_id(request_rec *r)
* Apache authorization check callback for mod_sso. * Apache authorization check callback for mod_sso.
*/ */
#if MODULE_MAGIC_NUMBER_MAJOR >= 20100714 #if MODULE_MAGIC_NUMBER_MAJOR >= 20100714
static authz_status sso_check_authorization(request_rec *r, const char *require_args, const void *parsed_require_args) static apr_array_header_t *required_groups_array(request_rec *r, const void *parsed_require_args) {
{ const ap_expr_info_t *expr = parsed_require_args;
const char *uri, *type; const char *err = NULL;
const char *service = NULL, *service_host = NULL, const char *require, *w, *t;
*service_path = NULL; apr_array_header_t *grouparr = apr_array_make(r->pool, 1, sizeof(char *));
char *sso_logout_path, *sso_login_path;
modsso_config *s_cfg; require = ap_expr_str_exec(r, expr, &err);
if (err) {
// We already did everything in mod_sso_check_user_id(), return NULL;
// so just succeed (if SSO is active).
type = ap_auth_type(r);
if (type && !strcasecmp(type, "SSO") && r->user) {
return OK;
} }
// XXX check groups via e.g. 'Require sso-groups' or 'Require valid-group' t = require;
s_cfg = (modsso_config *) while ((w = ap_getword_conf(r->pool, &t)) && w[0]) {
ap_get_module_config(r->per_dir_config, &sso_module); *(const char **)apr_array_push(grouparr) = w;
uri = r->uri;
if (parse_service(r, s_cfg, &service, &service_host, &service_path) != 0) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"sso (auth_checker): could not parse service \"%s\"",
s_cfg->service);
return HTTP_BAD_REQUEST;
} }
return grouparr;
}
sso_logout_path = apr_pstrcat(r->pool, service_path, "sso_logout", NULL); static char **required_groups_charpp(request_rec *r, const void *parsed_require_args) {
sso_login_path = apr_pstrcat(r->pool, service_path, "sso_login", NULL); apr_array_header_t *arr = required_groups_array(r, parsed_require_args);
if (!strcmp(uri, sso_logout_path) || !strcmp(uri, sso_login_path)) { if (!arr) {
return OK; return NULL;
} }
return groups_array_to_charpp(r->pool, arr);
return DECLINED;
} }
// This function will be called twice: first, before any authn
// handlers are executed, and we return AUTHZ_DENIED_NO_USER to tell
// Apache that we need a user. This should cause it to invoke
// mod_sso_check_user_id, and then call this function again.
static authz_status group_check_authorization(request_rec *r, const char *require_args, const void *parsed_require_args) { static authz_status group_check_authorization(request_rec *r, const char *require_args, const void *parsed_require_args) {
const ap_expr_info_t *expr = parsed_require_args; // Do we have a user? All ok then! We assume that the request was
const char *err = NULL; // validated by mod_sso_check_user_id using the value of
const char *group_str, *require, *w, *t; // SSO_REQUIRED_GROUPS we set earlier.
apr_array_header_t *user_groups = NULL; if (r->user) {
int i; return AUTHZ_GRANTED;
// Retrieve the comma-separated group list from r->notes.
group_str = apr_table_get(r->notes, "SSO_GROUPS");
if (!group_str) {
return DECLINED;
} }
user_groups = parse_commasep_groups(r->pool, group_str);
require = ap_expr_str_exec(r, expr, &err); // Set the list of groups in the request notes, so that the
if (err) { // sso authn handler can retrieve it and validate the ticket.
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02592) apr_table_setn(r->notes, "SSO_REQUIRED_GROUPS",
"mod_sso authorize: require group: Can't " (char *)required_groups_charpp(r, parsed_require_args));
"evaluate require expression: %s", err); return AUTHZ_DENIED_NO_USER;
return AUTHZ_DENIED; }
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, static const char *group_parse_config(cmd_parms *cmd, const char *require_line,
"sso (check_group): user '%s' user_groups '%s'", r->user, group_str); const void **parsed_require_line)
{
const char *expr_err = NULL;
ap_expr_info_t *expr;
t = require; expr = ap_expr_parse_cmd(cmd, require_line, AP_EXPR_FLAG_STRING_RESULT,
while ((w = ap_getword_conf(r->pool, &t)) && w[0]) { &expr_err, NULL);
// Check if w is in user_groups.
for (i = 0; i < user_groups->nelts; i++) { if (expr_err) {
char *el = APR_ARRAY_IDX(user_groups, i, char *); return apr_pstrcat(cmd->temp_pool,
if (!strcasecmp(w, el)) { "Cannot parse expression in require line: ",
return AUTHZ_GRANTED; expr_err, NULL);
}
}
} }
return DECLINED; *parsed_require_line = expr;
return NULL;
} }
#else #else
...@@ -1031,16 +988,10 @@ static int mod_sso_auth_checker(request_rec *r) ...@@ -1031,16 +988,10 @@ static int mod_sso_auth_checker(request_rec *r)
#endif #endif
#if MODULE_MAGIC_NUMBER_MAJOR >= 20100714 #if MODULE_MAGIC_NUMBER_MAJOR >= 20100714
static const authz_provider authz_sso_provider =
{
&sso_check_authorization,
NULL,
};
static const authz_provider authz_sso_group_provider = static const authz_provider authz_sso_group_provider =
{ {
&group_check_authorization, &group_check_authorization,
NULL, &group_parse_config,
}; };
#endif #endif
...@@ -1056,9 +1007,8 @@ static void mod_sso_register_hooks (apr_pool_t *p) ...@@ -1056,9 +1007,8 @@ static void mod_sso_register_hooks (apr_pool_t *p)
{ {
ap_hook_handler(mod_sso_method_handler, NULL, NULL, APR_HOOK_FIRST); ap_hook_handler(mod_sso_method_handler, NULL, NULL, APR_HOOK_FIRST);
#if MODULE_MAGIC_NUMBER_MAJOR >= 20100714 #if MODULE_MAGIC_NUMBER_MAJOR >= 20100714
ap_hook_check_authn(mod_sso_check_user_id, NULL, NULL, APR_HOOK_MIDDLE, AP_AUTH_INTERNAL_PER_CONF); ap_hook_check_authn(mod_sso_check_user_id, NULL, NULL, APR_HOOK_FIRST, AP_AUTH_INTERNAL_PER_CONF);
ap_hook_check_access_ex(mod_sso_check_access_ex, NULL, NULL, APR_HOOK_MIDDLE, AP_AUTH_INTERNAL_PER_CONF); ap_hook_check_access_ex(mod_sso_check_access_ex, NULL, NULL, APR_HOOK_MIDDLE, AP_AUTH_INTERNAL_PER_CONF);
ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, SSO_REQUIRE_NAME, "0", &authz_sso_provider, AP_AUTH_INTERNAL_PER_CONF);
ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "group", "0", &authz_sso_group_provider, AP_AUTH_INTERNAL_PER_CONF); ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "group", "0", &authz_sso_group_provider, AP_AUTH_INTERNAL_PER_CONF);
#else #else
static const char * const authzSucc[] = { "mod_sso.c", NULL }; static const char * const authzSucc[] = { "mod_sso.c", NULL };
......
...@@ -141,9 +141,10 @@ class HttpdIntegrationTest(unittest.TestCase): ...@@ -141,9 +141,10 @@ class HttpdIntegrationTest(unittest.TestCase):
def mkcookie(tkt): def mkcookie(tkt):
return "SSO_test=%s" % tkt return "SSO_test=%s" % tkt
# For Apache 2.2, set this to the empty string (we do not use the # Set to a non-empty string when testing the SSOGroups directive
# SSOGroup directive, so only the requested groups are generated). # (normally only the requested groups are generated).
extra_groups = "&g=group1,group2,group3" #extra_groups = "&g=group1,group2,group3"
extra_groups = ''
# Tests have a name so that we can recognize failures. # Tests have a name so that we can recognize failures.
checks = [ checks = [
...@@ -185,6 +186,10 @@ class HttpdIntegrationTest(unittest.TestCase): ...@@ -185,6 +186,10 @@ class HttpdIntegrationTest(unittest.TestCase):
"cookie": mkcookie(self._ticket()), "cookie": mkcookie(self._ticket()),
"status": 200, "status": 200,
"body": "ok"}), "body": "ok"}),
#("protected-group with cookie wrong group -> unauthorized",
# {"url": "/protected-group/index.html",
# "cookie": mkcookie(self._ticket(group="group2")),
# "status": 401}),
("protected-group with cookie wrong group -> redirect", ("protected-group with cookie wrong group -> redirect",
{"url": "/protected-group/index.html", {"url": "/protected-group/index.html",
"cookie": mkcookie(self._ticket(group="group2")), "cookie": mkcookie(self._ticket(group="group2")),
...@@ -213,7 +218,7 @@ class HttpdIntegrationTest(unittest.TestCase): ...@@ -213,7 +218,7 @@ class HttpdIntegrationTest(unittest.TestCase):
] ]
for name, check in checks: for name, check in checks:
for i in xrange(10): for i in xrange(10):
print 'CHECKING', check print 'CHECKING %s (%d of 10)' % (name, i), check
status, body, location = _query(check["url"], status, body, location = _query(check["url"],
host=check.get("http_host"), host=check.get("http_host"),
cookie=check.get("cookie")) cookie=check.get("cookie"))
......
...@@ -17,7 +17,7 @@ LogLevel debug ...@@ -17,7 +17,7 @@ LogLevel debug
SSOLoginServer login.example.com SSOLoginServer login.example.com
SSODomain example.com SSODomain example.com
SSOPublicKeyFile ${TESTROOT}/public.key SSOPublicKeyFile ${TESTROOT}/public.key
SSOGroups group1,group2,group3 #SSOGroups group1,group2,group3
DocumentRoot ${TESTROOT}/htdocs DocumentRoot ${TESTROOT}/htdocs
<Directory "${TESTROOT}/htdocs"> <Directory "${TESTROOT}/htdocs">
...@@ -25,6 +25,8 @@ DocumentRoot ${TESTROOT}/htdocs ...@@ -25,6 +25,8 @@ DocumentRoot ${TESTROOT}/htdocs
AuthName test AuthName test
SSOService service.example.com/ SSOService service.example.com/
require valid-user require valid-user
AllowOverride All
</Directory> </Directory>
<Location "/other-service"> <Location "/other-service">
......