Commit 7fd440f1 authored by ale's avatar ale

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">
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment