diff --git a/api/check_examples.py b/api/check_examples.py
index 00e75263..a0cd0658 100755
--- a/api/check_examples.py
+++ b/api/check_examples.py
@@ -29,6 +29,33 @@ except ImportError as e:
raise
+def check_parameter(filepath, request, parameter):
+ schema = parameter.get("schema")
+ example = None
+ try:
+ example_json = schema.get('example')
+ if example_json:
+ example = json.loads(example_json)
+ except Exception as e:
+ raise ValueError("Error parsing JSON example request for %r" % (
+ request
+ ), e)
+ fileurl = "file://" + os.path.abspath(filepath)
+ if example and schema:
+ try:
+ print ("Checking request schema for: %r %r" % (
+ filepath, request
+ ))
+ # Setting the 'id' tells jsonschema where the file is so that it
+ # can correctly resolve relative $ref references in the schema
+ schema['id'] = fileurl
+ jsonschema.validate(example, schema)
+ except Exception as e:
+ raise ValueError("Error validating JSON schema for %r %r" % (
+ request, code
+ ), e)
+
+
def check_response(filepath, request, code, response):
example = None
try:
@@ -43,7 +70,9 @@ def check_response(filepath, request, code, response):
fileurl = "file://" + os.path.abspath(filepath)
if example and schema:
try:
- print ("Checking schema for: %r %r %r" % (filepath, request, code))
+ print ("Checking response schema for: %r %r %r" % (
+ filepath, request, code
+ ))
# Setting the 'id' tells jsonschema where the file is so that it
# can correctly resolve relative $ref references in the schema
schema['id'] = fileurl
@@ -59,8 +88,13 @@ def check_swagger_file(filepath):
swagger = yaml.load(f)
for path, path_api in swagger.get('paths', {}).items():
+
for method, request_api in path_api.items():
request = "%s %s" % (method.upper(), path)
+ for parameter in request_api.get('parameters', ()):
+ if parameter['in'] == 'body':
+ check_parameter(filepath, request, parameter)
+
try:
responses = request_api['responses']
except KeyError:
diff --git a/api/client-server/v1/login.yaml b/api/client-server/v1/login.yaml
index 2df695be..3d415c29 100644
--- a/api/client-server/v1/login.yaml
+++ b/api/client-server/v1/login.yaml
@@ -29,7 +29,6 @@ paths:
parameters:
- in: body
name: body
- required: true
schema:
type: object
example: |-
@@ -63,7 +62,19 @@ paths:
description: The fully-qualified Matrix ID that has been registered.
access_token:
type: string
- description: An access token for the account. This access token can then be used to authorize other requests.
+ description: |-
+ An access token for the account.
+ This access token can then be used to authorize other requests.
+ The access token may expire at some point, and if so, it SHOULD come with a ``refresh_token``.
+ There is no specific error message to indicate that a request has failed because
+ an access token has expired; instead, if a client has reason to believe its
+ access token is valid, and it receives an auth error, they should attempt to
+ refresh for a new token on failure, and retry the request with the new token.
+ refresh_token:
+ type: string
+ # TODO: Work out how to linkify /tokenrefresh
+ description: |-
+ (optional) A ``refresh_token`` may be exchanged for a new ``access_token`` using the /tokenrefresh API endpoint.
home_server:
type: string
description: The hostname of the Home Server on which the account has been registered.
@@ -77,3 +88,60 @@ paths:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"
+ "/tokenrefresh":
+ post:
+ summary: Exchanges a refresh token for an access token.
+ description: |-
+ Exchanges a refresh token for a new access token.
+ This is intended to be used if the access token has expired.
+ security:
+ - accessToken: []
+ parameters:
+ - in: body
+ name: body
+ schema:
+ type: object
+ example: |-
+ {
+ "refresh_token": "a1b2c3"
+ }
+ properties:
+ refresh_token:
+ type: string
+ description: The refresh token which was issued by the server.
+ required: ["refresh_token"]
+ responses:
+ 200:
+ description: |-
+ The refresh token was accepted, and a new access token has been issued.
+ The passed refresh token is no longer valid and cannot be used.
+ A new refresh token will have been returned unless some policy does
+ not allow the user to continue to renew their session.
+ examples:
+ application/json: |-
+ {
+ "access_token": "bearwithme123",
+ "refresh_token": "exchangewithme987"
+ }
+ schema:
+ type: object
+ properties:
+ access_token:
+ type: string
+ description: |-
+ An access token for the account.
+ This access token can then be used to authorize other requests.
+ The access token may expire at some point, and if so, it SHOULD come with a ``refresh_token``.
+ refresh_token:
+ type: string
+ description: (optional) A ``refresh_token`` may be exchanged for a new ``access_token`` using the TODO Linkify /tokenrefresh API endpoint.
+ 403:
+ description: |-
+ The exchange attempt failed. For example, the refresh token may have already been used.
+ examples:
+ application/json: |-
+ {"errcode": "M_FORBIDDEN"}
+ 429:
+ description: This request was rate-limited.
+ schema:
+ "$ref": "definitions/error.yaml"
diff --git a/api/client-server/v1/membership.yaml b/api/client-server/v1/membership.yaml
index 04922656..41f2febc 100644
--- a/api/client-server/v1/membership.yaml
+++ b/api/client-server/v1/membership.yaml
@@ -69,7 +69,6 @@ paths:
"/rooms/{roomId}/invite":
post:
summary: Invite a user to participate in a particular room.
- # It's a crying shame that I don't know how to force line breaks.
description: |-
This API invites a user to participate in a particular room.
They do not start participating in the room until they actually join the
diff --git a/drafts/macaroons_caveats.rst b/drafts/macaroons_caveats.rst
new file mode 100644
index 00000000..93622c3d
--- /dev/null
+++ b/drafts/macaroons_caveats.rst
@@ -0,0 +1,34 @@
+Macaroon Caveats
+================
+
+`Macaroons`_ are issued by Matrix servers as authorization tokens. Macaroons may be restricted by adding caveats to them.
+
+.. _Macaroons: http://theory.stanford.edu/~ataly/Papers/macaroons.pdf
+
+Caveats can only be used for reducing the scope of a token, never for increasing it. Servers are required to reject any macroon with a caveat that they do not understand.
+
+Some caveats are specified in this specification, and must be understood by all servers. The use of non-standard caveats is allowed.
+
+All caveats must take the form:
+
+`key` `operator` `value`
+where `key` is a non-empty string drawn from the character set [A-Za-z0-9_]
+`operator` is a non-empty string which does not contain whitespace
+`value` is a non-empty string
+And these are joined by single space characters.
+
+Specified caveats:
+
++-------------+--------------------------------------------------+------------------------------------------------------------------------------------------------+
+| Caveat name | Description | Legal Values |
++-------------+--------------------------------------------------+------------------------------------------------------------------------------------------------+
+| gen | Generation of the macaroon caveat spec. | 1 |
+| user_id | ID of the user for which this macaroon is valid. | Pure equality check. Operator must be =. |
+| type | The purpose of this macaroon. | access - used to authorize any action except token refresh |
+| refresh - only used to authorize a token refresh |
+| time | Time before/after which this macaroon is valid. | A POSIX timestamp in milliseconds (in UTC). |
+| Operator < means the macaroon is valid before the timestamp, as interpreted by the server. |
+| Operator > means the macaroon is valid after the timestamp, as interpreted by the server. |
+| Operator == means the macaroon is valid at exactly the timestamp, as interpreted by the server.|
+| Note that exact equality of time is largely meaningless. |
++-------------+--------------------------------------------------+------------------------------------------------------------------------------------------------+
diff --git a/scripts/speculator/README b/scripts/speculator/README
index 6ab84f68..0a9f53fd 100644
--- a/scripts/speculator/README
+++ b/scripts/speculator/README
@@ -4,6 +4,7 @@ It serves the following HTTP endpoints:
- / lists open pull requests
- /spec/123 which renders the spec as html at pull request 123.
- /diff/rst/123 which gives a diff of the spec's rst at pull request 123.
+ - /diff/html/123 which gives a diff of the spec's HTML at pull request 123.
To run it, you must install the `go` tool, and run:
`go run main.go`
diff --git a/scripts/speculator/htmldiff.pl b/scripts/speculator/htmldiff.pl
new file mode 100755
index 00000000..b1689067
--- /dev/null
+++ b/scripts/speculator/htmldiff.pl
@@ -0,0 +1,564 @@
+#!/usr/bin/perl
+#
+# htmldiff - present a diff marked version of two html documents
+#
+# Copyright (c) 1998-2006 MACS, Inc.
+#
+# Copyright (c) 2007 SiSco, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# See http://www.themacs.com for more information.
+#
+# usage: htmldiff [[-c] [-l] [-o] oldversion newversion [output]]
+#
+# -c - disable metahtml comment processing
+# -o - disable outputting of old text
+# -l - use navindex to create sequence of diffs
+# oldversion - the previous version of the document
+# newversion - the newer version of the document
+# output - a filename to place the output in. If omitted, the output goes to
+# standard output.
+#
+# if invoked with no options or arguments, operates as a CGI script. It then
+# takes the following parameters:
+#
+# oldfile - the URL of the original file
+# newfile - the URL of the new file
+# mhtml - a flag to indicate whether it should be aware of MetaHTML comments.
+#
+# requires GNU diff utility
+# also requires the perl modules Getopt::Std
+#
+# NOTE: The markup created by htmldiff may not validate against the HTML 4.0
+# DTD. This is because the algorithm is realtively simple, and there are
+# places in the markup content model where the span element is not allowed.
+# Htmldiff is NOT aware of these places.
+#
+# $Source: /u/sources/public/2009/htmldiff/htmldiff.pl,v $
+# $Revision: 1.1 $
+#
+# $Log: htmldiff.pl,v $
+# Revision 1.1 2014/01/06 08:04:51 dom
+# added copy of htmldiff perl script since aptest.com repo no longer available
+#
+# Revision 1.5 2008/03/05 13:23:16 ahby
+# Fixed a problem with leading whitespace before markup.
+#
+# Revision 1.4 2007/12/13 13:09:16 ahby
+# Updated copyright and license.
+#
+# Revision 1.3 2007/12/13 12:53:34 ahby
+# Changed use of span to ins and del
+#
+# Revision 1.2 2002/02/13 16:27:23 ahby
+# Changed processing model.
+# Improved handling of old text and changed styles.
+#
+# Revision 1.1 2000/07/12 12:20:04 ahby
+# Updated to remove empty spans - this fixes validation problems under
+# strict.
+#
+# Revision 1.11 1999/12/08 19:46:45 ahby
+# Fixed validation errors introduced by placing markup where it didn't
+# belong.
+#
+# Revision 1.10 1999/10/18 13:42:58 ahby
+# Added -o to the usage message.
+#
+# Revision 1.9 1999/05/04 12:29:11 ahby
+# Added an option to turn off the display of old text.
+#
+# Revision 1.8 1999/04/09 14:37:27 ahby
+# Fixed a perl syntax error.
+#
+# Revision 1.7 1999/04/09 14:35:49 ahby
+# Added reference to MACS homepage.
+#
+# Revision 1.6 1999/04/09 14:35:09 ahby
+# Added comment about validity of generated markup.
+#
+# Revision 1.5 1999/02/22 22:17:54 ahby
+# Changed to use stylesheets.
+# Changed to rely upon span.
+# Changed to work around content model problems.
+#
+# Revision 1.4 1999/02/08 02:32:22 ahby
+# Added a copyright statement.
+#
+# Revision 1.3 1999/02/08 02:30:40 ahby
+# Added header processing.
+#
+# Revision 1.2 1998/12/10 17:31:31 ahby
+# Fixed to escape less-thans in change blocks and to not permit change
+# markup within specific elements (like TITLE).
+#
+# Revision 1.1 1998/11/26 00:09:22 ahby
+# Initial revision
+#
+#
+
+use Getopt::Std;
+
+sub usage {
+ print STDERR "htmldiff [-c] [-o] oldversion newversion [output]\n";
+ exit;
+}
+
+sub url_encode {
+ my $str = shift;
+ $str =~ s/([\x00-\x1f\x7F-\xFF])/
+ sprintf ('%%%02x', ord ($1))/eg;
+ return $str;
+}
+
+# markit - diff-mark the streams
+#
+# markit(file1, file2)
+#
+# markit relies upon GNUdiff to mark up the text.
+#
+# The markup is encoded using special control sequences:
+#
+# a block wrapped in control-a is deleted text
+# a block wrapped in control-b is old text
+# a block wrapped in control-c is new text
+#
+# The main processing loop attempts to wrap the text blocks in appropriate
+# SPANs based upon the type of text that it is.
+#
+# When the loop encounters a < in the text, it stops the span. Then it outputs
+# the element that is defined, then it restarts the span.
+
+sub markit {
+ my $retval = "";
+ my($file1) = shift;
+ my($file2) = shift;
+# my $old="deleted text: %c'\012'%c'\001'%c'\012'%<%c'\012'%c'\001'%c'\012'";
+ my $old="%c'\012'%c'\001'%c'\012'%<%c'\012'%c'\001'%c'\012'";
+ my $new="%c'\012'%c'\003'%c'\012'%>%c'\012'%c'\003'%c'\012'";
+ my $unchanged="%=";
+ my $changed="%c'\012'%c'\001'%c'\012'%<%c'\012'%c'\001'%c'\012'%c'\004'%c'\012'%>%c'\012'%c'\004'%c'\012'";
+ if ($opt_o) {
+ $old = "";
+ $changed = "%c'\012'%c'\004'%c'\012'%>%c'\012'%c'\004'%c'\012'";
+ }
+# my $old="%c'\002'deleted text:%c'\012'%c'\001'%c'\012'%<%c'\012'%c'\001'%c'\012'%c'\012'%c'\002'";
+# my $new="%c'\002'%c'\012'%c'\002'%>%c'\002'%c'\002'%c'\012'";
+# my $unchanged="%=";
+# my $changed="%c'\002'%c'\012'%c'\001'%c'\012'%<%c'\012'%c'\001'%c'\012'%c'\002'%c'\012'%>%c'\012'%c'\002'%c'\002'%c'\012'";
+
+ my @span;
+ $span[0]="";
+ $span[1]="";
+ $span[2]="";
+ $span[3]="";
+ $span[4]="";
+
+ my @diffEnd ;
+ $diffEnd[1] = '';
+ $diffEnd[2] = '';
+ $diffEnd[3] = '';
+ $diffEnd[4] = '';
+
+ my $diffcounter = 0;
+
+ open(FILE, qq(diff -d --old-group-format="$old" --new-group-format="$new" --changed-group-format="$changed" --unchanged-group-format="$unchanged" $file1 $file2 |)) || die("Diff failed: $!");
+# system (qq(diff --old-group-format="$old" --new-group-format="$new" --changed-group-format="$changed" --unchanged-group-format="$unchanged" $file1 $file2 > /tmp/output));
+
+ my $state = 0;
+ my $inblock = 0;
+ my $temp = "";
+ my $lineCount = 0;
+
+# strategy:
+#
+# process the output of diff...
+#
+# a link with control A-D means the start/end of the corresponding ordinal
+# state (1-4). Resting state is state 0.
+#
+# While in a state, accumulate the contents for that state. When exiting the
+# state, determine if it is appropriate to emit the contents with markup or
+# not (basically, if the accumulated buffer contains only empty lines or lines
+# with markup, then we don't want to emit the wrappers. We don't need them.
+#
+# Note that if there is markup in the "old" block, that markup is silently
+# removed. It isn't really that interesting, and it messes up the output
+# something fierce.
+
+ while () {
+ my $anchor = $opt_l ? qq[] : "" ;
+ my $anchorEnd = $opt_l ? q[] : "" ;
+ $lineCount ++;
+ if ($state == 0) { # if we are resting and we find a marker,
+ # then we must be entering a block
+ if (m/^([\001-\004])/) {
+ $state = ord($1);
+ $_ = "";
+ }
+# if (m/^\001/) {
+# $state = 1;
+# s/^/$span[1]/;
+# } elsif (m/^\002/) {
+# $state = 2;
+# s/^/$span[2]/;
+# } elsif (m/^\003/) {
+# $state = 3;
+# s/^/$span[3]/;
+# } elsif (m/^\004/) {
+# $state = 4;
+# s/^/$span[4]/;
+# }
+ } else {
+ # if we are in "old" state, remove markup
+ if (($state == 1) || ($state == 2)) {
+ s/\<.*\>//; # get rid of any old markup
+ s/\</g; # escape any remaining STAG or ETAGs
+ s/\>/>/g;
+ }
+ # if we found another marker, we must be exiting the state
+ if (m/^([\001-\004])/) {
+ if ($temp ne "") {
+ $_ = $span[$state] . $anchor . $temp . $anchorEnd . $diffEnd[$state] . "\n";
+ $temp = "";
+ } else {
+ $_ = "" ;
+ }
+ $state = 0;
+ } elsif (m/^\s*\) { # otherwise, is this line markup?
+ # if it is markup AND we haven't seen anything else yet,
+ # then we will emit the markup
+ if ($temp eq "") {
+ $retval .= $_;
+ $_ = "";
+ } else { # we wrap it with the state switches and hold it
+ s/^/$anchorEnd$diffEnd[$state]/;
+ s/$/$span[$state]$anchor/;
+ $temp .= $_;
+ $_ = "";
+ }
+ } else {
+ if (m/.+/) {
+ $temp .= $_;
+ $_ = "";
+ }
+ }
+ }
+
+ s/\001//g;
+ s/\002//g;
+ s/\003//g;
+ s/\004//g;
+ if ($_ !~ m/^$/) {
+ $retval .= $_;
+ }
+ $diffcounter++;
+ }
+ close FILE;
+ $retval =~ s/$span[1]\n+$diffEnd[1]//g;
+ $retval =~ s/$span[2]\n+$diffEnd[2]//g;
+ $retval =~ s/$span[3]\n+$diffEnd[3]//g;
+ $retval =~ s/$span[4]\n+$diffEnd[4]//g;
+ $retval =~ s/$span[1]\n*$//g;
+ $retval =~ s/$span[2]\n*$//g;
+ $retval =~ s/$span[3]\n*$//g;
+ $retval =~ s/$span[4]\n*$//g;
+ return $retval;
+}
+
+sub splitit {
+ my $filename = shift;
+ my $headertmp = shift;
+ my $inheader=0;
+ my $preformatted=0;
+ my $inelement=0;
+ my $retval = "";
+ my $styles = q(
+);
+ if ($opt_t) {
+ $styles .= q(
+
+);
+
+ }
+
+ if ($stripheader) {
+ open(HEADER, ">$headertmp");
+ }
+
+ my $incomment = 0;
+ my $inhead = 1;
+ open(FILE, $filename) || die("File $filename cannot be opened: $!");
+ while () {
+ if ($inhead == 1) {
+ if (m/\<\/head/i) {
+ print HEADER $styles;
+ }
+ if (m/\
+);
+ }
+ close HEADER;
+ } else {
+ print HEADER;
+ }
+ } else {
+ if ($incomment) {
+ if (m;-->;) {
+ $incomment = 0;
+ s/.*-->//;
+ } else {
+ next;
+ }
+ }
+ if (m;;) {
+ s///;
+ }
+ if (m;