ShariX Plugin for Moodle

Alexandra Panyukova 914ea9deef stage 5 3 weeks ago
classes 914ea9deef stage 5 3 weeks ago
db 914ea9deef stage 5 3 weeks ago
lang 914ea9deef stage 5 3 weeks ago
tests 914ea9deef stage 5 3 weeks ago
.gitignore f485c1febf initial commit 3 weeks ago
LICENSE f485c1febf initial commit 3 weeks ago
README.md 914ea9deef stage 5 3 weeks ago
client.php 2b73aed990 fix 3 weeks ago
manage.php 2b73aed990 fix 3 weeks ago
settings.php f485c1febf initial commit 3 weeks ago
version.php 914ea9deef stage 5 3 weeks ago

README.md

local_sharix

Moodle-side integration boundary for ShariX Open / Platform.

This repository is the standalone GPLv3 Moodle plugin described by the ShariX ↔ Moodle integration specification. The Moodle component name is fixed as local_sharix. The plugin is licensed as GPL-3.0-only.

Implemented through Stage 5

This source contains the accepted Stage 1 foundation, the Moodle-side Stage 2 course contract, the Stage 4 managed-access contract, and the Stage 5 normalized course-user-state contract:

  • one ShariX client per dedicated technical Moodle user;
  • fail-closed client enable/disable state;
  • three independent policy flags (allow_existing_courses, allow_provider_courses, allow_course_creation);
  • explicit curated existing-course scope;
  • provider category and provider role configuration for provider-course discovery and creation;
  • system capability local/sharix:useintegration;
  • predeclared restricted custom service local_sharix_api;
  • local_sharix_get_info handshake / feature discovery;
  • local_sharix_resolve_users exact bulk username resolution;
  • audit persistence foundation with recursive secret-key redaction;
  • Moodle audit event for administrative client-policy changes;
  • English and Russian UI/error strings;
  • PHPUnit tests for the client boundary, handshake, exact UID lookup, and audit redaction;
  • category-backed existing/provider course scopes with optional subcategory expansion;
  • local_sharix_get_courses;
  • local_sharix_get_actor_courses, using native moodle/course:update capability rather than role ids;
  • idempotent local_sharix_create_course;
  • Moodle Lock API plus a database uniqueness constraint for duplicate creation protection;
  • deterministic generated shortnames when ShariX does not supply one;
  • configured provider-category placement and configured course-context role assignment;
  • plugin ownership metadata for created courses and Moodle/audit events;
  • local_sharix_access_binding ownership records keyed by (client, external_key);
  • local_sharix_sync_cohort_access with plugin-owned hidden cohorts and standard Moodle cohort-enrolment instances;
  • idempotent cohort membership reconciliation with stale removal limited to the exact owned cohort;
  • local_sharix_ensure_user_enrolment for controlled individual access;
  • preservation of existing LDAP/cohort/manual/other active enrolment paths;
  • Moodle Lock API and database uniqueness for concurrent access provisioning;
  • normalized managed-access diagnostics and audit records;
  • local_sharix_get_course_user_state for one scoped course and exact canonical username;
  • normalized enrolment, completion-enabled/tracked/completed state and overall completion timestamp;
  • normalized grade availability/value/maximum/percentage for the requested Moodle grade item or course total;
  • read-only state lookup that never creates access and never decides ShariX RequirementCheck pass/fail.

Stage 5 keeps business evaluation in ShariX. Moodle reports facts; ShariX applies completion and minimum-score policy.

Moodle version baseline

The target deployment is confirmed as Moodle 5.0.2+ (Build: 20250926). This plugin therefore requires Moodle 5.0.2 (2025041402) or later within the Moodle 5.0 branch and declares $plugin->supported = [500, 500].

Stage 1 through Stage 5 implementation must be checked against the Moodle 5.0 External API, Access API, Course API, Enrolment API, Cohort API, Upgrade API, and locking APIs. The plugin does not claim compatibility with Moodle 4.x or with Moodle 5.1+ until that range is explicitly tested and released.

Installation

Copy or clone this repository as:

<moodle-root>/local/sharix

Then run the normal Moodle plugin upgrade procedure. Do not patch Moodle core.

The ShariX Integration API now exposes normalized Stage 4 managed-access and Stage 5 course-user-state endpoints. The check workflow no longer needs direct manual-enrolment, completion-status, or grade-report core web-service functions. A small set of standard read-only core functions remains temporarily for compatibility paths outside Stage 5 and can be removed in a later cleanup once those paths are fully retired.

Client setup

  1. Create a dedicated Moodle technical user for one ShariX deployment.
  2. Create a system role which grants only the capabilities required for the integration, including local/sharix:useintegration, and assign it to that user at system context.
  3. Open Site administration → Plugins → Local plugins → ShariX integration clients.
  4. Create one client record for that exact technical username.
  5. Configure the three policy flags and explicit existing-course scope. Empty scope means no existing courses are allowed. The client code becomes read-only after creation because it is a stable integration identifier.
  6. Configure provider category / provider role before enabling the corresponding future write capability.
  7. Enable Moodle web services and REST as required by the deployment.
  8. The plugin declares the restricted service ShariX Integration API (local_sharix_api). Authorize only the intended technical user for that service and issue an individual token for that user.
  9. Store the raw token only on the ShariX deployment side. local_sharix never stores raw tokens.

Use a separate technical user, client record, and token for every ShariX deployment (for example production and testing).

Web-service contract through Stage 5

local_sharix_get_info

No business parameters. The authenticated web-service user must:

  • have local/sharix:useintegration;
  • have exactly one local_sharix_client record;
  • have that client enabled.

The response exposes API version, current client flags and feature flags. At Stage 5 both managed_cohort_access and course_user_state are true.

local_sharix_resolve_users

Input:

{
  "usernames": ["+31611111111", "+31622222222"]
}

The lookup is exact and local-site-only. Duplicate inputs are collapsed to the first occurrence. Missing users are normal results, not exceptions. Only username, Moodle id, active/suspended/deleted state are returned; profile names and e-mail addresses are not exposed.

The canonical ShariX deployment convention remains:

ShariX user.phone_number = LDAP uid = Moodle username

The plugin itself does not create users and does not become an identity source.

local_sharix_get_courses

Returns only curated existing courses in the authenticated client scope. Scope is the union of explicit course mappings and enabled existing category scopes. Empty scope is empty; it never means all courses.

local_sharix_get_actor_courses

Input is exact actor_uid. The plugin resolves the actor by exact Moodle username, limits candidate courses to the client provider scope, and then applies Moodle's native moodle/course:update capability for that actor. Missing or inactive actors return a normalized result with an empty course list.

local_sharix_create_course

Creates a course only when allow_course_creation is enabled and the client has a valid provider category and course-context provider role. The operation requires an opaque idempotency_key. A retry with the same key and same payload returns the same course; the same key with a different payload is rejected.

The course is created in the configured provider category. The actor receives the configured role at course context with plugin ownership metadata. Creation uses a Moodle lock and a unique (clientid, idempotencyhash) database index.

local_sharix_sync_cohort_access

Inputs are an opaque stable external_key, a Moodle courseid, canonical Moodle usernames, and remove_stale. The course must belong to the authenticated client's Moodle-side scope.

The first call creates or reuses exactly one local_sharix-owned hidden system cohort and one standard Moodle cohort-enrolment instance. Repeated calls reconcile membership idempotently. When remove_stale=true, removals are performed only from that exact owned cohort. The plugin never removes a user's LDAP enrolment, another cohort membership, administrator-managed manual access, or another client's cohort.

The response reports binding/cohort/enrolment-instance ids, requested/resolved counts, additions/removals, and missing usernames. Missing users are a structured result and do not turn the whole reconciliation into an opaque server error.

local_sharix_ensure_user_enrolment

Inputs are an opaque stable external_key, a Moodle courseid, and one exact Moodle username. If the user already has active access by any Moodle enrolment path, the call succeeds without creating redundant ShariX access. Otherwise the plugin idempotently uses the standard Moodle manual-enrolment plugin and records the ShariX binding.

Stage 4 deliberately has no automatic unenrol counterpart. Completing or deleting a ShariX check does not revoke access.

local_sharix_get_course_user_state

Inputs are a scoped Moodle courseid, one exact Moodle username, and optional grade_item_id (0 means the Moodle course total). The endpoint is read-only. It returns normalized Moodle facts:

user_found / active / userid
enrolled
completion_enabled / completion_tracked / completed / completion_time
grade.available / itemid / value / max / percent

The course must be in the authenticated client's persisted Moodle-side scope. A missing, suspended, or deleted user is returned as a normal normalized state rather than an opaque exception. Existing LDAP, cohort, manual, or other active enrolment is observed without creating a local_sharix_access_binding.

The plugin does not decide whether the result passes a ShariX training requirement. It does not know CheckDefinition, minimum_score, Provider relationships, or ShariX service semantics. Those decisions remain in ShariX.

Completion is reported from Moodle's native completion subsystem. Grade lookup uses the requested item when supplied and otherwise the course-total grade item; the API returns both the raw final grade and a normalized percentage. If no numeric grade exists, grade.available=false.

Stable error codes

The machine-readable error contract currently uses:

CLIENT_NOT_CONFIGURED
CLIENT_DISABLED
FEATURE_NOT_ALLOWED
COURSE_OUT_OF_SCOPE
ACTOR_NOT_FOUND
ACTOR_INACTIVE
ACTOR_CANNOT_MANAGE_COURSE
PROVIDER_CATEGORY_NOT_CONFIGURED
PROVIDER_ROLE_NOT_CONFIGURED
COURSE_CREATE_FAILED
IDEMPOTENCY_CONFLICT
LOCK_TIMEOUT
INVALID_REQUEST
USER_NOT_FOUND
USER_INACTIVE
ACCESS_BINDING_CONFLICT
ACCESS_PROVISION_FAILED
OWNERSHIP_MISMATCH
ENROLMENT_METHOD_UNAVAILABLE

Localized error text is presentation only. ShariX adapters must branch on stable error codes, never translated text.

Security invariants

  • Unknown technical user: deny.
  • Disabled client: deny.
  • Custom service is restricted to explicitly authorized users.
  • Client scope is fail-closed.
  • Raw tokens/passwords/secrets are never persisted or returned.
  • Audit metadata recursively removes fields whose keys indicate credentials/tokens/secrets.
  • One technical Moodle user can belong to only one ShariX client.
  • Course IDs are deployment data, not source constants.
  • No Moodle core modifications.
  • Managed stale-member removal is restricted to the exact local_sharix-owned cohort.
  • Existing LDAP/manual/other active access is never removed by Stage 4 or modified by Stage 5 state reads.
  • An external_key is opaque integration data; the plugin does not parse ShariX business semantics from it.

Tests

Run within a matching Moodle test environment:

vendor/bin/phpunit --testsuite local_sharix_testsuite

At minimum, the current acceptance suite must cover:

  • unknown technical user denied;
  • disabled client denied;
  • enabled client bound to the current technical user;
  • get_info returns API v1 and client flags;
  • exact username resolution, including + phone UID;
  • missing/suspended/deleted users;
  • no profile data leakage in resolver output;
  • audit secret-key redaction;
  • explicit/category course-scope isolation;
  • subcategory expansion;
  • provider-course filtering by moodle/course:update with a custom role;
  • course-creation feature enforcement;
  • provider category and role assignment;
  • idempotent retry and changed-payload conflict;
  • first managed cohort sync provisions one owned cohort and one cohort-enrolment instance;
  • repeated cohort sync is idempotent;
  • stale removal affects only the owned cohort;
  • pre-existing external access remains active;
  • individual ensure-enrolment is idempotent and does not duplicate existing access;
  • access bindings are isolated by client and stable external key;
  • course-user-state denies out-of-scope courses;
  • missing users produce normalized state;
  • an already enrolled user is observed without creating ShariX access;
  • completion state and completion timestamp are normalized;
  • grade value/maximum/percentage are normalized for a selected grade item.

The supplied source bundle does not contain a Moodle core checkout, so PHPUnit and install/upgrade execution must be run in the real/sandbox Moodle 5.0.2+ environment before acceptance.

Stage 5 deployment prerequisites

The standard Moodle enrolment plugins Cohort sync (cohort) and Manual enrolments (manual) remain required for Stage 4 managed access. Moodle site/course completion must be enabled on courses where completion is a ShariX requirement. Existing ShariX client records, technical users, tokens, policy flags and course scopes are retained across the plugin upgrade. No third-party completion or grade web-service plugin is required.

Stage 5 does not introduce a new Moodle-side training-status domain and does not replace ShariX document/manual evidence. local_sharix_get_course_user_state is only an integration evidence source for the existing shared RequirementCheck workflow.