Merge pull request #4 from endlessm/T23999-oars-storage
T23999 Store OARS filter
This commit is contained in:
commit
ebb892092f
|
@ -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.
|
||||||
|
|
||||||
|
It’s 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>
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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, that’s
|
||||||
|
* 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);
|
||||||
|
|
|
@ -33,6 +33,8 @@ G_BEGIN_DECLS
|
||||||
* @EPC_APP_FILTER_ERROR_INVALID_USER: Given user ID doesn’t exist
|
* @EPC_APP_FILTER_ERROR_INVALID_USER: Given user ID doesn’t 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,
|
||||||
|
|
Loading…
Reference in New Issue