Jump to content

User:X!/URS

From Wikipedia, the free encyclopedia

This is a planning and information page for Unblock Request System (URS).

Mission

[edit]

The mission of URS is to provide an organized, intuitive interface for blocked users to discuss terms of their unblock in private with trusted administrators.

Users

[edit]

There are 4 types of users of URS. These are not mutually exclusive, so one user can be classified in two or more types of users.

  1. Blocked users - users who are requesting an unblock. They want to access the frontend for requesting and negotiating their unblock, as well as 1 part of the api for an RSS feed.
  2. Wikipedia administrators - administrators on Wikipedia who negotiate and unblock users. They want to access the frontend for discussing and closing requests, as well as preferences for their user account on the tool.
  3. Tool administrators - Users who have higher rights on the tool than the Wikipedia administrators. They have access to the backend, used for administrating users, approving admins, and other maintenance tasks.
  4. IRC Bot - A bot that hangs out in the unblock channel on IRC that would give status reports when asked, or at a set interval. Interacts with the api application.

Applications

[edit]

There are 3 types of applications in URS, each one aimed at a different user.

  1. API - The API is similar to the MediaWiki API (in fact, it uses the same code) in that it returns results in XML, JSON, PHP, YAML, etc formats that are generated by the backend. There are 3 possible uses for it, 2 are aimed at the IRC Bot, and 1 is at the blocked users.
  2. Backend - The backend is where the tool admins can administrate the workings of the tool. They can edit and delete requests, approve and block wiki admins, and edit the templates that users can use.
  3. Frontend - This is where the meat is. It is used by 2 users: the blocked users and the wiki admins. They blocked editors can post requests here, check the status, and post replies. The admins can view a list of requests, edit requests, reply, close requests, ban blocked editors, and change their account preferences.

Stories

[edit]

Each application has several dedicated purposes. This describes all the features of each application.

API

[edit]

Story A1: Get current requests

[edit]

This module is secured with a username and password, which the bot must use to get the private list. The list provides a list of open requests, with information such as the IP, number of replies, etc. The bot can also refine this query by providing a limit of entries, by filtering IP, username, etc.

Story A2: Get a single request

[edit]

This module provides similar info to A1, except that it only lists 1 certain entry, and it has the content of all the replies included with the info about the request. It is also secured with a username and password.

Story A3: RSS feed

[edit]

Unlike this other stories, this is meant to be accessed by the blocked user with an RSS reader. It is not protected by a username and password, but rather by a token that they are given when they make the request. It is a feed of all the comments made on the request, and nothing else


Backend

[edit]

All of these stories are meant to be accessed only with a username and password, so it is not mentioned individually in each description.

Story B1: An admin can edit and delete requests

[edit]

The admin views a list of requests, and can both edit and delete them as needed.

Story B2: An admin can approve users

[edit]

The admin views a list of users, which can be filtered to view only unapproved users. They then can approve the user to edit requests. Until then, the user cannot read requests or edit them until they are approved.

The approval process goes like this:

  1. User applies for an account
  2. User makes post on talk page confirming
  3. User verifies email address
  4. Admin approved once steps 2 and 3 are completed

Story B3: An admin can block abusive users

[edit]

If necessary, to prevent abuse, an admin can block a user. They can provide a reason for the block, as well as an expiry.


Frontend (blocked users)

[edit]

Story FB1: Users can post a request

[edit]

The user fills out a form with username or IP address, reason for unblock, email, etc. If a username is not given, it will use the users current IP address. If no block is found, on the username, IP, or an autoblock, it will reject the request. If no reason is given, it will reject the request. If the username/IP/email is blocked, it will reject the request.

Once the request is made, the user verifies their email with a confirm page (Story FB2), and it is then pushed live.

Story FB2: Users can confirm their email address

[edit]

This is accessed only with a link in an email message. If the token provided matches the database, it approves the request and pushes it live out of the holding area. An email is then sent with a link to the request page (Story FB3), with a secret token that they can access it with.

Story FB3: User can check the status of their request

[edit]

This area is protected by a token they the user is provided after verifying their email address. If they provide the right token, their request is shown. It shows all the block info, the username, and a list of public replies. They can see the admin that made each reply, timestamp, and content of the reply. The content is passed through the en.wikipedia parser, so it can use templates stored on enwiki, images hosted on commons, and all the wikimarkup on Wikipedia.

Story FB4: User can reply to their request

[edit]

If a concern is made, they can make another comment to the request, just as admins are able to do.


Frontend (wiki admins)

[edit]

This is all protected with a username and password, so it is not worth mentioning in each story.

Story FA1: An admin can view a list of requests

[edit]

The admin can view all the currently open requests, which can be filtered by IP, date, number of replies, block type, status of request, etc. The list is paginated, so they can view up to ## request at a time before going to the next page.

Story FA2: An admin can view a request

[edit]

Similar to story FB3, all the blockinfo is shown, the username, and the parsed replies.

Story FA3: The admin can reply to the request

[edit]

There are two types of replies: public, and private. Public replies can be viewed by both the user and the admins, while private replies can only be viewed by the admins. It has a textbox for the content of the reply, as well as a dropdown for changing the status of the request (new, on hold, denied, accepted). This also provides for the ability to close a request, by choosing "denied" or "accepted". This does not remove the request from the main page, so other admins can review it. After 30 days, it is removed and archived automatically.

Story FA4: An admin can ban an IP, username, block ID, or email address

[edit]

The admin can block a user, etc by visiting this module. They enter the user to be blocked, a time of expiry, and a reason.

Story FA5: sfDoctrineApplyPlugin pages

[edit]

To handle the user administration, URS will be using the sfDoctrineApplyPlugin. This provides account application, email confirmation, user settings, etc. The details are not too important, because it's all in the README.

Database

[edit]

YAML

[edit]
UrsRequest:
  actAs: { Timestampable: ~ }
  columns:
    
    name:
      type: string(255)
      notnull: true
    ip:
      type: string(255)
      notnull: true
    wiki_block:
      type: integer
    
    email:
      type: string(255)
      notnull: true
    email_token:
      type: string(32)
    
    type:
      type: integer(1)
      notnull: true
      default: 0
    status:
      type: integer(1)
      notnull: true
      default: 0
    
    token:
      type: string(32)
      notnull: true
      unique: true
    is_activated:
      type: boolean
      notnull: true
      default: 0
    expires_at: 
      type: timestamp
      notnull: true

UrsComment:
  actAs: { Timestampable: ~ }
  columns:
    
    request_id:
      type: integer
      notnull: true
    
    profile_id:
      type: string(255)
      notnull: true
    
    text:
      type: blob
      notnull: true
    
    status:
      type: integer(1)
      notnull: true
      default: 0
  relations:
    UrsRequest:
      local: request_id
      foreign: id
      foreignAlias: Comments
    sfGuardUserProfile:
      local: profile_id
      foreign: id
      foreignAlias: Comments

UrsBlock:
  actAs: { Timestampable: ~ }
  columns:
    
    type:
      type: integer
      notnull: true
    
    target:
      type: string(255)
      notnull: true
    
    reason:
      type: string(255)
      notnull: true
    
    expiry:
      type: timestamp
      notnull: true
    
    is_active:
      type: boolean
      notnull: true
      default: 1
    

sfGuardUserProfile:
  tableName: sf_guard_user_profile
  columns:
    id:
      type: integer
      primary: true
      autoincrement: true
    
    wiki_id:
      type: integer
      notnull: true
    
    user_id:
      type: integer
      notnull: true
      
    email:
      type: string(255)
      
    fullname:
      type: string(255)
      
    validate:
      type: string(17)
    
    is_blocked:
      type: boolean
      notnull: true
      default: 0
    
    is_approved:
      type: boolean
      notnull: true
      default: 0
    
  relations:
    User:
      class: sfGuardUser
      foreign: id
      local: user_id
      type: one  
      onDelete: cascade    
      foreignType: one
      foreignAlias: Profile

sfGuardGroup:
  actAs: [Timestampable]
  columns:
    name:
      type: string(255)
      unique: true
    description: string(1000)
  relations:
    Users:
      class: sfGuardUser
      refClass: sfGuardUserGroup
      local: group_id
      foreign: user_id
      foreignAlias: Groups
    Permissions:
      class: sfGuardPermission
      local: group_id
      foreign: permission_id
      refClass: sfGuardGroupPermission
      foreignAlias: Groups

sfGuardPermission:
  actAs: [Timestampable]
  columns:
    name:
      type: string(255)
      unique: true
    description: string(1000)

sfGuardGroupPermission:
  options:
    symfony:
      form:   false
      filter: false
  actAs: [Timestampable]
  columns:
    group_id:
      type: integer
      primary: true
    permission_id:
      type: integer
      primary: true
  relations:
    Group:
      class: sfGuardGroup
      local: group_id
      onDelete: CASCADE
    Permission:
      class: sfGuardPermission
      local: permission_id
      onDelete: CASCADE

sfGuardUser:
  actAs: [Timestampable]
  columns:
    first_name: string(255)
    last_name: string(255)
    email_address:
      type: string(255)
      notnull: true
      unique: true
    username:
      type: string(128)
      notnull: true
      unique: true
    algorithm:
      type: string(128)
      default: sha1
      notnull: true
    salt: string(128)
    password: string(128)
    is_active:
      type: boolean
      default: 1
    is_super_admin:
      type: boolean
      default: false
    last_login:
      type: timestamp
  indexes:
    is_active_idx:
      fields: [is_active]
  relations:
    Groups:
      class: sfGuardGroup
      local: user_id
      foreign: group_id
      refClass: sfGuardUserGroup
      foreignAlias: Users
    Permissions:
      class: sfGuardPermission
      local: user_id
      foreign: permission_id
      refClass: sfGuardUserPermission
      foreignAlias: Users

sfGuardUserPermission:
  options:
    symfony:
      form:   false
      filter: false
  actAs: [Timestampable]
  columns:
    user_id:
      type: integer
      primary: true
    permission_id:
      type: integer
      primary: true
  relations:
    User:
      class: sfGuardUser
      local: user_id
      onDelete: CASCADE
    Permission:
      class: sfGuardPermission
      local: permission_id
      onDelete: CASCADE

sfGuardUserGroup:
  options:
    symfony:
      form:   false
      filter: false
  actAs: [Timestampable]
  columns:
    user_id:
      type: integer
      primary: true
    group_id:
      type: integer
      primary: true
  relations:
    User:
      class: sfGuardUser
      local: user_id
      onDelete: CASCADE
    Group:
      class: sfGuardGroup
      local: group_id
      onDelete: CASCADE

sfGuardRememberKey:
  options:
    symfony:
      form:   false
      filter: false
  actAs: [Timestampable]
  columns:
    user_id: integer
    remember_key: string(32)
    ip_address: string(50)
  relations:
    User:
      class: sfGuardUser
      local: user_id
      foreignAlias: RememberKeys
      foreignType: one
      onDelete: CASCADE

sfGuardForgotPassword:
  options:
    symfony:
      form:   false
      filter: false
  actAs: [Timestampable]
  columns:
    user_id:
      type: integer
      notnull: true
    unique_key: string(255)
    expires_at:
      type: timestamp
      notnull: true
  relations:
    User:
      class: sfGuardUser
      local: user_id
      type: one
      foreignType: one
      foreignAlias: ForgotPassword
      onDelete: CASCADE

SQL

[edit]
CREATE TABLE urs_block (id BIGINT AUTO_INCREMENT, type BIGINT NOT NULL, target VARCHAR(255) NOT NULL, reason VARCHAR(255) NOT NULL, expiry DATETIME NOT NULL, is_active TINYINT(1) DEFAULT '1' NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, PRIMARY KEY(id)) ENGINE = INNODB;
CREATE TABLE urs_comment (id BIGINT AUTO_INCREMENT, request_id BIGINT NOT NULL, profile_id VARCHAR(255) NOT NULL, text LONGBLOB NOT NULL, status TINYINT DEFAULT 0 NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, INDEX request_id_idx (request_id), INDEX profile_id_idx (profile_id), PRIMARY KEY(id)) ENGINE = INNODB;
CREATE TABLE urs_request (id BIGINT AUTO_INCREMENT, name VARCHAR(255) NOT NULL, ip VARCHAR(255) NOT NULL, wiki_block BIGINT, email VARCHAR(255) NOT NULL, email_token VARCHAR(32), type TINYINT DEFAULT 0 NOT NULL, status TINYINT DEFAULT 0 NOT NULL, token VARCHAR(32) NOT NULL UNIQUE, is_activated TINYINT(1) DEFAULT '0' NOT NULL, expires_at DATETIME NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, PRIMARY KEY(id)) ENGINE = INNODB;
CREATE TABLE sf_guard_forgot_password (id BIGINT AUTO_INCREMENT, user_id BIGINT NOT NULL, unique_key VARCHAR(255), expires_at DATETIME NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, INDEX user_id_idx (user_id), PRIMARY KEY(id)) ENGINE = INNODB;
CREATE TABLE sf_guard_group (id BIGINT AUTO_INCREMENT, name VARCHAR(255) UNIQUE, description TEXT, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, PRIMARY KEY(id)) ENGINE = INNODB;
CREATE TABLE sf_guard_group_permission (group_id BIGINT, permission_id BIGINT, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, PRIMARY KEY(group_id, permission_id)) ENGINE = INNODB;
CREATE TABLE sf_guard_permission (id BIGINT AUTO_INCREMENT, name VARCHAR(255) UNIQUE, description TEXT, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, PRIMARY KEY(id)) ENGINE = INNODB;
CREATE TABLE sf_guard_remember_key (id BIGINT AUTO_INCREMENT, user_id BIGINT, remember_key VARCHAR(32), ip_address VARCHAR(50), created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, INDEX user_id_idx (user_id), PRIMARY KEY(id)) ENGINE = INNODB;
CREATE TABLE sf_guard_user (id BIGINT AUTO_INCREMENT, first_name VARCHAR(255), last_name VARCHAR(255), email_address VARCHAR(255) NOT NULL UNIQUE, username VARCHAR(128) NOT NULL UNIQUE, algorithm VARCHAR(128) DEFAULT 'sha1' NOT NULL, salt VARCHAR(128), password VARCHAR(128), is_active TINYINT(1) DEFAULT '1', is_super_admin TINYINT(1) DEFAULT '0', last_login DATETIME, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, INDEX is_active_idx_idx (is_active), PRIMARY KEY(id)) ENGINE = INNODB;
CREATE TABLE sf_guard_user_group (user_id BIGINT, group_id BIGINT, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, PRIMARY KEY(user_id, group_id)) ENGINE = INNODB;
CREATE TABLE sf_guard_user_permission (user_id BIGINT, permission_id BIGINT, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, PRIMARY KEY(user_id, permission_id)) ENGINE = INNODB;
CREATE TABLE sf_guard_user_profile (id BIGINT AUTO_INCREMENT, wiki_id BIGINT NOT NULL, user_id BIGINT NOT NULL, email VARCHAR(255), fullname VARCHAR(255), validate VARCHAR(17), is_blocked TINYINT(1) DEFAULT '0' NOT NULL, is_approved TINYINT(1) DEFAULT '0' NOT NULL, INDEX user_id_idx (user_id), PRIMARY KEY(id)) ENGINE = INNODB;
ALTER TABLE urs_comment ADD CONSTRAINT urs_comment_request_id_urs_request_id FOREIGN KEY (request_id) REFERENCES urs_request(id);
ALTER TABLE urs_comment ADD CONSTRAINT urs_comment_profile_id_sf_guard_user_profile_id FOREIGN KEY (profile_id) REFERENCES sf_guard_user_profile(id);
ALTER TABLE sf_guard_forgot_password ADD CONSTRAINT sf_guard_forgot_password_user_id_sf_guard_user_id FOREIGN KEY (user_id) REFERENCES sf_guard_user(id) ON DELETE CASCADE;
ALTER TABLE sf_guard_group_permission ADD CONSTRAINT sf_guard_group_permission_permission_id_sf_guard_permission_id FOREIGN KEY (permission_id) REFERENCES sf_guard_permission(id) ON DELETE CASCADE;
ALTER TABLE sf_guard_group_permission ADD CONSTRAINT sf_guard_group_permission_group_id_sf_guard_group_id FOREIGN KEY (group_id) REFERENCES sf_guard_group(id) ON DELETE CASCADE;
ALTER TABLE sf_guard_remember_key ADD CONSTRAINT sf_guard_remember_key_user_id_sf_guard_user_id FOREIGN KEY (user_id) REFERENCES sf_guard_user(id) ON DELETE CASCADE;
ALTER TABLE sf_guard_user_group ADD CONSTRAINT sf_guard_user_group_user_id_sf_guard_user_id FOREIGN KEY (user_id) REFERENCES sf_guard_user(id) ON DELETE CASCADE;
ALTER TABLE sf_guard_user_group ADD CONSTRAINT sf_guard_user_group_group_id_sf_guard_group_id FOREIGN KEY (group_id) REFERENCES sf_guard_group(id) ON DELETE CASCADE;
ALTER TABLE sf_guard_user_permission ADD CONSTRAINT sf_guard_user_permission_user_id_sf_guard_user_id FOREIGN KEY (user_id) REFERENCES sf_guard_user(id) ON DELETE CASCADE;
ALTER TABLE sf_guard_user_permission ADD CONSTRAINT sf_guard_user_permission_permission_id_sf_guard_permission_id FOREIGN KEY (permission_id) REFERENCES sf_guard_permission(id) ON DELETE CASCADE;
ALTER TABLE sf_guard_user_profile ADD CONSTRAINT sf_guard_user_profile_user_id_sf_guard_user_id FOREIGN KEY (user_id) REFERENCES sf_guard_user(id) ON DELETE CASCADE;