Merge pull request #4 from endlessm/T23999-oars-storage

T23999 Store OARS filter
This commit is contained in:
Georges Basile Stavracas Neto 2018-10-10 21:42:59 +00:00 committed by GitHub
commit ebb892092f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 171 additions and 2 deletions

View File

@ -31,5 +31,33 @@
<annotation name="org.freedesktop.Accounts.DefaultValue" <annotation name="org.freedesktop.Accounts.DefaultValue"
value="(false, [])"/> value="(false, [])"/>
</property> </property>
<!--
oars-filter:
A filter for which applications the user can see in app lists and install.
This is intended to be set by administrators and read by users, rather
than being editable by the user themselves.
Its a two-tuple of the rating type, and a dictionary of rating sections
and values. The type gives the rating scheme in use — currently only
`oars-1.0` is supported.
Each dictionary entry is a mapping from an OARS section to the
most severe value for it which the user is allowed to see (inclusive). Any
app with a more severe value for any section must not be listed or
installable by the user.
An empty dictionary means that no OARS filtering is to be performed for
the user.
This setting is essentially equivalent to the `content_rating` dictionary
in AppStream data:
https://www.freedesktop.org/software/appstream/docs/chap-Metadata.html#tag-content_rating.
-->
<property name="oars-filter" type="(sa{ss})" access="readwrite">
<annotation name="org.freedesktop.Accounts.DefaultValue"
value="('oars-1.0', @a{ss} {})"/>
</property>
</interface> </interface>
</node> </node>

View File

@ -95,6 +95,23 @@ def __lookup_user_id_or_error(user):
raise SystemExit(EXIT_INVALID_OPTION) raise SystemExit(EXIT_INVALID_OPTION)
def __oars_value_to_string(value):
"""Convert an EosParentalControls.AppFilterOarsValue to a human-readable
string."""
mapping = {
EosParentalControls.AppFilterOarsValue.UNKNOWN: "unknown",
EosParentalControls.AppFilterOarsValue.NONE: "none",
EosParentalControls.AppFilterOarsValue.MILD: "mild",
EosParentalControls.AppFilterOarsValue.MODERATE: "moderate",
EosParentalControls.AppFilterOarsValue.INTENSE: "intense",
}
try:
return mapping[value]
except KeyError:
return "invalid (OARS value {})".format(value)
def command_get(user, quiet=False, interactive=True): def command_get(user, quiet=False, interactive=True):
"""Get the app filter for the given user.""" """Get the app filter for the given user."""
user_id = __lookup_user_id_or_error(user) user_id = __lookup_user_id_or_error(user)
@ -121,6 +138,17 @@ def command_check(user, path, quiet=False, interactive=True):
raise SystemExit(EXIT_PATH_NOT_ALLOWED) raise SystemExit(EXIT_PATH_NOT_ALLOWED)
def command_oars_section(user, section, quiet=False, interactive=True):
"""Get the value of the given OARS section for the given user, according
to their OARS filter."""
user_id = __lookup_user_id_or_error(user)
app_filter = __get_app_filter_or_error(user_id, interactive)
value = app_filter.get_oars_value(section)
print('OARS section {} for user {} has value {}'.format(
section, user_id, __oars_value_to_string(value)))
def main(): def main():
# Parse command line arguments # Parse command line arguments
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
@ -164,6 +192,18 @@ def main():
parser_check.add_argument('path', parser_check.add_argument('path',
help='path to a program to check') help='path to a program to check')
# oars-section command
parser_oars_section = subparsers.add_parser('oars-section',
parents=[common_parser],
help='get the value of a '
'given OARS section')
parser_oars_section.set_defaults(function=command_oars_section)
parser_oars_section.add_argument('user', default='', nargs='?',
help='user ID or username to get the '
'OARS filter for (default: current '
'user)')
parser_oars_section.add_argument('section', help='OARS section to get')
# Parse the command line arguments and run the subcommand. # Parse the command line arguments and run the subcommand.
args = parser.parse_args() args = parser.parse_args()
args_dict = dict((k, v) for k, v in vars(args).items() if k != 'function') args_dict = dict((k, v) for k, v in vars(args).items() if k != 'function')

View File

@ -56,6 +56,8 @@ struct _EpcAppFilter
gchar **app_list; /* (owned) (array zero-terminated=1) */ gchar **app_list; /* (owned) (array zero-terminated=1) */
EpcAppFilterListType app_list_type; EpcAppFilterListType app_list_type;
GVariant *oars_ratings; /* (type a{ss}) (owned) */
}; };
G_DEFINE_BOXED_TYPE (EpcAppFilter, epc_app_filter, G_DEFINE_BOXED_TYPE (EpcAppFilter, epc_app_filter,
@ -101,6 +103,7 @@ epc_app_filter_unref (EpcAppFilter *filter)
if (filter->ref_count <= 0) if (filter->ref_count <= 0)
{ {
g_strfreev (filter->app_list); g_strfreev (filter->app_list);
g_variant_unref (filter->oars_ratings);
g_free (filter); g_free (filter);
} }
} }
@ -159,7 +162,50 @@ epc_app_filter_is_path_allowed (EpcAppFilter *filter,
} }
} }
/* Check if @error is a D-Bus remote error mataching @expected_error_name. */ /**
* epc_app_filter_get_oars_value:
* @filter: an #EpcAppFilter
* @oars_section: name of the OARS section to get the value from
*
* Get the value assigned to the given @oars_section in the OARS filter stored
* within @filter. If that section has no value explicitly defined,
* %EPC_APP_FILTER_OARS_VALUE_UNKNOWN is returned.
*
* This value is the most intense value allowed for apps to have in this
* section, inclusive. Any app with a more intense value for this section must
* be hidden from the user whose @filter this is.
*
* Returns: an #EpcAppFilterOarsValue
* Since: 0.1.0
*/
EpcAppFilterOarsValue
epc_app_filter_get_oars_value (EpcAppFilter *filter,
const gchar *oars_section)
{
const gchar *value_str;
g_return_val_if_fail (filter != NULL, EPC_APP_FILTER_OARS_VALUE_UNKNOWN);
g_return_val_if_fail (filter->ref_count >= 1,
EPC_APP_FILTER_OARS_VALUE_UNKNOWN);
g_return_val_if_fail (oars_section != NULL && *oars_section != '\0',
EPC_APP_FILTER_OARS_VALUE_UNKNOWN);
if (!g_variant_lookup (filter->oars_ratings, oars_section, "&s", &value_str))
return EPC_APP_FILTER_OARS_VALUE_UNKNOWN;
if (g_str_equal (value_str, "none"))
return EPC_APP_FILTER_OARS_VALUE_NONE;
else if (g_str_equal (value_str, "mild"))
return EPC_APP_FILTER_OARS_VALUE_MILD;
else if (g_str_equal (value_str, "moderate"))
return EPC_APP_FILTER_OARS_VALUE_MODERATE;
else if (g_str_equal (value_str, "intense"))
return EPC_APP_FILTER_OARS_VALUE_INTENSE;
else
return EPC_APP_FILTER_OARS_VALUE_UNKNOWN;
}
/* Check if @error is a D-Bus remote error matching @expected_error_name. */
static gboolean static gboolean
bus_remote_error_matches (const GError *error, bus_remote_error_matches (const GError *error,
const gchar *expected_error_name) const gchar *expected_error_name)
@ -325,6 +371,9 @@ get_app_filter_cb (GObject *obj,
g_autoptr(EpcAppFilter) app_filter = NULL; g_autoptr(EpcAppFilter) app_filter = NULL;
gboolean is_whitelist; gboolean is_whitelist;
g_auto(GStrv) app_list = NULL; g_auto(GStrv) app_list = NULL;
const gchar *content_rating_kind;
g_autoptr(GVariant) oars_variant = NULL;
g_autoptr(GHashTable) oars_map = NULL;
GetAppFilterData *data = g_task_get_task_data (task); GetAppFilterData *data = g_task_get_task_data (task);
result_variant = g_dbus_connection_call_finish (connection, result, &local_error); result_variant = g_dbus_connection_call_finish (connection, result, &local_error);
@ -350,6 +399,25 @@ get_app_filter_cb (GObject *obj,
return; return;
} }
if (!g_variant_lookup (properties, "oars-filter", "(&s@a{ss})",
&content_rating_kind, &oars_variant))
{
/* Default value. */
content_rating_kind = "oars-1.0";
oars_variant = g_variant_new ("@a{ss} {}");
}
/* Check that the OARS filter is in a format we support. Currently, thats
* only oars-1.0. */
if (!g_str_equal (content_rating_kind, "oars-1.0"))
{
g_task_return_new_error (task, EPC_APP_FILTER_ERROR,
EPC_APP_FILTER_ERROR_INVALID_DATA,
_("OARS filter for user %u has an unrecognized kind %s"),
data->user_id, content_rating_kind);
return;
}
/* Success. Create an #EpcAppFilter object to contain the results. */ /* Success. Create an #EpcAppFilter object to contain the results. */
app_filter = g_new0 (EpcAppFilter, 1); app_filter = g_new0 (EpcAppFilter, 1);
app_filter->ref_count = 1; app_filter->ref_count = 1;
@ -357,6 +425,7 @@ get_app_filter_cb (GObject *obj,
app_filter->app_list = g_steal_pointer (&app_list); app_filter->app_list = g_steal_pointer (&app_list);
app_filter->app_list_type = app_filter->app_list_type =
is_whitelist ? EPC_APP_FILTER_LIST_WHITELIST : EPC_APP_FILTER_LIST_BLACKLIST; is_whitelist ? EPC_APP_FILTER_LIST_WHITELIST : EPC_APP_FILTER_LIST_BLACKLIST;
app_filter->oars_ratings = g_steal_pointer (&oars_variant);
g_task_return_pointer (task, g_steal_pointer (&app_filter), g_task_return_pointer (task, g_steal_pointer (&app_filter),
(GDestroyNotify) epc_app_filter_unref); (GDestroyNotify) epc_app_filter_unref);

View File

@ -33,6 +33,8 @@ G_BEGIN_DECLS
* @EPC_APP_FILTER_ERROR_INVALID_USER: Given user ID doesnt exist * @EPC_APP_FILTER_ERROR_INVALID_USER: Given user ID doesnt exist
* @EPC_APP_FILTER_ERROR_PERMISSION_DENIED: Not authorized to query the app * @EPC_APP_FILTER_ERROR_PERMISSION_DENIED: Not authorized to query the app
* filter for the given user * filter for the given user
* @EPC_APP_FILTER_ERROR_INVALID_DATA: The data stored in the app filter for
* a user is inconsistent or invalid
* *
* Errors which can be returned by epc_get_app_filter_async(). * Errors which can be returned by epc_get_app_filter_async().
* *
@ -42,11 +44,38 @@ typedef enum
{ {
EPC_APP_FILTER_ERROR_INVALID_USER, EPC_APP_FILTER_ERROR_INVALID_USER,
EPC_APP_FILTER_ERROR_PERMISSION_DENIED, EPC_APP_FILTER_ERROR_PERMISSION_DENIED,
EPC_APP_FILTER_ERROR_INVALID_DATA,
} EpcAppFilterError; } EpcAppFilterError;
GQuark epc_app_filter_error_quark (void); GQuark epc_app_filter_error_quark (void);
#define EPC_APP_FILTER_ERROR epc_app_filter_error_quark () #define EPC_APP_FILTER_ERROR epc_app_filter_error_quark ()
/**
* EpcAppFilterOarsValue:
* @EPC_APP_FILTER_OARS_VALUE_UNKNOWN: Unknown value for the given
* section.
* @EPC_APP_FILTER_OARS_VALUE_NONE: No rating for the given section.
* @EPC_APP_FILTER_OARS_VALUE_MILD: Mild rating for the given section.
* @EPC_APP_FILTER_OARS_VALUE_MODERATE: Moderate rating for the given
* section.
* @EPC_APP_FILTER_OARS_VALUE_INTENSE: Intense rating for the given
* section.
*
* Rating values of the intensity of a given section in an app or game.
* These are directly equivalent to the values in the #AsContentRatingValue
* enumeration in libappstream.
*
* Since: 0.1.0
*/
typedef enum
{
EPC_APP_FILTER_OARS_VALUE_UNKNOWN,
EPC_APP_FILTER_OARS_VALUE_NONE,
EPC_APP_FILTER_OARS_VALUE_MILD,
EPC_APP_FILTER_OARS_VALUE_MODERATE,
EPC_APP_FILTER_OARS_VALUE_INTENSE,
} EpcAppFilterOarsValue;
/** /**
* EpcAppFilter: * EpcAppFilter:
* *
@ -72,6 +101,9 @@ uid_t epc_app_filter_get_user_id (EpcAppFilter *filter);
gboolean epc_app_filter_is_path_allowed (EpcAppFilter *filter, gboolean epc_app_filter_is_path_allowed (EpcAppFilter *filter,
const gchar *path); const gchar *path);
EpcAppFilterOarsValue epc_app_filter_get_oars_value (EpcAppFilter *filter,
const gchar *oars_section);
void epc_get_app_filter_async (GDBusConnection *connection, void epc_get_app_filter_async (GDBusConnection *connection,
uid_t user_id, uid_t user_id,
gboolean allow_interactive_authorization, gboolean allow_interactive_authorization,