Beispiel #1
0
def get_valid_vgrid_owner_id(configuration, vgrid_name):
    """Returns the first valid owner_id for *vgrid_name*
    """

    result = None

    # Look for owner in current (sub)vgrid

    (owners_status, owners_id) = vgrid_owners(vgrid_name,
                                              configuration,
                                              recursive=False)

    if owners_status:

        # Only allow valid users

        for owner_id in owners_id:
            if is_user(owner_id, configuration.mig_server_home):
                result = owner_id

    if result is None:

        # If no valid vgrid owners found look recursively towards top-vgrid

        (owners_status, owners_id) = vgrid_owners(vgrid_name,
                                                  configuration,
                                                  recursive=True)

        if owners_status:
            for owner_id in owners_id:
                if is_user(owner_id, configuration.mig_server_home):
                    result = owner_id

    return result
Beispiel #2
0
def get_allowed_path(configuration, client_id, path):
    """Check certificate data and path for either a valid user/server
    or a resource using a valid session id. If the check succeeds, the
    real path to the file is returned.
    """

    client_dir = client_id_dir(client_id)

    # Check cert and decide if it is a user, resource or server

    if not client_id:
        path_slash_stripped = path.lstrip("/")
        sessionid = path_slash_stripped[: path_slash_stripped.find("/")]

        # check that the sessionid is ok (does symlink exist?)

        if not os.path.islink(configuration.webserver_home + sessionid):
            raise Exception("Invalid session id!")

        target_dir = configuration.webserver_home + path_slash_stripped[: path_slash_stripped.rfind("/")]
        target_file = path_slash_stripped[path_slash_stripped.rfind("/") + 1 :]
    elif is_user(client_id, configuration.mig_server_home):
        real_path = os.path.normpath(os.path.join(configuration.user_home, client_dir, path))
        target_dir = os.path.dirname(real_path)
        target_file = os.path.basename(real_path)
    elif is_server(client_id, configuration.server_home):
        real_path = os.path.normpath(os.path.join(configuration.server_home, client_dir, path))
        target_dir = os.path.dirname(real_path)
        target_file = os.path.basename(real_path)
    else:
        raise Exception("Invalid credentials %s: no such user or server" % client_id)

    target_path = target_dir + "/" + target_file
    return target_path
Beispiel #3
0
def get_allowed_path(configuration, client_id, path):
    """Check certificate data and path for either a valid user/server
    or a resource using a valid session id. If the check succeeds, the
    real path to the file is returned.
    """

    client_dir = client_id_dir(client_id)

    # Check cert and decide if it is a user, resource or server

    if not client_id:
        path_slash_stripped = path.lstrip('/')
        sessionid = path_slash_stripped[:path_slash_stripped.find('/')]

        # check that the sessionid is ok (does symlink exist?)

        if not os.path.islink(configuration.webserver_home + sessionid):
            raise Exception('Invalid session id!')

        target_dir = configuration.webserver_home\
            + path_slash_stripped[:path_slash_stripped.rfind('/')]
        target_file = path_slash_stripped[path_slash_stripped.rfind('/') + 1:]
    elif is_user(client_id, configuration.mig_server_home):
        real_path = \
            os.path.normpath(os.path.join(configuration.user_home,
                                          client_dir, path))
        target_dir = os.path.dirname(real_path)
        target_file = os.path.basename(real_path)
    elif is_server(client_id, configuration.server_home):
        real_path = \
            os.path.normpath(os.path.join(configuration.server_home,
                                          client_dir, path))
        target_dir = os.path.dirname(real_path)
        target_file = os.path.basename(real_path)
    else:
        raise Exception('Invalid credentials %s: no such user or server' %
                        client_id)

    target_path = target_dir + '/' + target_file
    return target_path
Beispiel #4
0
def validate_input_and_cert(
    user_arguments_dict,
    defaults,
    output_objects,
    client_id,
    configuration,
    allow_rejects,
    require_user=True,
    filter_values=None,
    environ=None,
    ):
    """A wrapper used by most back end functionality - redirects to sign up
    if client_id is missing.
    """

    logger = configuration.logger
    if environ is None:
        environ = os.environ
    creds_error = ''
    if not client_id:
        creds_error = "Invalid or missing user credentials"
    elif require_user and not is_user(client_id, configuration.mig_server_home):
        creds_error = "No such user (%s)" % client_id

    if creds_error and not requested_page().endswith('logout.py'):
        output_objects.append({'object_type': 'error_text', 'text'
                              : creds_error
                              })

        # Redirect to sign-up cert page trying to guess relevant choices

        signup_url = os.path.join(configuration.migserver_https_sid_url,
                                  'cgi-sid', 'signup.py')
        signup_query = ''

        if not client_id:
            output_objects.append(
                {'object_type': 'text', 'text': '''Apparently you do not
already have access to %s, but you can sign up:''' % configuration.short_title
                 })
            output_objects.append({'object_type': 'link', 'text': signup_url,
                                   'destination': signup_url + signup_query})
            output_objects.append(
                {'object_type': 'text', 'text': '''If you already signed up and
received a user certificate you probably just need to import it in your
browser.'''})
        else:
            output_objects.append(
                {'object_type': 'text', 'text': '''Apparently you already have
suitable credentials and just need to sign up for a local %s account on:''' % \
                 configuration.short_title})

            if extract_client_cert(configuration, environ) is None:
                # Force logout/expire session cookie here to support signup
                identity = extract_client_openid(configuration, environ,
                                                 lookup_dn=False)
                if identity:
                    logger.info("expire openid user %s" % identity)
                    (success, _) = expire_oid_sessions(configuration, identity)
                else:
                    logger.info("no openid user logged in")
             
            output_objects.append({'object_type': 'link', 'text': signup_url,
                                   'destination': signup_url + signup_query})
        return (False, output_objects)

    (status, retval) = validate_input(user_arguments_dict, defaults,
            output_objects, allow_rejects, filter_values)

    return (status, retval)
Beispiel #5
0
def validate_input_and_cert(
    user_arguments_dict,
    defaults,
    output_objects,
    client_id,
    configuration,
    allow_rejects,
    require_user=True,
    filter_values=None,
    environ=None,
):
    """A wrapper used by most back end functionality - redirects to sign up
    if client_id is missing.
    """

    logger = configuration.logger
    if environ is None:
        environ = os.environ
    creds_error = ''
    if not client_id:
        creds_error = "Invalid or missing user credentials"
    elif require_user and not is_user(client_id,
                                      configuration.mig_server_home):
        creds_error = "No such user (%s)" % client_id

    if creds_error and not requested_page().endswith('logout.py'):
        output_objects.append({
            'object_type': 'error_text',
            'text': creds_error
        })

        if configuration.site_enable_gdp:
            main_url = configuration.migserver_http_url
            output_objects.append({
                'object_type':
                'text',
                'text':
                '''Apparently you do not
                        have access to this page, please return to:'''
            })

            output_objects.append({
                'object_type': 'link',
                'text': main_url,
                'destination': main_url
            })
        else:
            # Redirect to sign-up cert page trying to guess relevant choices

            signup_url = os.path.join(configuration.migserver_https_sid_url,
                                      'cgi-sid', 'signup.py')
            signup_query = ''

            if not client_id:
                output_objects.append({
                    'object_type':
                    'text',
                    'text':
                    '''Apparently you do not
    already have access to %s, but you can sign up:''' %
                    configuration.short_title
                })
                output_objects.append({'object_type': 'link', 'text':
                                       '%s sign up page' % \
                                       configuration.short_title,
                                       'destination': signup_url + signup_query})
                output_objects.append({
                    'object_type':
                    'text',
                    'text':
                    '''If you already signed up and
    received a user certificate you probably just need to import it in your
    browser.'''
                })
            else:
                output_objects.append({
                    'object_type':
                    'text',
                    'text':
                    '''Apparently you already have
    suitable credentials and just need to sign up for a local %s account on the'''
                    % configuration.short_title
                })

                base_url = extract_base_url(configuration, environ)
                if base_url == configuration.migserver_https_ext_cert_url and \
                       'extcert' in configuration.site_login_methods:
                    signup_query = '?show=extcert'
                elif base_url in (configuration.migserver_https_ext_oid_url,
                                  configuration.migserver_https_mig_oid_url):
                    # Force logout/expire session cookie here to support signup
                    (oid_db, identity) = extract_client_openid(configuration,
                                                               environ,
                                                               lookup_dn=False)
                    if oid_db and identity:
                        logger.info("openid expire user %s in %s" %
                                    (identity, oid_db))
                        (success,
                         _) = expire_oid_sessions(configuration, oid_db,
                                                  identity)
                        if oid_db == auth_openid_ext_db and \
                               'extoid' in configuration.site_signup_methods:
                            signup_query = '?show=extoid'
                        else:
                            logger.error("unknown migoid client_id %s on %s" \
                                         % (client_id, base_url))
                else:
                    logger.warning("unexpected client_id %s on %s" % \
                                   (client_id, base_url))

                output_objects.append({'object_type': 'link', 'text':
                                       '%s sign up page' % \
                                       configuration.short_title,
                                       'destination':
                                       signup_url + signup_query})
        return (False, output_objects)

    (status, retval) = validate_input(user_arguments_dict, defaults,
                                      output_objects, allow_rejects,
                                      filter_values)

    return (status, retval)
Beispiel #6
0
def init_vgrid_script_add_rem(
    vgrid_name,
    client_id,
    subject,
    subject_type,
    configuration,
    ):
    """Initialize vgrid specific add and remove scripts"""

    msg = ''
    if not vgrid_name:
        msg += 'Please specify vgrid_name in the querystring'
        return (False, msg, None)

    if not subject:
        msg += 'Please provide the name of the %s' % subject_type
        return (False, msg, None)

    if not valid_dir_input(configuration.vgrid_home, vgrid_name):
        msg += 'Illegal vgrid_name: %s' % vgrid_name
        return (False, msg, None)

    if subject_type == 'member' or subject_type == 'owner':
        if not is_user(subject, configuration.mig_server_home):
            msg += '%s is not a valid %s user!' % \
                    (subject, configuration.short_title)
            return (False, msg, None)
    elif subject_type == 'resource':
        if not is_resource(subject, configuration.resource_home):
            msg += '%s is not a valid %s resource' % \
                    (subject, configuration.short_title)
            msg += \
                ' (OK, if removing or e.g. the resource creation is pending)'
    elif subject_type == 'trigger':
        # Rules are checked later
        pass
    else:
        msg += 'unknown subject type in init_vgrid_script_add_rem'
        return (False, msg, [])

    # special case: members may terminate own membership

    if (subject_type == 'member') and (client_id == subject) \
        and (vgrid_is_member(vgrid_name, subject, configuration)):

        return (True, msg, [])

    # special case: members may remove own triggers and add new ones

    if (subject_type == 'trigger') and \
           (not vgrid_is_trigger(vgrid_name, subject, configuration) or \
            vgrid_is_trigger_owner(vgrid_name, subject, client_id,
                                   configuration)):
        return (True, msg, [])

    # otherwise: only owners may add or remove:

    if not vgrid_is_owner(vgrid_name, client_id, configuration):
        msg += 'You must be an owner of the %s vgrid to add/remove %s'\
             % (vgrid_name, subject_type)
        return (False, msg, None)

    return (True, msg, [])
Beispiel #7
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id, op_header=False)
    defaults = signature()[1]
    output_objects.append({'object_type': 'header', 'text'
                          : 'Remove Resource Owner'})
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
        )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    unique_resource_name = accepted['unique_resource_name'][-1]
    cert_id = accepted['cert_id'][-1]

    if not safe_handler(configuration, 'post', op_name, client_id,
                        get_csrf_limit(configuration), accepted):
        output_objects.append(
            {'object_type': 'error_text', 'text': '''Only accepting
CSRF-filtered POST requests to prevent unintended updates'''
             })
        return (output_objects, returnvalues.CLIENT_ERROR)

    if not is_owner(client_id, unique_resource_name,
                    configuration.resource_home, logger):
        output_objects.append({'object_type': 'error_text', 'text'
                              : 'You must be an owner of %s to remove another owner!'
                               % unique_resource_name})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # is_owner incorporates unique_resource_name verification - no need to
    # specifically check for illegal directory traversal

    if not is_user(cert_id, configuration.mig_server_home):
        output_objects.append({'object_type': 'error_text', 'text'
                              : '%s is not a valid %s user!' % \
                                (cert_id, configuration.short_title) })
        return (output_objects, returnvalues.CLIENT_ERROR)

    # reject remove if cert_id is not an owner

    if not resource_is_owner(unique_resource_name, cert_id, configuration):
        output_objects.append({'object_type': 'error_text', 'text'
                               : '%s is not an owner of %s.'
                               % (cert_id, unique_resource_name)})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Remove owner

    (rm_status, rm_msg) = resource_remove_owners(configuration,
                                                 unique_resource_name,
                                                 [cert_id])
    if not rm_status:
        output_objects.append({'object_type': 'error_text', 'text'
                              : 'Could not remove owner, reason: %s'
                               % rm_msg})
        return (output_objects, returnvalues.SYSTEM_ERROR)

    output_objects.append({'object_type': 'text', 'text'
                          : '%s was successfully removed and is no longer an owner of %s!'
                           % (cert_id, unique_resource_name)})
    output_objects.append({'object_type': 'link', 'destination':
                        'resadmin.py?unique_resource_name=%s' % \
                           unique_resource_name, 'class': 'adminlink iconspace',
                           'title': 'Administrate resource',
                           'text': 'Manage resource'})
    return (output_objects, returnvalues.OK)
Beispiel #8
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id, op_header=False)
    defaults = signature()[1]
    status = returnvalues.OK
    output_objects.append({
        'object_type': 'header',
        'text': 'Add Resource Owner(s)'
    })
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
    )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    unique_resource_name = accepted['unique_resource_name'][-1].strip()
    cert_id_list = accepted['cert_id']
    request_name = unhexlify(accepted['request_name'][-1])

    if not safe_handler(configuration, 'post', op_name, client_id,
                        get_csrf_limit(configuration), accepted):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            '''Only accepting
CSRF-filtered POST requests to prevent unintended updates'''
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    if not is_owner(client_id, unique_resource_name,
                    configuration.resource_home, logger):
        output_objects.append({
            'object_type':
            'error_text',
            'text':
            'You must be an owner of %s to add a new owner!' %
            unique_resource_name
        })
        return (output_objects, returnvalues.CLIENT_ERROR)

    # is_owner incorporates unique_resource_name verification - no need to
    # specifically check for illegal directory traversal

    cert_id_added = []
    for cert_id in cert_id_list:
        cert_id = cert_id.strip()
        if not cert_id:
            continue
        if not is_user(cert_id, configuration.mig_server_home):
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                '%s is not a valid %s user!' %
                (cert_id, configuration.short_title)
            })
            status = returnvalues.CLIENT_ERROR
            continue

        # don't add if already an owner

        if resource_is_owner(unique_resource_name, cert_id, configuration):
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                '%s is already an owner of %s.' %
                (cert_id, unique_resource_name)
            })
            status = returnvalues.CLIENT_ERROR
            continue

        # Add owner

        (add_status, add_msg) = resource_add_owners(configuration,
                                                    unique_resource_name,
                                                    [cert_id])
        if not add_status:
            output_objects.append({
                'object_type':
                'error_text',
                'text':
                'Could not add new owner, reason: %s' % add_msg
            })
            status = returnvalues.SYSTEM_ERROR
            continue
        cert_id_added.append(cert_id)

    if request_name:
        request_dir = os.path.join(configuration.resource_home,
                                   unique_resource_name)
        if not delete_access_request(configuration, request_dir, request_name):
            logger.error("failed to delete owner request for %s in %s" % \
                         (unique_resource_name, request_name))
            output_objects.append({
                'object_type': 'error_text', 'text':
                'Failed to remove saved request for %s in %s!' % \
                (unique_resource_name, request_name)})

    if cert_id_added:
        output_objects.append({
            'object_type':
            'html_form',
            'text':
            'New owner(s)<br/>%s<br/>successfully added to %s!' %
            ('<br />'.join(cert_id_added), unique_resource_name)
        })
        cert_id_fields = ''
        for cert_id in cert_id_added:
            cert_id_fields += """<input type=hidden name=cert_id value='%s' />
""" % cert_id

        form_method = 'post'
        csrf_limit = get_csrf_limit(configuration)
        fill_helpers = {
            'res_id': unique_resource_name,
            'cert_id_fields': cert_id_fields,
            'any_protocol': any_protocol,
            'form_method': form_method,
            'csrf_field': csrf_field,
            'csrf_limit': csrf_limit
        }
        target_op = 'sendrequestaction'
        csrf_token = make_csrf_token(configuration, form_method, target_op,
                                     client_id, csrf_limit)
        fill_helpers.update({'target_op': target_op, 'csrf_token': csrf_token})
        output_objects.append({
            'object_type':
            'html_form',
            'text':
            """
<form method='%(form_method)s' action='%(target_op)s.py'>
<input type='hidden' name='%(csrf_field)s' value='%(csrf_token)s' />
<input type=hidden name=request_type value='resourceaccept' />
<input type=hidden name=unique_resource_name value='%(res_id)s' />
%(cert_id_fields)s
<input type=hidden name=protocol value='%(any_protocol)s' />
<table>
<tr>
<td class='title'>Custom message to user(s)</td>
</tr>
<tr>
<td><textarea name=request_text cols=72 rows=10>
We have granted you ownership access to our %(res_id)s resource.
You can access the resource administration page from the Resources page.

Regards, the %(res_id)s resource owners
</textarea></td>
</tr>
<tr>
<td><input type='submit' value='Inform user(s)' /></td>
</tr>
</table>
</form>
<br />
""" % fill_helpers
        })

    output_objects.append({'object_type': 'link', 'destination':
                           'resadmin.py?unique_resource_name=%s' % \
                           unique_resource_name, 'class':
                           'adminlink iconspace', 'title':
                           'Administrate resource', 'text': 'Manage resource'})
    return (output_objects, status)
Beispiel #9
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id, op_header=False)
    defaults = signature()[1]
    output_objects.append({'object_type': 'header', 'text'
                          : 'Remove Resource Owner'})
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
        )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    if not correct_handler('POST'):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : 'Only accepting POST requests to prevent unintended updates'})
        return (output_objects, returnvalues.CLIENT_ERROR)

    unique_resource_name = accepted['unique_resource_name'][-1]
    cert_id = accepted['cert_id'][-1]

    if not is_owner(client_id, unique_resource_name,
                    configuration.resource_home, logger):
        output_objects.append({'object_type': 'error_text', 'text'
                              : 'You must be an owner of %s to remove another owner!'
                               % unique_resource_name})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # is_owner incorporates unique_resource_name verification - no need to
    # specifically check for illegal directory traversal

    if not is_user(cert_id, configuration.mig_server_home):
        output_objects.append({'object_type': 'error_text', 'text'
                              : '%s is not a valid %s user!' % \
                                (cert_id, configuration.short_title) })
        return (output_objects, returnvalues.CLIENT_ERROR)

    # reject remove if cert_id is not an owner

    if not resource_is_owner(unique_resource_name, cert_id, configuration):
        output_objects.append({'object_type': 'error_text', 'text'
                               : '%s is not an owner of %s.'
                               % (cert_id, unique_resource_name)})
        return (output_objects, returnvalues.CLIENT_ERROR)

    base_dir = os.path.abspath(os.path.join(configuration.resource_home,
                                            unique_resource_name)) + os.sep

    # Remove owner

    owners_file = os.path.join(base_dir, 'owners')
    (rm_status, rm_msg) = resource_remove_owners(configuration,
                                                 unique_resource_name,
                                                 [cert_id])
    if not rm_status:
        output_objects.append({'object_type': 'error_text', 'text'
                              : 'Could not remove owner, reason: %s'
                               % rm_msg})
        return (output_objects, returnvalues.SYSTEM_ERROR)

    output_objects.append({'object_type': 'text', 'text'
                          : '%s was successfully removed and is no longer an owner of %s!'
                           % (cert_id, unique_resource_name)})
    output_objects.append({'object_type': 'link', 'destination':
                        'resadmin.py?unique_resource_name=%s' % \
                           unique_resource_name, 'class': 'adminlink', 'title':
                           'Administrate resource', 'text': 'Manage resource'})
    return (output_objects, returnvalues.OK)
Beispiel #10
0
def main(client_id, user_arguments_dict):
    """Main function used by front end"""

    (configuration, logger, output_objects, op_name) = \
        initialize_main_variables(client_id, op_header=False)
    defaults = signature()[1]
    output_objects.append({'object_type': 'header', 'text'
                          : 'Add Resource Owner'})
    (validate_status, accepted) = validate_input_and_cert(
        user_arguments_dict,
        defaults,
        output_objects,
        client_id,
        configuration,
        allow_rejects=False,
        )
    if not validate_status:
        return (accepted, returnvalues.CLIENT_ERROR)

    if not correct_handler('POST'):
        output_objects.append(
            {'object_type': 'error_text', 'text'
             : 'Only accepting POST requests to prevent unintended updates'})
        return (output_objects, returnvalues.CLIENT_ERROR)

    unique_resource_name = accepted['unique_resource_name'][-1].strip()
    cert_id = accepted['cert_id'][-1].strip()

    if not is_owner(client_id, unique_resource_name,
                    configuration.resource_home, logger):
        output_objects.append({'object_type': 'error_text', 'text'
                              : 'You must be an owner of %s to add a new owner!'
                               % unique_resource_name})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # is_owner incorporates unique_resource_name verification - no need to
    # specifically check for illegal directory traversal

    if not is_user(cert_id, configuration.mig_server_home):
        output_objects.append({'object_type': 'error_text', 'text'
                              : '%s is not a valid %s user!'
                               % (cert_id, configuration.short_title)})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # don't add if already an owner

    if resource_is_owner(unique_resource_name, cert_id, configuration):
        output_objects.append({'object_type': 'error_text', 'text'
                               : '%s is already an owner of %s.'
                               % (cert_id, unique_resource_name)})
        return (output_objects, returnvalues.CLIENT_ERROR)

    # Please note that base_dir must end in slash to avoid access to other
    # resource dirs when own name is a prefix of another user name

    base_dir = \
        os.path.abspath(os.path.join(configuration.resource_home,
                        unique_resource_name)) + os.sep

    # Add owner

    (add_status, add_msg) = resource_add_owners(configuration,
                                                unique_resource_name,
                                                [cert_id])
    if not add_status:
        output_objects.append({'object_type': 'error_text', 'text'
                              : 'Could not add new owner, reason: %s'
                               % add_msg})
        return (output_objects, returnvalues.SYSTEM_ERROR)

    output_objects.append({'object_type': 'text', 'text'
                          : 'New owner %s successfully added to %s!'
                           % (cert_id, unique_resource_name)})
    output_objects.append({'object_type': 'html_form', 'text'
                          : """
<form method='post' action='sendrequestaction.py'>
<input type=hidden name=request_type value='resourceaccept' />
<input type=hidden name=unique_resource_name value='%s' />
<input type=hidden name=cert_id value='%s' />
<input type=hidden name=protocol value='%s' />
<table>
<tr>
<td class='title'>Custom message to user</td>
</tr>
<tr>
<td><textarea name=request_text cols=72 rows=10>
We have granted you ownership access to our %s resource.
You can access the resource administration page from the Resources page.

Regards, the %s resource owners
</textarea></td>
</tr>
<tr>
<td><input type='submit' value='Inform user' /></td>
</tr>
</table>
</form>
<br />
""" % (unique_resource_name, cert_id, any_protocol,
              unique_resource_name, unique_resource_name)})
    output_objects.append({'object_type': 'link', 'destination':
                           'resadmin.py?unique_resource_name=%s' % \
                           unique_resource_name, 'class': 'adminlink', 'title':
                           'Administrate resource', 'text': 'Manage resource'})
    return (output_objects, returnvalues.OK)