Move the crudhandler to own repo (#27)
This commit is contained in:
parent
d9304f6996
commit
ce2cae9430
228 changed files with 13281 additions and 3292 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,6 +1,4 @@
|
||||||
.idea/
|
.idea/
|
||||||
.idea/*
|
|
||||||
.idea/*/*
|
|
||||||
config.yml
|
config.yml
|
||||||
config.yaml
|
config.yaml
|
||||||
*.db
|
*.db
|
||||||
|
|
|
@ -3,7 +3,7 @@ POST http://localhost:8080/api/v1/login
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"username": "user",
|
"username": "user5",
|
||||||
"password": "1234"
|
"password": "1234"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,9 +15,9 @@ POST http://localhost:8080/api/v1/register
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"username": "user4",
|
"username": "user5",
|
||||||
"password": "1234",
|
"password": "1234",
|
||||||
"email": "4@knt.li"
|
"email": "5@knt.li"
|
||||||
}
|
}
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
|
@ -5,25 +5,24 @@ Authorization: Bearer {{auth_token}}
|
||||||
###
|
###
|
||||||
|
|
||||||
# Get one list
|
# Get one list
|
||||||
GET http://localhost:8080/api/v1/lists/1
|
GET http://localhost:8080/api/v1/lists/15
|
||||||
Authorization: Bearer {{auth_token}}
|
Authorization: Bearer {{auth_token}}
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
# Add a new list
|
# Add a new list
|
||||||
PUT http://localhost:8080/api/v1/namespaces/1/lists
|
PUT http://localhost:8080/api/v1/namespaces/6/lists
|
||||||
Authorization: Bearer {{auth_token}}
|
Authorization: Bearer {{auth_token}}
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"title": "sc",
|
"title": "sffffc me only"
|
||||||
"created": 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
# Add a new list
|
# Add a new item
|
||||||
PUT http://localhost:8080/api/v1/lists/1
|
PUT http://localhost:8080/api/v1/lists/14
|
||||||
Authorization: Bearer {{auth_token}}
|
Authorization: Bearer {{auth_token}}
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
|
@ -31,8 +30,8 @@ Content-Type: application/json
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
# Delete a list from a list
|
# Delete a task from a list
|
||||||
DELETE http://localhost:8080/api/v1/lists/28
|
DELETE http://localhost:8080/api/v1/lists/14
|
||||||
Authorization: Bearer {{auth_token}}
|
Authorization: Bearer {{auth_token}}
|
||||||
|
|
||||||
###
|
###
|
||||||
|
@ -116,7 +115,7 @@ GET http://localhost:8080/api/v1/tasks/caldav
|
||||||
###
|
###
|
||||||
|
|
||||||
# Update a task
|
# Update a task
|
||||||
POST http://localhost:8080/api/v1/tasks/27
|
POST http://localhost:8080/api/v1/tasks/30
|
||||||
Authorization: Bearer {{auth_token}}
|
Authorization: Bearer {{auth_token}}
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
# Architectural concepts
|
|
||||||
|
|
||||||
Vikunja was built with a maximum flexibility in mind while developing. To achive this, I built a set of easy-to-use
|
|
||||||
functions and respective web handlers, all represented through interfaces.
|
|
||||||
|
|
||||||
## CRUDable
|
|
||||||
|
|
||||||
This interface defines methods to Create/Read/ReadAll/Update/Delete something. In order to use the common web
|
|
||||||
handler, the struct must implement this and the `Rights` interface.
|
|
||||||
|
|
||||||
The interface is defined as followed:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type CRUDable interface {
|
|
||||||
Create(*User) error
|
|
||||||
ReadOne() error
|
|
||||||
ReadAll(string, *User, int) (interface{}, error)
|
|
||||||
Update() error
|
|
||||||
Delete() error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Each of these methods is called on an instance of a struct like so:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (l *List) ReadOne() (err error) {
|
|
||||||
*l, err = GetListByID(l.ID)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In that case, it takes the `ID` saved in the struct instance, gets the full list object and fills the original object with it.
|
|
||||||
(See parambinder to understand where that `ID` is coming from).
|
|
||||||
|
|
||||||
All functions should behave like this, if they create or update something, they should return the created/updated struct
|
|
||||||
instance. The only exception is `ReadAll()` which returns an interface. Usually this is an array, because, well you cannot
|
|
||||||
make an array of a set type (If you know a way to do this, don't hesitate to drop me a message).
|
|
||||||
|
|
||||||
### Pagination
|
|
||||||
|
|
||||||
When using the `ReadAll`-method, the third parameter contains the requested page. Your function should return only the number of results
|
|
||||||
corresponding to that page. The number of items per page is definied in the config as `service.pagecount` (Get it with `viper.GetInt("service.pagecount")`).
|
|
||||||
|
|
||||||
These can be calculated in combination with a helper function, `getLimitFromPageIndex(pageIndex)` which returns
|
|
||||||
SQL-needed `limit` (max-length) and `offset` parameters. You can feed this function directly into xorm's `Limit`-Function like so:
|
|
||||||
|
|
||||||
```go
|
|
||||||
lists := []List{}
|
|
||||||
err := x.Limit(getLimitFromPageIndex(pageIndex)).Find(&lists)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Search
|
|
||||||
|
|
||||||
When using the `ReadAll`-method, the first parameter is a search term which should be used to search items of your struct. You define the critera.
|
|
||||||
|
|
||||||
Users can then pass the `?s=something` parameter to the url to search.
|
|
||||||
|
|
||||||
As the logic for "give me everything" and "give me everything where the name contains 'something'" is mostly the same, we made the decision to design the function like this, in order to keep the places with mostly the same logic as few as possible. Also just adding `?s=query` to the url one already knows and uses is a lot more convenient.
|
|
||||||
|
|
||||||
## Rights
|
|
||||||
|
|
||||||
This interface defines methods to check for rights on structs. They accept a `User` as parameter and usually return a `bool`.
|
|
||||||
|
|
||||||
The interface is defined as followed:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Rights interface {
|
|
||||||
IsAdmin(*User) bool
|
|
||||||
CanWrite(*User) bool
|
|
||||||
CanRead(*User) bool
|
|
||||||
CanDelete(*User) bool
|
|
||||||
CanUpdate(*User) bool
|
|
||||||
CanCreate(*User) bool
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
When using the standard web handler, all methods except `CanRead()` are called before their `CRUD` counterparts. `CanRead()`
|
|
||||||
is called after `ReadOne()` was invoked as this would otherwise mean getting an object from the db to check if the user has the
|
|
||||||
right to see it and then getting it again if thats the case. Calling the function afterwards means we only have to get the
|
|
||||||
object once.
|
|
||||||
|
|
||||||
## Standard web handler
|
|
||||||
|
|
||||||
## Errors
|
|
||||||
|
|
||||||
Error types with their messages and http-codes are set in `models/error.go`. If the error type implements `HTTPError`, the server returns a user-friendly error message when this error occours. This means it returns a good HTTP status code, a message, and an error code. The error code should be unique across all error codes and can be used on the client to show a localized error message or do other stuff based on the exact error the server returns. That way the client won't have to "guess" that the error message remains the same over multiple versions of Vikunja.
|
|
||||||
|
|
||||||
An `HTTPError` is defined as follows:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type HTTPError struct {
|
|
||||||
HTTPCode int `json:"-"` // Can be any valid HTTP status code, I'd reccomend to use the constants of the http package.
|
|
||||||
Code int `json:"code"` // Must be a uniqe int identifier for this specific error. I'd reccomend defining a constant for this.
|
|
||||||
Message string `json:"message"` // A user-readable message what went wrong.
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
# How the binder works
|
|
||||||
|
|
||||||
The binder binds all values inside the url to their respective fields in a struct. Those fields need to have a tag
|
|
||||||
"param" with the name of the url placeholder which must be the same as in routes.
|
|
||||||
|
|
||||||
Whenever one of the standard CRUD methods is invoked, this binder is called, which enables one handler method
|
|
||||||
to handle all kinds of different urls with different parameters.
|
|
10
go.mod
10
go.mod
|
@ -18,6 +18,7 @@ module code.vikunja.io/api
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.30.0 // indirect
|
cloud.google.com/go v0.30.0 // indirect
|
||||||
|
code.vikunja.io/web v0.0.0-20181130221802-d23d2a4c1efb
|
||||||
github.com/BurntSushi/toml v0.3.1 // indirect
|
github.com/BurntSushi/toml v0.3.1 // indirect
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc
|
||||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf
|
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf
|
||||||
|
@ -40,13 +41,13 @@ require (
|
||||||
github.com/imdario/mergo v0.3.6
|
github.com/imdario/mergo v0.3.6
|
||||||
github.com/joho/godotenv v1.3.0 // indirect
|
github.com/joho/godotenv v1.3.0 // indirect
|
||||||
github.com/karalabe/xgo v0.0.0-20181007145344-72da7d1d3970
|
github.com/karalabe/xgo v0.0.0-20181007145344-72da7d1d3970
|
||||||
github.com/labstack/echo v0.0.0-20180911044237-1abaa3049251
|
github.com/labstack/echo v3.3.5+incompatible
|
||||||
github.com/labstack/gommon v0.0.0-20180312174116-6fe1405d73ec
|
github.com/labstack/gommon v0.2.8
|
||||||
github.com/lib/pq v1.0.0 // indirect
|
github.com/lib/pq v1.0.0 // indirect
|
||||||
github.com/mattn/go-oci8 v0.0.0-20181011085415-1a014d1384b5 // indirect
|
github.com/mattn/go-oci8 v0.0.0-20181011085415-1a014d1384b5 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.9.0
|
github.com/mattn/go-sqlite3 v1.9.0
|
||||||
github.com/mitchellh/mapstructure v1.1.2 // indirect
|
github.com/mitchellh/mapstructure v1.1.2 // indirect
|
||||||
github.com/op/go-logging v0.0.0-20160211212156-b2cb9fa56473
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||||
github.com/pkg/errors v0.8.0 // indirect
|
github.com/pkg/errors v0.8.0 // indirect
|
||||||
github.com/spf13/viper v1.2.0
|
github.com/spf13/viper v1.2.0
|
||||||
github.com/stretchr/testify v1.2.2
|
github.com/stretchr/testify v1.2.2
|
||||||
|
@ -55,9 +56,8 @@ require (
|
||||||
github.com/swaggo/gin-swagger v1.0.0 // indirect
|
github.com/swaggo/gin-swagger v1.0.0 // indirect
|
||||||
github.com/swaggo/swag v1.3.3-0.20181109030545-8f09470d62b2
|
github.com/swaggo/swag v1.3.3-0.20181109030545-8f09470d62b2
|
||||||
github.com/urfave/cli v1.20.0 // indirect
|
github.com/urfave/cli v1.20.0 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
|
||||||
github.com/ziutek/mymysql v1.5.4 // indirect
|
github.com/ziutek/mymysql v1.5.4 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20180312195533-182114d58262
|
golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3
|
||||||
golang.org/x/tools v0.0.0-20181026183834-f60e5f99f081 // indirect
|
golang.org/x/tools v0.0.0-20181026183834-f60e5f99f081 // indirect
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
|
|
28
go.sum
28
go.sum
|
@ -1,5 +1,7 @@
|
||||||
cloud.google.com/go v0.30.0 h1:xKvyLgk56d0nksWq49J0UyGEeUIicTl4+UBiX1NPX9g=
|
cloud.google.com/go v0.30.0 h1:xKvyLgk56d0nksWq49J0UyGEeUIicTl4+UBiX1NPX9g=
|
||||||
cloud.google.com/go v0.30.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.30.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
code.vikunja.io/web v0.0.0-20181130221802-d23d2a4c1efb h1:OyXe+z31V3xUUEOv/kVJmkfp/9y/YdlaKjfhGpDAmNQ=
|
||||||
|
code.vikunja.io/web v0.0.0-20181130221802-d23d2a4c1efb/go.mod h1:PmGEu9qI7nbEKDn38H0SWgCoGO4GLdbjdlnWSzFi2PA=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
|
github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
|
||||||
|
@ -12,7 +14,6 @@ github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzs
|
||||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||||
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/denisenkom/go-mssqldb v0.0.0-20180901172138-1eb28afdf9b6 h1:BZGp1dbKFjqlGmxEpwkDpCWNxVwEYnUPoncIzLiHlPo=
|
github.com/denisenkom/go-mssqldb v0.0.0-20180901172138-1eb28afdf9b6 h1:BZGp1dbKFjqlGmxEpwkDpCWNxVwEYnUPoncIzLiHlPo=
|
||||||
|
@ -60,10 +61,10 @@ github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/karalabe/xgo v0.0.0-20181007145344-72da7d1d3970 h1:0+1ZURVRim6FxA/jhWhJklsgoWc69q1sxlIu2Ztnhy0=
|
github.com/karalabe/xgo v0.0.0-20181007145344-72da7d1d3970 h1:0+1ZURVRim6FxA/jhWhJklsgoWc69q1sxlIu2Ztnhy0=
|
||||||
github.com/karalabe/xgo v0.0.0-20181007145344-72da7d1d3970/go.mod h1:iYGcTYIPUvEWhFo6aKUuLchs+AV4ssYdyuBbQJZGcBk=
|
github.com/karalabe/xgo v0.0.0-20181007145344-72da7d1d3970/go.mod h1:iYGcTYIPUvEWhFo6aKUuLchs+AV4ssYdyuBbQJZGcBk=
|
||||||
github.com/labstack/echo v0.0.0-20180911044237-1abaa3049251 h1:4q++nZ4OEtmbHazhA/7i3T9B+CBWtnHpuMMcW55ZjRk=
|
github.com/labstack/echo v3.3.5+incompatible h1:9PfxPUmasKzeJor9uQTaXLT6WUG/r+vSTmvXxvv3JO4=
|
||||||
github.com/labstack/echo v0.0.0-20180911044237-1abaa3049251/go.mod h1:rWD2DNQgFb1IY9lVYZVLWn2Ko4dyHZ/LpHORyBLP3hI=
|
github.com/labstack/echo v3.3.5+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
|
||||||
github.com/labstack/gommon v0.0.0-20180312174116-6fe1405d73ec h1:aYKwS4iCpqxskMuvI8+Byq0CxnnWHO/xuLk2pZJ96tY=
|
github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
|
||||||
github.com/labstack/gommon v0.0.0-20180312174116-6fe1405d73ec/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
|
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
|
||||||
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
|
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
|
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
|
||||||
|
@ -72,8 +73,8 @@ github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3
|
||||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
|
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-oci8 v0.0.0-20181011085415-1a014d1384b5 h1:+IPgoz43mdEYG5lrqNcjr3DQpAE38SqHtyx1IsqqQGM=
|
github.com/mattn/go-oci8 v0.0.0-20181011085415-1a014d1384b5 h1:+IPgoz43mdEYG5lrqNcjr3DQpAE38SqHtyx1IsqqQGM=
|
||||||
github.com/mattn/go-oci8 v0.0.0-20181011085415-1a014d1384b5/go.mod h1:/M9VLO+lUPmxvoOK2PfWRZ8mTtB4q1Hy9lEGijv9Nr8=
|
github.com/mattn/go-oci8 v0.0.0-20181011085415-1a014d1384b5/go.mod h1:/M9VLO+lUPmxvoOK2PfWRZ8mTtB4q1Hy9lEGijv9Nr8=
|
||||||
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
|
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
|
||||||
|
@ -82,8 +83,8 @@ github.com/mitchellh/mapstructure v1.0.0 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KH
|
||||||
github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/op/go-logging v0.0.0-20160211212156-b2cb9fa56473 h1:J1QZwDXgZ4dJD2s19iqR9+U00OWM2kDzbf1O/fmvCWg=
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
||||||
github.com/op/go-logging v0.0.0-20160211212156-b2cb9fa56473/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||||
|
@ -100,7 +101,6 @@ github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc=
|
||||||
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/spf13/viper v1.2.0 h1:M4Rzxlu+RgU4pyBRKhKaVN1VeYOm8h2jgyXnAseDgCc=
|
github.com/spf13/viper v1.2.0 h1:M4Rzxlu+RgU4pyBRKhKaVN1VeYOm8h2jgyXnAseDgCc=
|
||||||
github.com/spf13/viper v1.2.0/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI=
|
github.com/spf13/viper v1.2.0/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI=
|
||||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/swaggo/echo-swagger v0.0.0-20180315045949-97f46bb9e5a5 h1:yU0aDQpp0Dq4BAu8rrHnVdC6SZS0LceJVLCUCbGasbE=
|
github.com/swaggo/echo-swagger v0.0.0-20180315045949-97f46bb9e5a5 h1:yU0aDQpp0Dq4BAu8rrHnVdC6SZS0LceJVLCUCbGasbE=
|
||||||
|
@ -113,22 +113,22 @@ github.com/swaggo/swag v1.3.3-0.20181109030545-8f09470d62b2 h1:HMUGTfTJJZ2fRHar5
|
||||||
github.com/swaggo/swag v1.3.3-0.20181109030545-8f09470d62b2/go.mod h1:hog2WgeMOrQ/LvQ+o1YGTeT+vWVrbi0SiIslBtxKTyM=
|
github.com/swaggo/swag v1.3.3-0.20181109030545-8f09470d62b2/go.mod h1:hog2WgeMOrQ/LvQ+o1YGTeT+vWVrbi0SiIslBtxKTyM=
|
||||||
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
||||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
github.com/valyala/bytebufferpool v0.0.0-20160817181652-e746df99fe4a/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8=
|
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8=
|
||||||
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw=
|
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw=
|
||||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||||
golang.org/x/crypto v0.0.0-20180312195533-182114d58262 h1:1NLVUmR8SQ7cNNA5Vo7ronpXbR+5A+9IwIC/bLE7D8Y=
|
golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 h1:et7+NAX3lLIk5qUCTA9QelBjGE/NkhzYw/mhnr0s7nI=
|
||||||
golang.org/x/crypto v0.0.0-20180312195533-182114d58262/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 h1:x/bBzNauLQAlE3fLku/xy92Y8QwKX5HZymrMz2IiKFc=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 h1:x/bBzNauLQAlE3fLku/xy92Y8QwKX5HZymrMz2IiKFc=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58 h1:otZG8yDCO4LVps5+9bxOeNiCvgmOyt96J3roHTYs7oE=
|
golang.org/x/net v0.0.0-20181005035420-146acd28ed58 h1:otZG8yDCO4LVps5+9bxOeNiCvgmOyt96J3roHTYs7oE=
|
||||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/sys v0.0.0-20180312081825-c28acc882ebc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992 h1:BH3eQWeGbwRU2+wxxuuPOdFBmaiBH81O8BugSjHeTFg=
|
golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992 h1:BH3eQWeGbwRU2+wxxuuPOdFBmaiBH81O8BugSjHeTFg=
|
||||||
golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 h1:YAFjXN64LMvktoUZH9zgY4lGc/msGN7HQfoSuKCgaDU=
|
||||||
|
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/tools v0.0.0-20181026183834-f60e5f99f081 h1:QJP9sxq2/KbTxFnGduVryxJOt6r/UVGyom3tLaqu7tc=
|
golang.org/x/tools v0.0.0-20181026183834-f60e5f99f081 h1:QJP9sxq2/KbTxFnGduVryxJOt6r/UVGyom3tLaqu7tc=
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
// Vikunja is a todo-list application to facilitate your life.
|
|
||||||
// Copyright 2018 Vikunja and contributors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package models
|
|
||||||
|
|
||||||
// CRUDable defines the crud methods
|
|
||||||
type CRUDable interface {
|
|
||||||
Create(*User) error
|
|
||||||
ReadOne() error
|
|
||||||
ReadAll(string, *User, int) (interface{}, error)
|
|
||||||
Update() error
|
|
||||||
Delete() error
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Vikunja is a todo-list application to facilitate your life.
|
// Vikunja is a todo-list application to facilitate your life.
|
||||||
// Copyright 2018 Vikunja and contributors. All rights reserved.
|
// Copyright 2018 Vikunja and contributors. All web.Rights reserved.
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
@ -17,22 +17,11 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/web"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTPErrorProcessor is executed when the defined error is thrown, it will make sure the user sees an appropriate error message and http status code
|
|
||||||
type HTTPErrorProcessor interface {
|
|
||||||
HTTPError() HTTPError
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPError holds informations about an http error
|
|
||||||
type HTTPError struct {
|
|
||||||
HTTPCode int `json:"-"`
|
|
||||||
Code int `json:"code"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// =====================
|
// =====================
|
||||||
// User Operation Errors
|
// User Operation Errors
|
||||||
// =====================
|
// =====================
|
||||||
|
@ -57,8 +46,8 @@ func (err ErrUsernameExists) Error() string {
|
||||||
const ErrorCodeUsernameExists = 1001
|
const ErrorCodeUsernameExists = 1001
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrUsernameExists) HTTPError() HTTPError {
|
func (err ErrUsernameExists) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrorCodeUsernameExists, Message: "A user with this username already exists."}
|
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrorCodeUsernameExists, Message: "A user with this username already exists."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrUserEmailExists represents a "UserEmailExists" kind of error.
|
// ErrUserEmailExists represents a "UserEmailExists" kind of error.
|
||||||
|
@ -81,8 +70,8 @@ func (err ErrUserEmailExists) Error() string {
|
||||||
const ErrorCodeUserEmailExists = 1002
|
const ErrorCodeUserEmailExists = 1002
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrUserEmailExists) HTTPError() HTTPError {
|
func (err ErrUserEmailExists) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrorCodeUserEmailExists, Message: "A user with this email address already exists."}
|
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrorCodeUserEmailExists, Message: "A user with this email address already exists."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrNoUsernamePassword represents a "NoUsernamePassword" kind of error.
|
// ErrNoUsernamePassword represents a "NoUsernamePassword" kind of error.
|
||||||
|
@ -102,8 +91,8 @@ func (err ErrNoUsernamePassword) Error() string {
|
||||||
const ErrCodeNoUsernamePassword = 1004
|
const ErrCodeNoUsernamePassword = 1004
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrNoUsernamePassword) HTTPError() HTTPError {
|
func (err ErrNoUsernamePassword) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeNoUsernamePassword, Message: "Please specify a username and a password."}
|
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeNoUsernamePassword, Message: "Please specify a username and a password."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrUserDoesNotExist represents a "UserDoesNotExist" kind of error.
|
// ErrUserDoesNotExist represents a "UserDoesNotExist" kind of error.
|
||||||
|
@ -125,8 +114,8 @@ func (err ErrUserDoesNotExist) Error() string {
|
||||||
const ErrCodeUserDoesNotExist = 1005
|
const ErrCodeUserDoesNotExist = 1005
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrUserDoesNotExist) HTTPError() HTTPError {
|
func (err ErrUserDoesNotExist) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeUserDoesNotExist, Message: "The user does not exist."}
|
return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeUserDoesNotExist, Message: "The user does not exist."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrCouldNotGetUserID represents a "ErrCouldNotGetUserID" kind of error.
|
// ErrCouldNotGetUserID represents a "ErrCouldNotGetUserID" kind of error.
|
||||||
|
@ -146,8 +135,8 @@ func (err ErrCouldNotGetUserID) Error() string {
|
||||||
const ErrCodeCouldNotGetUserID = 1006
|
const ErrCodeCouldNotGetUserID = 1006
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrCouldNotGetUserID) HTTPError() HTTPError {
|
func (err ErrCouldNotGetUserID) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeCouldNotGetUserID, Message: "Could not get user id."}
|
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeCouldNotGetUserID, Message: "Could not get user id."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrNoPasswordResetToken represents an error where no password reset token exists for that user
|
// ErrNoPasswordResetToken represents an error where no password reset token exists for that user
|
||||||
|
@ -163,8 +152,8 @@ func (err ErrNoPasswordResetToken) Error() string {
|
||||||
const ErrCodeNoPasswordResetToken = 1008
|
const ErrCodeNoPasswordResetToken = 1008
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrNoPasswordResetToken) HTTPError() HTTPError {
|
func (err ErrNoPasswordResetToken) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeNoPasswordResetToken, Message: "No token to reset a user's password provided."}
|
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeNoPasswordResetToken, Message: "No token to reset a user's password provided."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrInvalidPasswordResetToken is an error where the password reset token is invalid
|
// ErrInvalidPasswordResetToken is an error where the password reset token is invalid
|
||||||
|
@ -180,8 +169,8 @@ func (err ErrInvalidPasswordResetToken) Error() string {
|
||||||
const ErrCodeInvalidPasswordResetToken = 1009
|
const ErrCodeInvalidPasswordResetToken = 1009
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrInvalidPasswordResetToken) HTTPError() HTTPError {
|
func (err ErrInvalidPasswordResetToken) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeInvalidPasswordResetToken, Message: "Invalid token to reset a user's password."}
|
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeInvalidPasswordResetToken, Message: "Invalid token to reset a user's password."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsErrInvalidPasswordResetToken checks if an error is a ErrInvalidPasswordResetToken.
|
// IsErrInvalidPasswordResetToken checks if an error is a ErrInvalidPasswordResetToken.
|
||||||
|
@ -203,8 +192,8 @@ func (err ErrInvalidEmailConfirmToken) Error() string {
|
||||||
const ErrCodeInvalidEmailConfirmToken = 1010
|
const ErrCodeInvalidEmailConfirmToken = 1010
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrInvalidEmailConfirmToken) HTTPError() HTTPError {
|
func (err ErrInvalidEmailConfirmToken) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeInvalidEmailConfirmToken, Message: "Invalid email confirm token."}
|
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeInvalidEmailConfirmToken, Message: "Invalid email confirm token."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsErrInvalidEmailConfirmToken checks if an error is a ErrInvalidEmailConfirmToken.
|
// IsErrInvalidEmailConfirmToken checks if an error is a ErrInvalidEmailConfirmToken.
|
||||||
|
@ -225,8 +214,8 @@ func (err ErrWrongUsernameOrPassword) Error() string {
|
||||||
const ErrCodeWrongUsernameOrPassword = 1011
|
const ErrCodeWrongUsernameOrPassword = 1011
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrWrongUsernameOrPassword) HTTPError() HTTPError {
|
func (err ErrWrongUsernameOrPassword) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeWrongUsernameOrPassword, Message: "Wrong username or password."}
|
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeWrongUsernameOrPassword, Message: "Wrong username or password."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsErrWrongUsernameOrPassword checks if an error is a IsErrEmailNotConfirmed.
|
// IsErrWrongUsernameOrPassword checks if an error is a IsErrEmailNotConfirmed.
|
||||||
|
@ -248,8 +237,8 @@ func (err ErrEmailNotConfirmed) Error() string {
|
||||||
const ErrCodeEmailNotConfirmed = 1012
|
const ErrCodeEmailNotConfirmed = 1012
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrEmailNotConfirmed) HTTPError() HTTPError {
|
func (err ErrEmailNotConfirmed) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeEmailNotConfirmed, Message: "Please confirm your email address."}
|
return web.HTTPError{HTTPCode: http.StatusPreconditionFailed, Code: ErrCodeEmailNotConfirmed, Message: "Please confirm your email address."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsErrEmailNotConfirmed checks if an error is a IsErrEmailNotConfirmed.
|
// IsErrEmailNotConfirmed checks if an error is a IsErrEmailNotConfirmed.
|
||||||
|
@ -279,8 +268,8 @@ func (err ErrIDCannotBeZero) Error() string {
|
||||||
const ErrCodeIDCannotBeZero = 2001
|
const ErrCodeIDCannotBeZero = 2001
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrIDCannotBeZero) HTTPError() HTTPError {
|
func (err ErrIDCannotBeZero) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeIDCannotBeZero, Message: "The ID cannot be empty or 0."}
|
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeIDCannotBeZero, Message: "The ID cannot be empty or 0."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrInvalidData represents a "ErrInvalidData" kind of error. Used when a struct is invalid -> validation failed.
|
// ErrInvalidData represents a "ErrInvalidData" kind of error. Used when a struct is invalid -> validation failed.
|
||||||
|
@ -302,13 +291,13 @@ func (err ErrInvalidData) Error() string {
|
||||||
const ErrCodeInvalidData = 2002
|
const ErrCodeInvalidData = 2002
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrInvalidData) HTTPError() HTTPError {
|
func (err ErrInvalidData) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidData, Message: err.Message}
|
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidData, Message: err.Message}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidationHTTPError is the http error when a validation fails
|
// ValidationHTTPError is the http error when a validation fails
|
||||||
type ValidationHTTPError struct {
|
type ValidationHTTPError struct {
|
||||||
HTTPError
|
web.HTTPError
|
||||||
InvalidFields []string `json:"invalid_fields"`
|
InvalidFields []string `json:"invalid_fields"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,8 +332,8 @@ func (err ErrListDoesNotExist) Error() string {
|
||||||
const ErrCodeListDoesNotExist = 3001
|
const ErrCodeListDoesNotExist = 3001
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrListDoesNotExist) HTTPError() HTTPError {
|
func (err ErrListDoesNotExist) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeListDoesNotExist, Message: "This list does not exist."}
|
return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeListDoesNotExist, Message: "This list does not exist."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrNeedToHaveListReadAccess represents an error, where the user dont has read access to that List
|
// ErrNeedToHaveListReadAccess represents an error, where the user dont has read access to that List
|
||||||
|
@ -367,8 +356,8 @@ func (err ErrNeedToHaveListReadAccess) Error() string {
|
||||||
const ErrCodeNeedToHaveListReadAccess = 3004
|
const ErrCodeNeedToHaveListReadAccess = 3004
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrNeedToHaveListReadAccess) HTTPError() HTTPError {
|
func (err ErrNeedToHaveListReadAccess) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeNeedToHaveListReadAccess, Message: "You need to have read access to this list."}
|
return web.HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeNeedToHaveListReadAccess, Message: "You need to have read access to this list."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrListTitleCannotBeEmpty represents a "ErrListTitleCannotBeEmpty" kind of error. Used if the list does not exist.
|
// ErrListTitleCannotBeEmpty represents a "ErrListTitleCannotBeEmpty" kind of error. Used if the list does not exist.
|
||||||
|
@ -388,8 +377,8 @@ func (err ErrListTitleCannotBeEmpty) Error() string {
|
||||||
const ErrCodeListTitleCannotBeEmpty = 3005
|
const ErrCodeListTitleCannotBeEmpty = 3005
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrListTitleCannotBeEmpty) HTTPError() HTTPError {
|
func (err ErrListTitleCannotBeEmpty) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeListTitleCannotBeEmpty, Message: "You must provide at least a list title."}
|
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeListTitleCannotBeEmpty, Message: "You must provide at least a list title."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================
|
// ================
|
||||||
|
@ -413,8 +402,8 @@ func (err ErrListTaskCannotBeEmpty) Error() string {
|
||||||
const ErrCodeListTaskCannotBeEmpty = 4001
|
const ErrCodeListTaskCannotBeEmpty = 4001
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrListTaskCannotBeEmpty) HTTPError() HTTPError {
|
func (err ErrListTaskCannotBeEmpty) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeListTaskCannotBeEmpty, Message: "You must provide at least a list task text."}
|
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeListTaskCannotBeEmpty, Message: "You must provide at least a list task text."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrListTaskDoesNotExist represents a "ErrListDoesNotExist" kind of error. Used if the list does not exist.
|
// ErrListTaskDoesNotExist represents a "ErrListDoesNotExist" kind of error. Used if the list does not exist.
|
||||||
|
@ -436,8 +425,8 @@ func (err ErrListTaskDoesNotExist) Error() string {
|
||||||
const ErrCodeListTaskDoesNotExist = 4002
|
const ErrCodeListTaskDoesNotExist = 4002
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrListTaskDoesNotExist) HTTPError() HTTPError {
|
func (err ErrListTaskDoesNotExist) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeListTaskDoesNotExist, Message: "This list task does not exist"}
|
return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeListTaskDoesNotExist, Message: "This list task does not exist"}
|
||||||
}
|
}
|
||||||
|
|
||||||
// =================
|
// =================
|
||||||
|
@ -463,8 +452,8 @@ func (err ErrNamespaceDoesNotExist) Error() string {
|
||||||
const ErrCodeNamespaceDoesNotExist = 5001
|
const ErrCodeNamespaceDoesNotExist = 5001
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrNamespaceDoesNotExist) HTTPError() HTTPError {
|
func (err ErrNamespaceDoesNotExist) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeNamespaceDoesNotExist, Message: "Namespace not found."}
|
return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeNamespaceDoesNotExist, Message: "Namespace not found."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrUserDoesNotHaveAccessToNamespace represents an error, where the user is not the owner of that namespace (used i.e. when deleting a namespace)
|
// ErrUserDoesNotHaveAccessToNamespace represents an error, where the user is not the owner of that namespace (used i.e. when deleting a namespace)
|
||||||
|
@ -487,8 +476,8 @@ func (err ErrUserDoesNotHaveAccessToNamespace) Error() string {
|
||||||
const ErrCodeUserDoesNotHaveAccessToNamespace = 5003
|
const ErrCodeUserDoesNotHaveAccessToNamespace = 5003
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrUserDoesNotHaveAccessToNamespace) HTTPError() HTTPError {
|
func (err ErrUserDoesNotHaveAccessToNamespace) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeUserDoesNotHaveAccessToNamespace, Message: "This user does not have access to the namespace."}
|
return web.HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeUserDoesNotHaveAccessToNamespace, Message: "This user does not have access to the namespace."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrNamespaceNameCannotBeEmpty represents an error, where a namespace name is empty.
|
// ErrNamespaceNameCannotBeEmpty represents an error, where a namespace name is empty.
|
||||||
|
@ -511,8 +500,8 @@ func (err ErrNamespaceNameCannotBeEmpty) Error() string {
|
||||||
const ErrCodeNamespaceNameCannotBeEmpty = 5006
|
const ErrCodeNamespaceNameCannotBeEmpty = 5006
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrNamespaceNameCannotBeEmpty) HTTPError() HTTPError {
|
func (err ErrNamespaceNameCannotBeEmpty) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeNamespaceNameCannotBeEmpty, Message: "The namespace name cannot be empty."}
|
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeNamespaceNameCannotBeEmpty, Message: "The namespace name cannot be empty."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrNeedToHaveNamespaceReadAccess represents an error, where the user is not the owner of that namespace (used i.e. when deleting a namespace)
|
// ErrNeedToHaveNamespaceReadAccess represents an error, where the user is not the owner of that namespace (used i.e. when deleting a namespace)
|
||||||
|
@ -535,8 +524,8 @@ func (err ErrNeedToHaveNamespaceReadAccess) Error() string {
|
||||||
const ErrCodeNeedToHaveNamespaceReadAccess = 5009
|
const ErrCodeNeedToHaveNamespaceReadAccess = 5009
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrNeedToHaveNamespaceReadAccess) HTTPError() HTTPError {
|
func (err ErrNeedToHaveNamespaceReadAccess) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeNeedToHaveNamespaceReadAccess, Message: "You need to have namespace read access to do this."}
|
return web.HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeNeedToHaveNamespaceReadAccess, Message: "You need to have namespace read access to do this."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrTeamDoesNotHaveAccessToNamespace represents an error, where the Team is not the owner of that namespace (used i.e. when deleting a namespace)
|
// ErrTeamDoesNotHaveAccessToNamespace represents an error, where the Team is not the owner of that namespace (used i.e. when deleting a namespace)
|
||||||
|
@ -559,8 +548,8 @@ func (err ErrTeamDoesNotHaveAccessToNamespace) Error() string {
|
||||||
const ErrCodeTeamDoesNotHaveAccessToNamespace = 5010
|
const ErrCodeTeamDoesNotHaveAccessToNamespace = 5010
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrTeamDoesNotHaveAccessToNamespace) HTTPError() HTTPError {
|
func (err ErrTeamDoesNotHaveAccessToNamespace) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeTeamDoesNotHaveAccessToNamespace, Message: "You need to have access to this namespace to do this."}
|
return web.HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeTeamDoesNotHaveAccessToNamespace, Message: "You need to have access to this namespace to do this."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrUserAlreadyHasNamespaceAccess represents an error where a user already has access to a namespace
|
// ErrUserAlreadyHasNamespaceAccess represents an error where a user already has access to a namespace
|
||||||
|
@ -583,8 +572,8 @@ func (err ErrUserAlreadyHasNamespaceAccess) Error() string {
|
||||||
const ErrCodeUserAlreadyHasNamespaceAccess = 5011
|
const ErrCodeUserAlreadyHasNamespaceAccess = 5011
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrUserAlreadyHasNamespaceAccess) HTTPError() HTTPError {
|
func (err ErrUserAlreadyHasNamespaceAccess) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeUserAlreadyHasNamespaceAccess, Message: "This user already has access to this namespace."}
|
return web.HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeUserAlreadyHasNamespaceAccess, Message: "This user already has access to this namespace."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============
|
// ============
|
||||||
|
@ -610,8 +599,8 @@ func (err ErrTeamNameCannotBeEmpty) Error() string {
|
||||||
const ErrCodeTeamNameCannotBeEmpty = 6001
|
const ErrCodeTeamNameCannotBeEmpty = 6001
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrTeamNameCannotBeEmpty) HTTPError() HTTPError {
|
func (err ErrTeamNameCannotBeEmpty) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeTeamNameCannotBeEmpty, Message: "The team name cannot be empty"}
|
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeTeamNameCannotBeEmpty, Message: "The team name cannot be empty"}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrTeamDoesNotExist represents an error where a team does not exist
|
// ErrTeamDoesNotExist represents an error where a team does not exist
|
||||||
|
@ -633,8 +622,8 @@ func (err ErrTeamDoesNotExist) Error() string {
|
||||||
const ErrCodeTeamDoesNotExist = 6002
|
const ErrCodeTeamDoesNotExist = 6002
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrTeamDoesNotExist) HTTPError() HTTPError {
|
func (err ErrTeamDoesNotExist) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeTeamDoesNotExist, Message: "This team does not exist."}
|
return web.HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeTeamDoesNotExist, Message: "This team does not exist."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrInvalidTeamRight represents an error where a team right is invalid
|
// ErrInvalidTeamRight represents an error where a team right is invalid
|
||||||
|
@ -656,8 +645,8 @@ func (err ErrInvalidTeamRight) Error() string {
|
||||||
const ErrCodeInvalidTeamRight = 6003
|
const ErrCodeInvalidTeamRight = 6003
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrInvalidTeamRight) HTTPError() HTTPError {
|
func (err ErrInvalidTeamRight) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidTeamRight, Message: "The team right is invalid."}
|
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidTeamRight, Message: "The team right is invalid."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrTeamAlreadyHasAccess represents an error where a team already has access to a list/namespace
|
// ErrTeamAlreadyHasAccess represents an error where a team already has access to a list/namespace
|
||||||
|
@ -680,8 +669,8 @@ func (err ErrTeamAlreadyHasAccess) Error() string {
|
||||||
const ErrCodeTeamAlreadyHasAccess = 6004
|
const ErrCodeTeamAlreadyHasAccess = 6004
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrTeamAlreadyHasAccess) HTTPError() HTTPError {
|
func (err ErrTeamAlreadyHasAccess) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeTeamAlreadyHasAccess, Message: "This team already has access."}
|
return web.HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeTeamAlreadyHasAccess, Message: "This team already has access."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrUserIsMemberOfTeam represents an error where a user is already member of a team.
|
// ErrUserIsMemberOfTeam represents an error where a user is already member of a team.
|
||||||
|
@ -704,8 +693,8 @@ func (err ErrUserIsMemberOfTeam) Error() string {
|
||||||
const ErrCodeUserIsMemberOfTeam = 6005
|
const ErrCodeUserIsMemberOfTeam = 6005
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrUserIsMemberOfTeam) HTTPError() HTTPError {
|
func (err ErrUserIsMemberOfTeam) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeUserIsMemberOfTeam, Message: "This user is already a member of that team."}
|
return web.HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeUserIsMemberOfTeam, Message: "This user is already a member of that team."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrCannotDeleteLastTeamMember represents an error where a user wants to delete the last member of a team (probably himself)
|
// ErrCannotDeleteLastTeamMember represents an error where a user wants to delete the last member of a team (probably himself)
|
||||||
|
@ -728,8 +717,8 @@ func (err ErrCannotDeleteLastTeamMember) Error() string {
|
||||||
const ErrCodeCannotDeleteLastTeamMember = 6006
|
const ErrCodeCannotDeleteLastTeamMember = 6006
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrCannotDeleteLastTeamMember) HTTPError() HTTPError {
|
func (err ErrCannotDeleteLastTeamMember) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeCannotDeleteLastTeamMember, Message: "You cannot delete the last member of a team."}
|
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeCannotDeleteLastTeamMember, Message: "You cannot delete the last member of a team."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrTeamDoesNotHaveAccessToList represents an error, where the Team is not the owner of that List (used i.e. when deleting a List)
|
// ErrTeamDoesNotHaveAccessToList represents an error, where the Team is not the owner of that List (used i.e. when deleting a List)
|
||||||
|
@ -752,8 +741,8 @@ func (err ErrTeamDoesNotHaveAccessToList) Error() string {
|
||||||
const ErrCodeTeamDoesNotHaveAccessToList = 6007
|
const ErrCodeTeamDoesNotHaveAccessToList = 6007
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrTeamDoesNotHaveAccessToList) HTTPError() HTTPError {
|
func (err ErrTeamDoesNotHaveAccessToList) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeTeamDoesNotHaveAccessToList, Message: "This team does not have access to the list."}
|
return web.HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeTeamDoesNotHaveAccessToList, Message: "This team does not have access to the list."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====================
|
// ====================
|
||||||
|
@ -779,8 +768,8 @@ func (err ErrInvalidUserRight) Error() string {
|
||||||
const ErrCodeInvalidUserRight = 7001
|
const ErrCodeInvalidUserRight = 7001
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrInvalidUserRight) HTTPError() HTTPError {
|
func (err ErrInvalidUserRight) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidUserRight, Message: "The user right is invalid."}
|
return web.HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidUserRight, Message: "The user right is invalid."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrUserAlreadyHasAccess represents an error where a user already has access to a list/namespace
|
// ErrUserAlreadyHasAccess represents an error where a user already has access to a list/namespace
|
||||||
|
@ -803,8 +792,8 @@ func (err ErrUserAlreadyHasAccess) Error() string {
|
||||||
const ErrCodeUserAlreadyHasAccess = 7002
|
const ErrCodeUserAlreadyHasAccess = 7002
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrUserAlreadyHasAccess) HTTPError() HTTPError {
|
func (err ErrUserAlreadyHasAccess) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeUserAlreadyHasAccess, Message: "This user already has access to this list."}
|
return web.HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeUserAlreadyHasAccess, Message: "This user already has access to this list."}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrUserDoesNotHaveAccessToList represents an error, where the user is not the owner of that List (used i.e. when deleting a List)
|
// ErrUserDoesNotHaveAccessToList represents an error, where the user is not the owner of that List (used i.e. when deleting a List)
|
||||||
|
@ -827,6 +816,6 @@ func (err ErrUserDoesNotHaveAccessToList) Error() string {
|
||||||
const ErrCodeUserDoesNotHaveAccessToList = 7003
|
const ErrCodeUserDoesNotHaveAccessToList = 7003
|
||||||
|
|
||||||
// HTTPError holds the http error description
|
// HTTPError holds the http error description
|
||||||
func (err ErrUserDoesNotHaveAccessToList) HTTPError() HTTPError {
|
func (err ErrUserDoesNotHaveAccessToList) HTTPError() web.HTTPError {
|
||||||
return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeUserDoesNotHaveAccessToList, Message: "This user does not have access to the list."}
|
return web.HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeUserDoesNotHaveAccessToList, Message: "This user does not have access to the list."}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,10 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import "sort"
|
import (
|
||||||
|
"code.vikunja.io/web"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
// List represents a list of tasks
|
// List represents a list of tasks
|
||||||
type List struct {
|
type List struct {
|
||||||
|
@ -32,8 +35,8 @@ type List struct {
|
||||||
Created int64 `xorm:"created" json:"created"`
|
Created int64 `xorm:"created" json:"created"`
|
||||||
Updated int64 `xorm:"updated" json:"updated"`
|
Updated int64 `xorm:"updated" json:"updated"`
|
||||||
|
|
||||||
CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetListsByNamespaceID gets all lists in a namespace
|
// GetListsByNamespaceID gets all lists in a namespace
|
||||||
|
@ -55,7 +58,12 @@ func GetListsByNamespaceID(nID int64) (lists []*List, err error) {
|
||||||
// @Failure 403 {object} models.HTTPError "The user does not have access to the list"
|
// @Failure 403 {object} models.HTTPError "The user does not have access to the list"
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /lists [get]
|
// @Router /lists [get]
|
||||||
func (l *List) ReadAll(search string, u *User, page int) (interface{}, error) {
|
func (l *List) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||||
|
u, err := getUserWithError(a)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
lists, err := getRawListsForUser(search, u, page)
|
lists, err := getRawListsForUser(search, u, page)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -206,7 +214,12 @@ func AddListDetails(lists []*List) (err error) {
|
||||||
// @Success 200 {array} models.List "The tasks"
|
// @Success 200 {array} models.List "The tasks"
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /tasks [get]
|
// @Router /tasks [get]
|
||||||
func (lt *ListTask) ReadAll(search string, u *User, page int) (interface{}, error) {
|
func (lt *ListTask) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||||
|
u, err := getUserWithError(a)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return GetTasksByUser(search, u, page)
|
return GetTasksByUser(search, u, page)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "code.vikunja.io/web"
|
||||||
|
|
||||||
// CreateOrUpdateList updates a list or creates it if it doesn't exist
|
// CreateOrUpdateList updates a list or creates it if it doesn't exist
|
||||||
func CreateOrUpdateList(list *List) (err error) {
|
func CreateOrUpdateList(list *List) (err error) {
|
||||||
|
|
||||||
|
@ -84,7 +86,12 @@ func (l *List) Update() (err error) {
|
||||||
// @Failure 403 {object} models.HTTPError "The user does not have access to the list"
|
// @Failure 403 {object} models.HTTPError "The user does not have access to the list"
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /namespaces/{namespaceID}/lists [put]
|
// @Router /namespaces/{namespaceID}/lists [put]
|
||||||
func (l *List) Create(doer *User) (err error) {
|
func (l *List) Create(a web.Auth) (err error) {
|
||||||
|
doer, err := getUserWithError(a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Check rights
|
// Check rights
|
||||||
u, err := GetUserByID(doer.ID)
|
u, err := GetUserByID(doer.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -18,10 +18,13 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
|
"code.vikunja.io/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsAdmin returns whether the user has admin rights on the list or not
|
// IsAdmin returns whether the user has admin rights on the list or not
|
||||||
func (l *List) IsAdmin(u *User) bool {
|
func (l *List) IsAdmin(a web.Auth) bool {
|
||||||
|
u := getUserForRights(a)
|
||||||
|
|
||||||
// Owners are always admins
|
// Owners are always admins
|
||||||
if l.Owner.ID == u.ID {
|
if l.Owner.ID == u.ID {
|
||||||
return true
|
return true
|
||||||
|
@ -36,7 +39,9 @@ func (l *List) IsAdmin(u *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanWrite return whether the user can write on that list or not
|
// CanWrite return whether the user can write on that list or not
|
||||||
func (l *List) CanWrite(user *User) bool {
|
func (l *List) CanWrite(a web.Auth) bool {
|
||||||
|
user := getUserForRights(a)
|
||||||
|
|
||||||
// Admins always have write access
|
// Admins always have write access
|
||||||
if l.IsAdmin(user) {
|
if l.IsAdmin(user) {
|
||||||
return true
|
return true
|
||||||
|
@ -51,7 +56,9 @@ func (l *List) CanWrite(user *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanRead checks if a user has read access to a list
|
// CanRead checks if a user has read access to a list
|
||||||
func (l *List) CanRead(user *User) bool {
|
func (l *List) CanRead(a web.Auth) bool {
|
||||||
|
user := getUserForRights(a)
|
||||||
|
|
||||||
// Admins always have read access
|
// Admins always have read access
|
||||||
if l.IsAdmin(user) {
|
if l.IsAdmin(user) {
|
||||||
return true
|
return true
|
||||||
|
@ -66,7 +73,9 @@ func (l *List) CanRead(user *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanDelete checks if the user can delete a list
|
// CanDelete checks if the user can delete a list
|
||||||
func (l *List) CanDelete(doer *User) bool {
|
func (l *List) CanDelete(a web.Auth) bool {
|
||||||
|
doer := getUserForRights(a)
|
||||||
|
|
||||||
if err := l.GetSimpleByID(); err != nil {
|
if err := l.GetSimpleByID(); err != nil {
|
||||||
log.Log.Error("Error occurred during CanDelete for List: %s", err)
|
log.Log.Error("Error occurred during CanDelete for List: %s", err)
|
||||||
return false
|
return false
|
||||||
|
@ -75,7 +84,9 @@ func (l *List) CanDelete(doer *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanUpdate checks if the user can update a list
|
// CanUpdate checks if the user can update a list
|
||||||
func (l *List) CanUpdate(doer *User) bool {
|
func (l *List) CanUpdate(a web.Auth) bool {
|
||||||
|
doer := getUserForRights(a)
|
||||||
|
|
||||||
if err := l.GetSimpleByID(); err != nil {
|
if err := l.GetSimpleByID(); err != nil {
|
||||||
log.Log.Error("Error occurred during CanUpdate for List: %s", err)
|
log.Log.Error("Error occurred during CanUpdate for List: %s", err)
|
||||||
return false
|
return false
|
||||||
|
@ -84,10 +95,10 @@ func (l *List) CanUpdate(doer *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanCreate checks if the user can update a list
|
// CanCreate checks if the user can update a list
|
||||||
func (l *List) CanCreate(doer *User) bool {
|
func (l *List) CanCreate(a web.Auth) bool {
|
||||||
// A user can create a list if he has write access to the namespace
|
// A user can create a list if he has write access to the namespace
|
||||||
n, _ := GetNamespaceByID(l.NamespaceID)
|
n, _ := GetNamespaceByID(l.NamespaceID)
|
||||||
return n.CanWrite(doer)
|
return n.CanWrite(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *List) checkListTeamRight(user *User, r TeamRight) bool {
|
func (l *List) checkListTeamRight(user *User, r TeamRight) bool {
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "code.vikunja.io/web"
|
||||||
|
|
||||||
// ListTask represents an task in a todolist
|
// ListTask represents an task in a todolist
|
||||||
type ListTask struct {
|
type ListTask struct {
|
||||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"listtask"`
|
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"listtask"`
|
||||||
|
@ -33,8 +35,8 @@ type ListTask struct {
|
||||||
|
|
||||||
CreatedBy User `xorm:"-" json:"createdBy" valid:"-"`
|
CreatedBy User `xorm:"-" json:"createdBy" valid:"-"`
|
||||||
|
|
||||||
CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName returns the table name for listtasks
|
// TableName returns the table name for listtasks
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/web"
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,7 +35,12 @@ import (
|
||||||
// @Failure 403 {object} models.HTTPError "The user does not have access to the list"
|
// @Failure 403 {object} models.HTTPError "The user does not have access to the list"
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /lists/{id} [put]
|
// @Router /lists/{id} [put]
|
||||||
func (i *ListTask) Create(doer *User) (err error) {
|
func (i *ListTask) Create(a web.Auth) (err error) {
|
||||||
|
doer, err := getUserWithError(a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
i.ID = 0
|
i.ID = 0
|
||||||
|
|
||||||
// Check if we have at least a text
|
// Check if we have at least a text
|
||||||
|
|
|
@ -18,10 +18,13 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
|
"code.vikunja.io/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CanDelete checks if the user can delete an task
|
// CanDelete checks if the user can delete an task
|
||||||
func (i *ListTask) CanDelete(doer *User) bool {
|
func (i *ListTask) CanDelete(a web.Auth) bool {
|
||||||
|
doer := getUserForRights(a)
|
||||||
|
|
||||||
// Get the task
|
// Get the task
|
||||||
lI, err := GetListTaskByID(i.ID)
|
lI, err := GetListTaskByID(i.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -36,7 +39,9 @@ func (i *ListTask) CanDelete(doer *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanUpdate determines if a user has the right to update a list task
|
// CanUpdate determines if a user has the right to update a list task
|
||||||
func (i *ListTask) CanUpdate(doer *User) bool {
|
func (i *ListTask) CanUpdate(a web.Auth) bool {
|
||||||
|
doer := getUserForRights(a)
|
||||||
|
|
||||||
// Get the task
|
// Get the task
|
||||||
lI, err := GetListTaskByID(i.ID)
|
lI, err := GetListTaskByID(i.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -51,7 +56,9 @@ func (i *ListTask) CanUpdate(doer *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanCreate determines if a user has the right to create a list task
|
// CanCreate determines if a user has the right to create a list task
|
||||||
func (i *ListTask) CanCreate(doer *User) bool {
|
func (i *ListTask) CanCreate(a web.Auth) bool {
|
||||||
|
doer := getUserForRights(a)
|
||||||
|
|
||||||
// A user can create an task if he has write acces to its list
|
// A user can create an task if he has write acces to its list
|
||||||
l := &List{ID: i.ListID}
|
l := &List{ID: i.ListID}
|
||||||
l.ReadOne()
|
l.ReadOne()
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "code.vikunja.io/web"
|
||||||
|
|
||||||
// ListUser represents a list <-> user relation
|
// ListUser represents a list <-> user relation
|
||||||
type ListUser struct {
|
type ListUser struct {
|
||||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"namespace"`
|
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"namespace"`
|
||||||
|
@ -26,8 +28,8 @@ type ListUser struct {
|
||||||
Created int64 `xorm:"created" json:"created"`
|
Created int64 `xorm:"created" json:"created"`
|
||||||
Updated int64 `xorm:"updated" json:"updated"`
|
Updated int64 `xorm:"updated" json:"updated"`
|
||||||
|
|
||||||
CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName is the table name for ListUser
|
// TableName is the table name for ListUser
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "code.vikunja.io/web"
|
||||||
|
|
||||||
// Create creates a new list <-> user relation
|
// Create creates a new list <-> user relation
|
||||||
// @Summary Add a user to a list
|
// @Summary Add a user to a list
|
||||||
// @Description Gives a user access to a list.
|
// @Description Gives a user access to a list.
|
||||||
|
@ -31,7 +33,7 @@ package models
|
||||||
// @Failure 403 {object} models.HTTPError "The user does not have access to the list"
|
// @Failure 403 {object} models.HTTPError "The user does not have access to the list"
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /lists/{id}/users [put]
|
// @Router /lists/{id}/users [put]
|
||||||
func (ul *ListUser) Create(u *User) (err error) {
|
func (ul *ListUser) Create(a web.Auth) (err error) {
|
||||||
|
|
||||||
// Check if the right is valid
|
// Check if the right is valid
|
||||||
if err := ul.Right.isValid(); err != nil {
|
if err := ul.Right.isValid(); err != nil {
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "code.vikunja.io/web"
|
||||||
|
|
||||||
// ReadAll gets all users who have access to a list
|
// ReadAll gets all users who have access to a list
|
||||||
// @Summary Get users on a list
|
// @Summary Get users on a list
|
||||||
// @Description Returns a list with all users which have access on a given list.
|
// @Description Returns a list with all users which have access on a given list.
|
||||||
|
@ -30,7 +32,12 @@ package models
|
||||||
// @Failure 403 {object} models.HTTPError "No right to see the list."
|
// @Failure 403 {object} models.HTTPError "No right to see the list."
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /lists/{id}/users [get]
|
// @Router /lists/{id}/users [get]
|
||||||
func (ul *ListUser) ReadAll(search string, u *User, page int) (interface{}, error) {
|
func (ul *ListUser) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||||
|
u, err := getUserWithError(a)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the user has access to the list
|
// Check if the user has access to the list
|
||||||
l := &List{ID: ul.ListID}
|
l := &List{ID: ul.ListID}
|
||||||
if err := l.GetSimpleByID(); err != nil {
|
if err := l.GetSimpleByID(); err != nil {
|
||||||
|
@ -42,7 +49,7 @@ func (ul *ListUser) ReadAll(search string, u *User, page int) (interface{}, erro
|
||||||
|
|
||||||
// Get all users
|
// Get all users
|
||||||
all := []*UserWithRight{}
|
all := []*UserWithRight{}
|
||||||
err := x.
|
err = x.
|
||||||
Join("INNER", "users_list", "user_id = users.id").
|
Join("INNER", "users_list", "user_id = users.id").
|
||||||
Where("users_list.list_id = ?", ul.ListID).
|
Where("users_list.list_id = ?", ul.ListID).
|
||||||
Limit(getLimitFromPageIndex(page)).
|
Limit(getLimitFromPageIndex(page)).
|
||||||
|
|
|
@ -18,10 +18,13 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
|
"code.vikunja.io/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CanCreate checks if the user can create a new user <-> list relation
|
// CanCreate checks if the user can create a new user <-> list relation
|
||||||
func (lu *ListUser) CanCreate(doer *User) bool {
|
func (lu *ListUser) CanCreate(a web.Auth) bool {
|
||||||
|
doer := getUserForRights(a)
|
||||||
|
|
||||||
// Get the list and check if the user has write access on it
|
// Get the list and check if the user has write access on it
|
||||||
l := List{ID: lu.ListID}
|
l := List{ID: lu.ListID}
|
||||||
if err := l.GetSimpleByID(); err != nil {
|
if err := l.GetSimpleByID(); err != nil {
|
||||||
|
@ -32,7 +35,9 @@ func (lu *ListUser) CanCreate(doer *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanDelete checks if the user can delete a user <-> list relation
|
// CanDelete checks if the user can delete a user <-> list relation
|
||||||
func (lu *ListUser) CanDelete(doer *User) bool {
|
func (lu *ListUser) CanDelete(a web.Auth) bool {
|
||||||
|
doer := getUserForRights(a)
|
||||||
|
|
||||||
// Get the list and check if the user has write access on it
|
// Get the list and check if the user has write access on it
|
||||||
l := List{ID: lu.ListID}
|
l := List{ID: lu.ListID}
|
||||||
if err := l.GetSimpleByID(); err != nil {
|
if err := l.GetSimpleByID(); err != nil {
|
||||||
|
@ -43,7 +48,9 @@ func (lu *ListUser) CanDelete(doer *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanUpdate checks if the user can update a user <-> list relation
|
// CanUpdate checks if the user can update a user <-> list relation
|
||||||
func (lu *ListUser) CanUpdate(doer *User) bool {
|
func (lu *ListUser) CanUpdate(a web.Auth) bool {
|
||||||
|
doer := getUserForRights(a)
|
||||||
|
|
||||||
// Get the list and check if the user has write access on it
|
// Get the list and check if the user has write access on it
|
||||||
l := List{ID: lu.ListID}
|
l := List{ID: lu.ListID}
|
||||||
if err := l.GetSimpleByID(); err != nil {
|
if err := l.GetSimpleByID(); err != nil {
|
||||||
|
|
|
@ -16,7 +16,10 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"code.vikunja.io/web"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
// Namespace holds informations about a namespace
|
// Namespace holds informations about a namespace
|
||||||
type Namespace struct {
|
type Namespace struct {
|
||||||
|
@ -30,8 +33,8 @@ type Namespace struct {
|
||||||
Created int64 `xorm:"created" json:"created"`
|
Created int64 `xorm:"created" json:"created"`
|
||||||
Updated int64 `xorm:"updated" json:"updated"`
|
Updated int64 `xorm:"updated" json:"updated"`
|
||||||
|
|
||||||
CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName makes beautiful table names
|
// TableName makes beautiful table names
|
||||||
|
@ -99,7 +102,11 @@ type NamespaceWithLists struct {
|
||||||
// @Success 200 {array} models.NamespaceWithLists "The Namespaces."
|
// @Success 200 {array} models.NamespaceWithLists "The Namespaces."
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /namespaces [get]
|
// @Router /namespaces [get]
|
||||||
func (n *Namespace) ReadAll(search string, doer *User, page int) (interface{}, error) {
|
func (n *Namespace) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||||
|
doer, err := getUserWithError(a)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
all := []*NamespaceWithLists{}
|
all := []*NamespaceWithLists{}
|
||||||
|
|
||||||
|
@ -117,7 +124,7 @@ func (n *Namespace) ReadAll(search string, doer *User, page int) (interface{}, e
|
||||||
[]*List{},
|
[]*List{},
|
||||||
})
|
})
|
||||||
|
|
||||||
err := x.Select("namespaces.*").
|
err = x.Select("namespaces.*").
|
||||||
Table("namespaces").
|
Table("namespaces").
|
||||||
Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id").
|
Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id").
|
||||||
Join("LEFT", "team_members", "team_members.team_id = team_namespaces.team_id").
|
Join("LEFT", "team_members", "team_members.team_id = team_namespaces.team_id").
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "code.vikunja.io/web"
|
||||||
|
|
||||||
// Create implements the creation method via the interface
|
// Create implements the creation method via the interface
|
||||||
// @Summary Creates a new namespace
|
// @Summary Creates a new namespace
|
||||||
// @Description Creates a new namespace.
|
// @Description Creates a new namespace.
|
||||||
|
@ -29,7 +31,12 @@ package models
|
||||||
// @Failure 403 {object} models.HTTPError "The user does not have access to the namespace"
|
// @Failure 403 {object} models.HTTPError "The user does not have access to the namespace"
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /namespaces [put]
|
// @Router /namespaces [put]
|
||||||
func (n *Namespace) Create(doer *User) (err error) {
|
func (n *Namespace) Create(a web.Auth) (err error) {
|
||||||
|
doer, err := getUserWithError(a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Check if we have at least a name
|
// Check if we have at least a name
|
||||||
if n.Name == "" {
|
if n.Name == "" {
|
||||||
return ErrNamespaceNameCannotBeEmpty{NamespaceID: 0, UserID: doer.ID}
|
return ErrNamespaceNameCannotBeEmpty{NamespaceID: 0, UserID: doer.ID}
|
||||||
|
|
|
@ -18,10 +18,13 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
|
"code.vikunja.io/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsAdmin returns true or false if the user is admin on that namespace or not
|
// IsAdmin returns true or false if the user is admin on that namespace or not
|
||||||
func (n *Namespace) IsAdmin(u *User) bool {
|
func (n *Namespace) IsAdmin(a web.Auth) bool {
|
||||||
|
u := getUserForRights(a)
|
||||||
|
|
||||||
// Owners always have admin rights
|
// Owners always have admin rights
|
||||||
if u.ID == n.Owner.ID {
|
if u.ID == n.Owner.ID {
|
||||||
return true
|
return true
|
||||||
|
@ -37,7 +40,9 @@ func (n *Namespace) IsAdmin(u *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanWrite checks if a user has write access to a namespace
|
// CanWrite checks if a user has write access to a namespace
|
||||||
func (n *Namespace) CanWrite(u *User) bool {
|
func (n *Namespace) CanWrite(a web.Auth) bool {
|
||||||
|
u := getUserForRights(a)
|
||||||
|
|
||||||
// Admins always have write access
|
// Admins always have write access
|
||||||
if n.IsAdmin(u) {
|
if n.IsAdmin(u) {
|
||||||
return true
|
return true
|
||||||
|
@ -53,7 +58,9 @@ func (n *Namespace) CanWrite(u *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanRead checks if a user has read access to that namespace
|
// CanRead checks if a user has read access to that namespace
|
||||||
func (n *Namespace) CanRead(u *User) bool {
|
func (n *Namespace) CanRead(a web.Auth) bool {
|
||||||
|
u := getUserForRights(a)
|
||||||
|
|
||||||
// Admins always have read access
|
// Admins always have read access
|
||||||
if n.IsAdmin(u) {
|
if n.IsAdmin(u) {
|
||||||
return true
|
return true
|
||||||
|
@ -69,7 +76,9 @@ func (n *Namespace) CanRead(u *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanUpdate checks if the user can update the namespace
|
// CanUpdate checks if the user can update the namespace
|
||||||
func (n *Namespace) CanUpdate(u *User) bool {
|
func (n *Namespace) CanUpdate(a web.Auth) bool {
|
||||||
|
u := getUserForRights(a)
|
||||||
|
|
||||||
nn, err := GetNamespaceByID(n.ID)
|
nn, err := GetNamespaceByID(n.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error("Error occurred during CanUpdate for Namespace: %s", err)
|
log.Log.Error("Error occurred during CanUpdate for Namespace: %s", err)
|
||||||
|
@ -79,7 +88,9 @@ func (n *Namespace) CanUpdate(u *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanDelete checks if the user can delete a namespace
|
// CanDelete checks if the user can delete a namespace
|
||||||
func (n *Namespace) CanDelete(u *User) bool {
|
func (n *Namespace) CanDelete(a web.Auth) bool {
|
||||||
|
u := getUserForRights(a)
|
||||||
|
|
||||||
nn, err := GetNamespaceByID(n.ID)
|
nn, err := GetNamespaceByID(n.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error("Error occurred during CanDelete for Namespace: %s", err)
|
log.Log.Error("Error occurred during CanDelete for Namespace: %s", err)
|
||||||
|
@ -89,7 +100,7 @@ func (n *Namespace) CanDelete(u *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanCreate checks if the user can create a new namespace
|
// CanCreate checks if the user can create a new namespace
|
||||||
func (n *Namespace) CanCreate(u *User) bool {
|
func (n *Namespace) CanCreate(a web.Auth) bool {
|
||||||
// This is currently a dummy function, later on we could imagine global limits etc.
|
// This is currently a dummy function, later on we could imagine global limits etc.
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -99,9 +110,9 @@ func (n *Namespace) checkTeamRights(u *User, r TeamRight) bool {
|
||||||
Table("namespaces").
|
Table("namespaces").
|
||||||
Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id").
|
Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id").
|
||||||
Join("LEFT", "team_members", "team_members.team_id = team_namespaces.team_id").
|
Join("LEFT", "team_members", "team_members.team_id = team_namespaces.team_id").
|
||||||
Where("namespaces.id = ? "+
|
Where("namespaces.id = ? AND ("+
|
||||||
"AND (team_members.user_id = ? AND team_namespaces.right = ?) "+
|
"(team_members.user_id = ? AND team_namespaces.right = ?) "+
|
||||||
"OR namespaces.owner_id = ? ", n.ID, u.ID, r, u.ID).
|
"OR namespaces.owner_id = ?)", n.ID, u.ID, r, u.ID).
|
||||||
Get(&Namespace{})
|
Get(&Namespace{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error("Error occurred during checkTeamRights for Namespace: %s, TeamRight: %d", err, r)
|
log.Log.Error("Error occurred during checkTeamRights for Namespace: %s, TeamRight: %d", err, r)
|
||||||
|
@ -116,8 +127,8 @@ func (n *Namespace) checkUserRights(u *User, r UserRight) bool {
|
||||||
Table("namespaces").
|
Table("namespaces").
|
||||||
Join("LEFT", "users_namespace", "users_namespace.namespace_id = namespaces.id").
|
Join("LEFT", "users_namespace", "users_namespace.namespace_id = namespaces.id").
|
||||||
Where("namespaces.id = ? AND ("+
|
Where("namespaces.id = ? AND ("+
|
||||||
"namespaces.owner_id = ? "+
|
"(users_namespace.user_id = ? AND users_namespace.right = ?) "+
|
||||||
"OR (users_namespace.user_id = ? AND users_namespace.right = ?))", n.ID, u.ID, u.ID, r).
|
"OR namespaces.owner_id = ?)", n.ID, u.ID, r, u.ID).
|
||||||
Get(&Namespace{})
|
Get(&Namespace{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error("Error occurred during checkUserRights for Namespace: %s, UserRight: %d", err, r)
|
log.Log.Error("Error occurred during checkUserRights for Namespace: %s, UserRight: %d", err, r)
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "code.vikunja.io/web"
|
||||||
|
|
||||||
// NamespaceUser represents a namespace <-> user relation
|
// NamespaceUser represents a namespace <-> user relation
|
||||||
type NamespaceUser struct {
|
type NamespaceUser struct {
|
||||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"namespace"`
|
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"namespace"`
|
||||||
|
@ -26,8 +28,8 @@ type NamespaceUser struct {
|
||||||
Created int64 `xorm:"created" json:"created"`
|
Created int64 `xorm:"created" json:"created"`
|
||||||
Updated int64 `xorm:"updated" json:"updated"`
|
Updated int64 `xorm:"updated" json:"updated"`
|
||||||
|
|
||||||
CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName is the table name for NamespaceUser
|
// TableName is the table name for NamespaceUser
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "code.vikunja.io/web"
|
||||||
|
|
||||||
// Create creates a new namespace <-> user relation
|
// Create creates a new namespace <-> user relation
|
||||||
// @Summary Add a user to a namespace
|
// @Summary Add a user to a namespace
|
||||||
// @Description Gives a user access to a namespace.
|
// @Description Gives a user access to a namespace.
|
||||||
|
@ -31,8 +33,7 @@ package models
|
||||||
// @Failure 403 {object} models.HTTPError "The user does not have access to the namespace"
|
// @Failure 403 {object} models.HTTPError "The user does not have access to the namespace"
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /namespaces/{id}/users [put]
|
// @Router /namespaces/{id}/users [put]
|
||||||
func (un *NamespaceUser) Create(u *User) (err error) {
|
func (un *NamespaceUser) Create(a web.Auth) (err error) {
|
||||||
|
|
||||||
// Reset the id
|
// Reset the id
|
||||||
un.ID = 0
|
un.ID = 0
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "code.vikunja.io/web"
|
||||||
|
|
||||||
// ReadAll gets all users who have access to a namespace
|
// ReadAll gets all users who have access to a namespace
|
||||||
// @Summary Get users on a namespace
|
// @Summary Get users on a namespace
|
||||||
// @Description Returns a namespace with all users which have access on a given namespace.
|
// @Description Returns a namespace with all users which have access on a given namespace.
|
||||||
|
@ -30,7 +32,12 @@ package models
|
||||||
// @Failure 403 {object} models.HTTPError "No right to see the namespace."
|
// @Failure 403 {object} models.HTTPError "No right to see the namespace."
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /namespaces/{id}/users [get]
|
// @Router /namespaces/{id}/users [get]
|
||||||
func (un *NamespaceUser) ReadAll(search string, u *User, page int) (interface{}, error) {
|
func (un *NamespaceUser) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||||
|
u, err := getUserWithError(a)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the user has access to the namespace
|
// Check if the user has access to the namespace
|
||||||
l, err := GetNamespaceByID(un.NamespaceID)
|
l, err := GetNamespaceByID(un.NamespaceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -18,10 +18,13 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
|
"code.vikunja.io/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CanCreate checks if the user can create a new user <-> namespace relation
|
// CanCreate checks if the user can create a new user <-> namespace relation
|
||||||
func (nu *NamespaceUser) CanCreate(doer *User) bool {
|
func (nu *NamespaceUser) CanCreate(a web.Auth) bool {
|
||||||
|
doer := getUserForRights(a)
|
||||||
|
|
||||||
// Get the namespace and check if the user has write access on it
|
// Get the namespace and check if the user has write access on it
|
||||||
n, err := GetNamespaceByID(nu.NamespaceID)
|
n, err := GetNamespaceByID(nu.NamespaceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -32,7 +35,9 @@ func (nu *NamespaceUser) CanCreate(doer *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanDelete checks if the user can delete a user <-> namespace relation
|
// CanDelete checks if the user can delete a user <-> namespace relation
|
||||||
func (nu *NamespaceUser) CanDelete(doer *User) bool {
|
func (nu *NamespaceUser) CanDelete(a web.Auth) bool {
|
||||||
|
doer := getUserForRights(a)
|
||||||
|
|
||||||
// Get the namespace and check if the user has write access on it
|
// Get the namespace and check if the user has write access on it
|
||||||
n, err := GetNamespaceByID(nu.NamespaceID)
|
n, err := GetNamespaceByID(nu.NamespaceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -43,7 +48,9 @@ func (nu *NamespaceUser) CanDelete(doer *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanUpdate checks if the user can update a user <-> namespace relation
|
// CanUpdate checks if the user can update a user <-> namespace relation
|
||||||
func (nu *NamespaceUser) CanUpdate(doer *User) bool {
|
func (nu *NamespaceUser) CanUpdate(a web.Auth) bool {
|
||||||
|
doer := getUserForRights(a)
|
||||||
|
|
||||||
// Get the namespace and check if the user has write access on it
|
// Get the namespace and check if the user has write access on it
|
||||||
n, err := GetNamespaceByID(nu.NamespaceID)
|
n, err := GetNamespaceByID(nu.NamespaceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
// Vikunja is a todo-list application to facilitate your life.
|
|
||||||
// Copyright 2018 Vikunja and contributors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package models
|
|
||||||
|
|
||||||
// Rights defines rights methods
|
|
||||||
type Rights interface {
|
|
||||||
IsAdmin(*User) bool
|
|
||||||
CanWrite(*User) bool
|
|
||||||
CanRead(*User) bool
|
|
||||||
CanDelete(*User) bool
|
|
||||||
CanUpdate(*User) bool
|
|
||||||
CanCreate(*User) bool
|
|
||||||
}
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "code.vikunja.io/web"
|
||||||
|
|
||||||
// TeamList defines the relation between a team and a list
|
// TeamList defines the relation between a team and a list
|
||||||
type TeamList struct {
|
type TeamList struct {
|
||||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
|
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
|
||||||
|
@ -26,8 +28,8 @@ type TeamList struct {
|
||||||
Created int64 `xorm:"created" json:"created"`
|
Created int64 `xorm:"created" json:"created"`
|
||||||
Updated int64 `xorm:"updated" json:"updated"`
|
Updated int64 `xorm:"updated" json:"updated"`
|
||||||
|
|
||||||
CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName makes beautiful table names
|
// TableName makes beautiful table names
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "code.vikunja.io/web"
|
||||||
|
|
||||||
// Create creates a new team <-> list relation
|
// Create creates a new team <-> list relation
|
||||||
// @Summary Add a team to a list
|
// @Summary Add a team to a list
|
||||||
// @Description Gives a team access to a list.
|
// @Description Gives a team access to a list.
|
||||||
|
@ -31,7 +33,7 @@ package models
|
||||||
// @Failure 403 {object} models.HTTPError "The user does not have access to the list"
|
// @Failure 403 {object} models.HTTPError "The user does not have access to the list"
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /lists/{id}/teams [put]
|
// @Router /lists/{id}/teams [put]
|
||||||
func (tl *TeamList) Create(doer *User) (err error) {
|
func (tl *TeamList) Create(a web.Auth) (err error) {
|
||||||
|
|
||||||
// Check if the rights are valid
|
// Check if the rights are valid
|
||||||
if err = tl.Right.isValid(); err != nil {
|
if err = tl.Right.isValid(); err != nil {
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "code.vikunja.io/web"
|
||||||
|
|
||||||
// ReadAll implements the method to read all teams of a list
|
// ReadAll implements the method to read all teams of a list
|
||||||
// @Summary Get teams on a list
|
// @Summary Get teams on a list
|
||||||
// @Description Returns a list with all teams which have access on a given list.
|
// @Description Returns a list with all teams which have access on a given list.
|
||||||
|
@ -30,7 +32,12 @@ package models
|
||||||
// @Failure 403 {object} models.HTTPError "No right to see the list."
|
// @Failure 403 {object} models.HTTPError "No right to see the list."
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /lists/{id}/teams [get]
|
// @Router /lists/{id}/teams [get]
|
||||||
func (tl *TeamList) ReadAll(search string, u *User, page int) (interface{}, error) {
|
func (tl *TeamList) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||||
|
u, err := getUserWithError(a)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the user can read the namespace
|
// Check if the user can read the namespace
|
||||||
l := &List{ID: tl.ListID}
|
l := &List{ID: tl.ListID}
|
||||||
if err := l.GetSimpleByID(); err != nil {
|
if err := l.GetSimpleByID(); err != nil {
|
||||||
|
@ -42,7 +49,7 @@ func (tl *TeamList) ReadAll(search string, u *User, page int) (interface{}, erro
|
||||||
|
|
||||||
// Get the teams
|
// Get the teams
|
||||||
all := []*TeamWithRight{}
|
all := []*TeamWithRight{}
|
||||||
err := x.
|
err = x.
|
||||||
Table("teams").
|
Table("teams").
|
||||||
Join("INNER", "team_list", "team_id = teams.id").
|
Join("INNER", "team_list", "team_id = teams.id").
|
||||||
Where("team_list.list_id = ?", tl.ListID).
|
Where("team_list.list_id = ?", tl.ListID).
|
||||||
|
|
|
@ -18,10 +18,13 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
|
"code.vikunja.io/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CanCreate checks if the user can create a team <-> list relation
|
// CanCreate checks if the user can create a team <-> list relation
|
||||||
func (tl *TeamList) CanCreate(u *User) bool {
|
func (tl *TeamList) CanCreate(a web.Auth) bool {
|
||||||
|
u := getUserForRights(a)
|
||||||
|
|
||||||
l := List{ID: tl.ListID}
|
l := List{ID: tl.ListID}
|
||||||
if err := l.GetSimpleByID(); err != nil {
|
if err := l.GetSimpleByID(); err != nil {
|
||||||
log.Log.Error("Error occurred during CanCreate for TeamList: %s", err)
|
log.Log.Error("Error occurred during CanCreate for TeamList: %s", err)
|
||||||
|
@ -31,7 +34,9 @@ func (tl *TeamList) CanCreate(u *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanDelete checks if the user can delete a team <-> list relation
|
// CanDelete checks if the user can delete a team <-> list relation
|
||||||
func (tl *TeamList) CanDelete(user *User) bool {
|
func (tl *TeamList) CanDelete(a web.Auth) bool {
|
||||||
|
user := getUserForRights(a)
|
||||||
|
|
||||||
l := List{ID: tl.ListID}
|
l := List{ID: tl.ListID}
|
||||||
if err := l.GetSimpleByID(); err != nil {
|
if err := l.GetSimpleByID(); err != nil {
|
||||||
log.Log.Error("Error occurred during CanDelete for TeamList: %s", err)
|
log.Log.Error("Error occurred during CanDelete for TeamList: %s", err)
|
||||||
|
@ -41,7 +46,9 @@ func (tl *TeamList) CanDelete(user *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanUpdate checks if the user can update a team <-> list relation
|
// CanUpdate checks if the user can update a team <-> list relation
|
||||||
func (tl *TeamList) CanUpdate(user *User) bool {
|
func (tl *TeamList) CanUpdate(a web.Auth) bool {
|
||||||
|
user := getUserForRights(a)
|
||||||
|
|
||||||
l := List{ID: tl.ListID}
|
l := List{ID: tl.ListID}
|
||||||
if err := l.GetSimpleByID(); err != nil {
|
if err := l.GetSimpleByID(); err != nil {
|
||||||
log.Log.Error("Error occurred during CanUpdate for TeamList: %s", err)
|
log.Log.Error("Error occurred during CanUpdate for TeamList: %s", err)
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "code.vikunja.io/web"
|
||||||
|
|
||||||
// Create implements the create method to assign a user to a team
|
// Create implements the create method to assign a user to a team
|
||||||
// @Summary Add a user to a team
|
// @Summary Add a user to a team
|
||||||
// @Description Add a user to a team.
|
// @Description Add a user to a team.
|
||||||
|
@ -30,7 +32,8 @@ package models
|
||||||
// @Failure 403 {object} models.HTTPError "The user does not have access to the team"
|
// @Failure 403 {object} models.HTTPError "The user does not have access to the team"
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /teams/{id}/members [put]
|
// @Router /teams/{id}/members [put]
|
||||||
func (tm *TeamMember) Create(doer *User) (err error) {
|
func (tm *TeamMember) Create(a web.Auth) (err error) {
|
||||||
|
|
||||||
// Check if the team extst
|
// Check if the team extst
|
||||||
_, err = GetTeamByID(tm.TeamID)
|
_, err = GetTeamByID(tm.TeamID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -18,20 +18,23 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
|
"code.vikunja.io/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CanCreate checks if the user can add a new tem member
|
// CanCreate checks if the user can add a new tem member
|
||||||
func (tm *TeamMember) CanCreate(u *User) bool {
|
func (tm *TeamMember) CanCreate(a web.Auth) bool {
|
||||||
return tm.IsAdmin(u)
|
return tm.IsAdmin(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanDelete checks if the user can delete a new team member
|
// CanDelete checks if the user can delete a new team member
|
||||||
func (tm *TeamMember) CanDelete(u *User) bool {
|
func (tm *TeamMember) CanDelete(a web.Auth) bool {
|
||||||
return tm.IsAdmin(u)
|
return tm.IsAdmin(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsAdmin checks if the user is team admin
|
// IsAdmin checks if the user is team admin
|
||||||
func (tm *TeamMember) IsAdmin(u *User) bool {
|
func (tm *TeamMember) IsAdmin(a web.Auth) bool {
|
||||||
|
u := getUserForRights(a)
|
||||||
|
|
||||||
// A user can add a member to a team if he is admin of that team
|
// A user can add a member to a team if he is admin of that team
|
||||||
exists, err := x.Where("user_id = ? AND team_id = ? AND admin = ?", u.ID, tm.TeamID, true).
|
exists, err := x.Where("user_id = ? AND team_id = ? AND admin = ?", u.ID, tm.TeamID, true).
|
||||||
Get(&TeamMember{})
|
Get(&TeamMember{})
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "code.vikunja.io/web"
|
||||||
|
|
||||||
// TeamNamespace defines the relationship between a Team and a Namespace
|
// TeamNamespace defines the relationship between a Team and a Namespace
|
||||||
type TeamNamespace struct {
|
type TeamNamespace struct {
|
||||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
|
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
|
||||||
|
@ -26,8 +28,8 @@ type TeamNamespace struct {
|
||||||
Created int64 `xorm:"created" json:"created"`
|
Created int64 `xorm:"created" json:"created"`
|
||||||
Updated int64 `xorm:"updated" json:"updated"`
|
Updated int64 `xorm:"updated" json:"updated"`
|
||||||
|
|
||||||
CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName makes beautiful table names
|
// TableName makes beautiful table names
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "code.vikunja.io/web"
|
||||||
|
|
||||||
// Create creates a new team <-> namespace relation
|
// Create creates a new team <-> namespace relation
|
||||||
// @Summary Add a team to a namespace
|
// @Summary Add a team to a namespace
|
||||||
// @Description Gives a team access to a namespace.
|
// @Description Gives a team access to a namespace.
|
||||||
|
@ -31,7 +33,7 @@ package models
|
||||||
// @Failure 403 {object} models.HTTPError "The team does not have access to the namespace"
|
// @Failure 403 {object} models.HTTPError "The team does not have access to the namespace"
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /namespaces/{id}/teams [put]
|
// @Router /namespaces/{id}/teams [put]
|
||||||
func (tn *TeamNamespace) Create(doer *User) (err error) {
|
func (tn *TeamNamespace) Create(a web.Auth) (err error) {
|
||||||
|
|
||||||
// Check if the rights are valid
|
// Check if the rights are valid
|
||||||
if err = tn.Right.isValid(); err != nil {
|
if err = tn.Right.isValid(); err != nil {
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "code.vikunja.io/web"
|
||||||
|
|
||||||
// ReadAll implements the method to read all teams of a namespace
|
// ReadAll implements the method to read all teams of a namespace
|
||||||
// @Summary Get teams on a namespace
|
// @Summary Get teams on a namespace
|
||||||
// @Description Returns a namespace with all teams which have access on a given namespace.
|
// @Description Returns a namespace with all teams which have access on a given namespace.
|
||||||
|
@ -30,7 +32,12 @@ package models
|
||||||
// @Failure 403 {object} models.HTTPError "No right to see the namespace."
|
// @Failure 403 {object} models.HTTPError "No right to see the namespace."
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /namespaces/{id}/teams [get]
|
// @Router /namespaces/{id}/teams [get]
|
||||||
func (tn *TeamNamespace) ReadAll(search string, user *User, page int) (interface{}, error) {
|
func (tn *TeamNamespace) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||||
|
user, err := getUserWithError(a)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the user can read the namespace
|
// Check if the user can read the namespace
|
||||||
n, err := GetNamespaceByID(tn.NamespaceID)
|
n, err := GetNamespaceByID(tn.NamespaceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -18,10 +18,13 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
|
"code.vikunja.io/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CanCreate checks if one can create a new team <-> namespace relation
|
// CanCreate checks if one can create a new team <-> namespace relation
|
||||||
func (tn *TeamNamespace) CanCreate(user *User) bool {
|
func (tn *TeamNamespace) CanCreate(a web.Auth) bool {
|
||||||
|
user := getUserForRights(a)
|
||||||
|
|
||||||
n, err := GetNamespaceByID(tn.NamespaceID)
|
n, err := GetNamespaceByID(tn.NamespaceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error("Error occurred during CanCreate for TeamNamespace: %s", err)
|
log.Log.Error("Error occurred during CanCreate for TeamNamespace: %s", err)
|
||||||
|
@ -31,7 +34,9 @@ func (tn *TeamNamespace) CanCreate(user *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanDelete checks if a user can remove a team from a namespace. Only namespace admins can do that.
|
// CanDelete checks if a user can remove a team from a namespace. Only namespace admins can do that.
|
||||||
func (tn *TeamNamespace) CanDelete(user *User) bool {
|
func (tn *TeamNamespace) CanDelete(a web.Auth) bool {
|
||||||
|
user := getUserForRights(a)
|
||||||
|
|
||||||
n, err := GetNamespaceByID(tn.NamespaceID)
|
n, err := GetNamespaceByID(tn.NamespaceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error("Error occurred during CanDelete for TeamNamespace: %s", err)
|
log.Log.Error("Error occurred during CanDelete for TeamNamespace: %s", err)
|
||||||
|
@ -41,7 +46,9 @@ func (tn *TeamNamespace) CanDelete(user *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanUpdate checks if a user can update a team from a Only namespace admins can do that.
|
// CanUpdate checks if a user can update a team from a Only namespace admins can do that.
|
||||||
func (tn *TeamNamespace) CanUpdate(user *User) bool {
|
func (tn *TeamNamespace) CanUpdate(a web.Auth) bool {
|
||||||
|
user := getUserForRights(a)
|
||||||
|
|
||||||
n, err := GetNamespaceByID(tn.NamespaceID)
|
n, err := GetNamespaceByID(tn.NamespaceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error("Error occurred during CanUpdate for TeamNamespace: %s", err)
|
log.Log.Error("Error occurred during CanUpdate for TeamNamespace: %s", err)
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "code.vikunja.io/web"
|
||||||
|
|
||||||
// Team holds a team object
|
// Team holds a team object
|
||||||
type Team struct {
|
type Team struct {
|
||||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"team"`
|
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"team"`
|
||||||
|
@ -29,8 +31,8 @@ type Team struct {
|
||||||
Created int64 `xorm:"created" json:"created"`
|
Created int64 `xorm:"created" json:"created"`
|
||||||
Updated int64 `xorm:"updated" json:"updated"`
|
Updated int64 `xorm:"updated" json:"updated"`
|
||||||
|
|
||||||
CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName makes beautiful table names
|
// TableName makes beautiful table names
|
||||||
|
@ -61,8 +63,8 @@ type TeamMember struct {
|
||||||
Created int64 `xorm:"created" json:"created"`
|
Created int64 `xorm:"created" json:"created"`
|
||||||
Updated int64 `xorm:"updated" json:"updated"`
|
Updated int64 `xorm:"updated" json:"updated"`
|
||||||
|
|
||||||
CRUDable `xorm:"-" json:"-"`
|
web.CRUDable `xorm:"-" json:"-"`
|
||||||
Rights `xorm:"-" json:"-"`
|
web.Rights `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName makes beautiful table names
|
// TableName makes beautiful table names
|
||||||
|
@ -122,7 +124,12 @@ func (t *Team) ReadOne() (err error) {
|
||||||
// @Success 200 {array} models.Team "The teams."
|
// @Success 200 {array} models.Team "The teams."
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /teams [get]
|
// @Router /teams [get]
|
||||||
func (t *Team) ReadAll(search string, user *User, page int) (teams interface{}, err error) {
|
func (t *Team) ReadAll(search string, a web.Auth, page int) (interface{}, error) {
|
||||||
|
user, err := getUserWithError(a)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
all := []*Team{}
|
all := []*Team{}
|
||||||
err = x.Select("teams.*").
|
err = x.Select("teams.*").
|
||||||
Table("teams").
|
Table("teams").
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "code.vikunja.io/web"
|
||||||
|
|
||||||
// Create is the handler to create a team
|
// Create is the handler to create a team
|
||||||
// @Summary Creates a new team
|
// @Summary Creates a new team
|
||||||
// @Description Creates a new team in a given namespace. The user needs write-access to the namespace.
|
// @Description Creates a new team in a given namespace. The user needs write-access to the namespace.
|
||||||
|
@ -28,7 +30,12 @@ package models
|
||||||
// @Failure 400 {object} models.HTTPError "Invalid team object provided."
|
// @Failure 400 {object} models.HTTPError "Invalid team object provided."
|
||||||
// @Failure 500 {object} models.Message "Internal error"
|
// @Failure 500 {object} models.Message "Internal error"
|
||||||
// @Router /teams [put]
|
// @Router /teams [put]
|
||||||
func (t *Team) Create(doer *User) (err error) {
|
func (t *Team) Create(a web.Auth) (err error) {
|
||||||
|
doer, err := getUserWithError(a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Check if we have a name
|
// Check if we have a name
|
||||||
if t.Name == "" {
|
if t.Name == "" {
|
||||||
return ErrTeamNameCannotBeEmpty{}
|
return ErrTeamNameCannotBeEmpty{}
|
||||||
|
|
|
@ -18,16 +18,18 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
|
"code.vikunja.io/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CanCreate checks if the user can create a new team
|
// CanCreate checks if the user can create a new team
|
||||||
func (t *Team) CanCreate(u *User) bool {
|
func (t *Team) CanCreate(a web.Auth) bool {
|
||||||
// This is currently a dummy function, later on we could imagine global limits etc.
|
// This is currently a dummy function, later on we could imagine global limits etc.
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanUpdate checks if the user can update a team
|
// CanUpdate checks if the user can update a team
|
||||||
func (t *Team) CanUpdate(u *User) bool {
|
func (t *Team) CanUpdate(a web.Auth) bool {
|
||||||
|
u := getUserForRights(a)
|
||||||
|
|
||||||
// Check if the current user is in the team and has admin rights in it
|
// Check if the current user is in the team and has admin rights in it
|
||||||
exists, err := x.Where("team_id = ?", t.ID).
|
exists, err := x.Where("team_id = ?", t.ID).
|
||||||
|
@ -43,12 +45,14 @@ func (t *Team) CanUpdate(u *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanDelete checks if a user can delete a team
|
// CanDelete checks if a user can delete a team
|
||||||
func (t *Team) CanDelete(u *User) bool {
|
func (t *Team) CanDelete(a web.Auth) bool {
|
||||||
return t.IsAdmin(u)
|
return t.IsAdmin(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsAdmin returns true when the user is admin of a team
|
// IsAdmin returns true when the user is admin of a team
|
||||||
func (t *Team) IsAdmin(u *User) bool {
|
func (t *Team) IsAdmin(a web.Auth) bool {
|
||||||
|
u := getUserForRights(a)
|
||||||
|
|
||||||
exists, err := x.Where("team_id = ?", t.ID).
|
exists, err := x.Where("team_id = ?", t.ID).
|
||||||
And("user_id = ?", u.ID).
|
And("user_id = ?", u.ID).
|
||||||
And("admin = ?", true).
|
And("admin = ?", true).
|
||||||
|
@ -61,7 +65,9 @@ func (t *Team) IsAdmin(u *User) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanRead returns true if the user has read access to the team
|
// CanRead returns true if the user has read access to the team
|
||||||
func (t *Team) CanRead(user *User) bool {
|
func (t *Team) CanRead(a web.Auth) bool {
|
||||||
|
user := getUserForRights(a)
|
||||||
|
|
||||||
// Check if the user is in the team
|
// Check if the user is in the team
|
||||||
exists, err := x.Where("team_id = ?", t.ID).
|
exists, err := x.Where("team_id = ?", t.ID).
|
||||||
And("user_id = ?", user.ID).
|
And("user_id = ?", user.ID).
|
||||||
|
|
|
@ -17,9 +17,13 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/log"
|
||||||
|
"code.vikunja.io/web"
|
||||||
|
"fmt"
|
||||||
"github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserLogin Object to recive user credentials in JSON format
|
// UserLogin Object to recive user credentials in JSON format
|
||||||
|
@ -41,13 +45,34 @@ type User struct {
|
||||||
|
|
||||||
Created int64 `xorm:"created" json:"created"`
|
Created int64 `xorm:"created" json:"created"`
|
||||||
Updated int64 `xorm:"updated" json:"updated"`
|
Updated int64 `xorm:"updated" json:"updated"`
|
||||||
|
|
||||||
|
web.Auth `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AuthDummy implements the auth of the crud handler
|
||||||
|
func (User) AuthDummy() {}
|
||||||
|
|
||||||
// TableName returns the table name for users
|
// TableName returns the table name for users
|
||||||
func (User) TableName() string {
|
func (User) TableName() string {
|
||||||
return "users"
|
return "users"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getUserForRights(a web.Auth) *User {
|
||||||
|
u, err := getUserWithError(a)
|
||||||
|
if err != nil {
|
||||||
|
log.Log.Error(err.Error())
|
||||||
|
}
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUserWithError(a web.Auth) (*User, error) {
|
||||||
|
u, is := a.(*User)
|
||||||
|
if !is {
|
||||||
|
return &User{}, fmt.Errorf("user is not user element, is %s", reflect.TypeOf(a))
|
||||||
|
}
|
||||||
|
return u, nil
|
||||||
|
}
|
||||||
|
|
||||||
// APIUserPassword represents a user object without timestamps and a json password field.
|
// APIUserPassword represents a user object without timestamps and a json password field.
|
||||||
type APIUserPassword struct {
|
type APIUserPassword struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
|
@ -119,14 +144,14 @@ func CheckUserCredentials(u *UserLogin) (User, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentUser returns the current user based on its jwt token
|
// GetCurrentUser returns the current user based on its jwt token
|
||||||
func GetCurrentUser(c echo.Context) (user User, err error) {
|
func GetCurrentUser(c echo.Context) (user *User, err error) {
|
||||||
jwtinf := c.Get("user").(*jwt.Token)
|
jwtinf := c.Get("user").(*jwt.Token)
|
||||||
claims := jwtinf.Claims.(jwt.MapClaims)
|
claims := jwtinf.Claims.(jwt.MapClaims)
|
||||||
userID, ok := claims["id"].(float64)
|
userID, ok := claims["id"].(float64)
|
||||||
if !ok {
|
if !ok {
|
||||||
return user, ErrCouldNotGetUserID{}
|
return user, ErrCouldNotGetUserID{}
|
||||||
}
|
}
|
||||||
user = User{
|
user = &User{
|
||||||
ID: int64(userID),
|
ID: int64(userID),
|
||||||
Email: claims["email"].(string),
|
Email: claims["email"].(string),
|
||||||
Username: claims["username"].(string),
|
Username: claims["username"].(string),
|
||||||
|
|
|
@ -19,7 +19,7 @@ package v1
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/caldav"
|
"code.vikunja.io/api/pkg/caldav"
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
"code.vikunja.io/api/pkg/routes/crud"
|
"code.vikunja.io/web/handler"
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
@ -54,7 +54,7 @@ func Caldav(c echo.Context) error {
|
||||||
// Get all tasks for that user
|
// Get all tasks for that user
|
||||||
tasks, err := models.GetTasksByUser("", &u, -1)
|
tasks, err := models.GetTasksByUser("", &u, -1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return crud.HandleHTTPError(err)
|
return handler.HandleHTTPError(err, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
hour := int64(time.Hour.Seconds())
|
hour := int64(time.Hour.Seconds())
|
||||||
|
|
|
@ -81,7 +81,7 @@ func getNamespace(c echo.Context) (namespace models.Namespace, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !namespace.CanRead(&user) {
|
if !namespace.CanRead(user) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
"code.vikunja.io/api/pkg/routes/crud"
|
"code.vikunja.io/web/handler"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
|
@ -53,7 +53,7 @@ func Login(c echo.Context) error {
|
||||||
// Check user
|
// Check user
|
||||||
user, err := models.CheckUserCredentials(&u)
|
user, err := models.CheckUserCredentials(&u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return crud.HandleHTTPError(err)
|
return handler.HandleHTTPError(err, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create token
|
// Create token
|
||||||
|
|
|
@ -18,7 +18,7 @@ package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
"code.vikunja.io/api/pkg/routes/crud"
|
"code.vikunja.io/web/handler"
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
@ -44,7 +44,7 @@ func RegisterUser(c echo.Context) error {
|
||||||
// Insert the user
|
// Insert the user
|
||||||
newUser, err := models.CreateUser(datUser.APIFormat())
|
newUser, err := models.CreateUser(datUser.APIFormat())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return crud.HandleHTTPError(err)
|
return handler.HandleHTTPError(err, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, newUser)
|
return c.JSON(http.StatusOK, newUser)
|
||||||
|
|
|
@ -18,7 +18,7 @@ package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
"code.vikunja.io/api/pkg/routes/crud"
|
"code.vikunja.io/web/handler"
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
@ -43,7 +43,7 @@ func UserConfirmEmail(c echo.Context) error {
|
||||||
|
|
||||||
err := models.UserEmailConfirm(&emailConfirm)
|
err := models.UserEmailConfirm(&emailConfirm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return crud.HandleHTTPError(err)
|
return handler.HandleHTTPError(err, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, models.Message{"The email was confirmed successfully."})
|
return c.JSON(http.StatusOK, models.Message{"The email was confirmed successfully."})
|
||||||
|
|
|
@ -18,7 +18,7 @@ package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
"code.vikunja.io/api/pkg/routes/crud"
|
"code.vikunja.io/web/handler"
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -55,10 +55,10 @@ func UserDelete(c echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete it
|
// Delete it
|
||||||
err = models.DeleteUserByID(userID, &doer)
|
err = models.DeleteUserByID(userID, doer)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return crud.HandleHTTPError(err)
|
return handler.HandleHTTPError(err, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, models.Message{"success"})
|
return c.JSON(http.StatusOK, models.Message{"success"})
|
||||||
|
|
|
@ -18,7 +18,7 @@ package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
"code.vikunja.io/api/pkg/routes/crud"
|
"code.vikunja.io/web/handler"
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
@ -39,7 +39,7 @@ func UserList(c echo.Context) error {
|
||||||
s := c.QueryParam("s")
|
s := c.QueryParam("s")
|
||||||
users, err := models.ListUsers(s)
|
users, err := models.ListUsers(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return crud.HandleHTTPError(err)
|
return handler.HandleHTTPError(err, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obfuscate the mailadresses
|
// Obfuscate the mailadresses
|
||||||
|
|
|
@ -18,7 +18,7 @@ package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
"code.vikunja.io/api/pkg/routes/crud"
|
"code.vikunja.io/web/handler"
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
@ -43,7 +43,7 @@ func UserResetPassword(c echo.Context) error {
|
||||||
|
|
||||||
err := models.UserPasswordReset(&pwReset)
|
err := models.UserPasswordReset(&pwReset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return crud.HandleHTTPError(err)
|
return handler.HandleHTTPError(err, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, models.Message{"The password was updated successfully."})
|
return c.JSON(http.StatusOK, models.Message{"The password was updated successfully."})
|
||||||
|
@ -69,7 +69,7 @@ func UserRequestResetPasswordToken(c echo.Context) error {
|
||||||
|
|
||||||
err := models.RequestUserPasswordResetToken(&pwTokenReset)
|
err := models.RequestUserPasswordResetToken(&pwTokenReset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return crud.HandleHTTPError(err)
|
return handler.HandleHTTPError(err, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, models.Message{"Token was sent."})
|
return c.JSON(http.StatusOK, models.Message{"Token was sent."})
|
||||||
|
|
|
@ -18,7 +18,7 @@ package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
"code.vikunja.io/api/pkg/routes/crud"
|
"code.vikunja.io/web/handler"
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
@ -42,7 +42,7 @@ func UserShow(c echo.Context) error {
|
||||||
|
|
||||||
user, err := models.GetUserByID(userInfos.ID)
|
user, err := models.GetUserByID(userInfos.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return crud.HandleHTTPError(err)
|
return handler.HandleHTTPError(err, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, user)
|
return c.JSON(http.StatusOK, user)
|
||||||
|
|
|
@ -18,7 +18,7 @@ package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
"code.vikunja.io/api/pkg/routes/crud"
|
"code.vikunja.io/web/handler"
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
@ -57,12 +57,12 @@ func UserChangePassword(c echo.Context) error {
|
||||||
|
|
||||||
// Check the current password
|
// Check the current password
|
||||||
if _, err = models.CheckUserCredentials(&models.UserLogin{Username: doer.Username, Password: newPW.OldPassword}); err != nil {
|
if _, err = models.CheckUserCredentials(&models.UserLogin{Username: doer.Username, Password: newPW.OldPassword}); err != nil {
|
||||||
return crud.HandleHTTPError(err)
|
return handler.HandleHTTPError(err, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the password
|
// Update the password
|
||||||
if err = models.UpdateUserPassword(&doer, newPW.NewPassword); err != nil {
|
if err = models.UpdateUserPassword(doer, newPW.NewPassword); err != nil {
|
||||||
return crud.HandleHTTPError(err)
|
return handler.HandleHTTPError(err, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, models.Message{"The password was updated successfully."})
|
return c.JSON(http.StatusOK, models.Message{"The password was updated successfully."})
|
||||||
|
|
|
@ -44,9 +44,11 @@ package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "code.vikunja.io/api/docs" // To generate swagger docs
|
_ "code.vikunja.io/api/docs" // To generate swagger docs
|
||||||
|
"code.vikunja.io/api/pkg/log"
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
|
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
|
||||||
"code.vikunja.io/api/pkg/routes/crud"
|
"code.vikunja.io/web"
|
||||||
|
"code.vikunja.io/web/handler"
|
||||||
"github.com/asaskevich/govalidator"
|
"github.com/asaskevich/govalidator"
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
"github.com/labstack/echo/middleware"
|
"github.com/labstack/echo/middleware"
|
||||||
|
@ -67,7 +69,7 @@ func (cv *CustomValidator) Validate(i interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
httperr := models.ValidationHTTPError{
|
httperr := models.ValidationHTTPError{
|
||||||
models.HTTPError{
|
web.HTTPError{
|
||||||
Code: models.ErrCodeInvalidData,
|
Code: models.ErrCodeInvalidData,
|
||||||
Message: "Invalid Data",
|
Message: "Invalid Data",
|
||||||
},
|
},
|
||||||
|
@ -122,6 +124,20 @@ func RegisterRoutes(e *echo.Echo) {
|
||||||
// ===== Routes with Authetification =====
|
// ===== Routes with Authetification =====
|
||||||
// Authetification
|
// Authetification
|
||||||
a.Use(middleware.JWT([]byte(viper.GetString("service.JWTSecret"))))
|
a.Use(middleware.JWT([]byte(viper.GetString("service.JWTSecret"))))
|
||||||
|
|
||||||
|
// Put the authprovider in the context to be able to use it later
|
||||||
|
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
|
return func(c echo.Context) error {
|
||||||
|
c.Set("AuthProvider", &web.Auths{
|
||||||
|
AuthObject: func(echo.Context) (web.Auth, error) {
|
||||||
|
return models.GetCurrentUser(c)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
c.Set("LoggingProvider", &log.Log)
|
||||||
|
return next(c)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
a.POST("/tokenTest", apiv1.CheckToken)
|
a.POST("/tokenTest", apiv1.CheckToken)
|
||||||
|
|
||||||
// User stuff
|
// User stuff
|
||||||
|
@ -129,8 +145,8 @@ func RegisterRoutes(e *echo.Echo) {
|
||||||
a.POST("/user/password", apiv1.UserChangePassword)
|
a.POST("/user/password", apiv1.UserChangePassword)
|
||||||
a.GET("/users", apiv1.UserList)
|
a.GET("/users", apiv1.UserList)
|
||||||
|
|
||||||
listHandler := &crud.WebHandler{
|
listHandler := &handler.WebHandler{
|
||||||
EmptyStruct: func() crud.CObject {
|
EmptyStruct: func() handler.CObject {
|
||||||
return &models.List{}
|
return &models.List{}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -140,8 +156,8 @@ func RegisterRoutes(e *echo.Echo) {
|
||||||
a.DELETE("/lists/:list", listHandler.DeleteWeb)
|
a.DELETE("/lists/:list", listHandler.DeleteWeb)
|
||||||
a.PUT("/namespaces/:namespace/lists", listHandler.CreateWeb)
|
a.PUT("/namespaces/:namespace/lists", listHandler.CreateWeb)
|
||||||
|
|
||||||
taskHandler := &crud.WebHandler{
|
taskHandler := &handler.WebHandler{
|
||||||
EmptyStruct: func() crud.CObject {
|
EmptyStruct: func() handler.CObject {
|
||||||
return &models.ListTask{}
|
return &models.ListTask{}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -150,8 +166,8 @@ func RegisterRoutes(e *echo.Echo) {
|
||||||
a.DELETE("/tasks/:listtask", taskHandler.DeleteWeb)
|
a.DELETE("/tasks/:listtask", taskHandler.DeleteWeb)
|
||||||
a.POST("/tasks/:listtask", taskHandler.UpdateWeb)
|
a.POST("/tasks/:listtask", taskHandler.UpdateWeb)
|
||||||
|
|
||||||
listTeamHandler := &crud.WebHandler{
|
listTeamHandler := &handler.WebHandler{
|
||||||
EmptyStruct: func() crud.CObject {
|
EmptyStruct: func() handler.CObject {
|
||||||
return &models.TeamList{}
|
return &models.TeamList{}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -160,8 +176,8 @@ func RegisterRoutes(e *echo.Echo) {
|
||||||
a.DELETE("/lists/:list/teams/:team", listTeamHandler.DeleteWeb)
|
a.DELETE("/lists/:list/teams/:team", listTeamHandler.DeleteWeb)
|
||||||
a.POST("/lists/:list/teams/:team", listTeamHandler.UpdateWeb)
|
a.POST("/lists/:list/teams/:team", listTeamHandler.UpdateWeb)
|
||||||
|
|
||||||
listUserHandler := &crud.WebHandler{
|
listUserHandler := &handler.WebHandler{
|
||||||
EmptyStruct: func() crud.CObject {
|
EmptyStruct: func() handler.CObject {
|
||||||
return &models.ListUser{}
|
return &models.ListUser{}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -170,8 +186,8 @@ func RegisterRoutes(e *echo.Echo) {
|
||||||
a.DELETE("/lists/:list/users/:user", listUserHandler.DeleteWeb)
|
a.DELETE("/lists/:list/users/:user", listUserHandler.DeleteWeb)
|
||||||
a.POST("/lists/:list/users/:user", listUserHandler.UpdateWeb)
|
a.POST("/lists/:list/users/:user", listUserHandler.UpdateWeb)
|
||||||
|
|
||||||
namespaceHandler := &crud.WebHandler{
|
namespaceHandler := &handler.WebHandler{
|
||||||
EmptyStruct: func() crud.CObject {
|
EmptyStruct: func() handler.CObject {
|
||||||
return &models.Namespace{}
|
return &models.Namespace{}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -182,8 +198,8 @@ func RegisterRoutes(e *echo.Echo) {
|
||||||
a.DELETE("/namespaces/:namespace", namespaceHandler.DeleteWeb)
|
a.DELETE("/namespaces/:namespace", namespaceHandler.DeleteWeb)
|
||||||
a.GET("/namespaces/:namespace/lists", apiv1.GetListsByNamespaceID)
|
a.GET("/namespaces/:namespace/lists", apiv1.GetListsByNamespaceID)
|
||||||
|
|
||||||
namespaceTeamHandler := &crud.WebHandler{
|
namespaceTeamHandler := &handler.WebHandler{
|
||||||
EmptyStruct: func() crud.CObject {
|
EmptyStruct: func() handler.CObject {
|
||||||
return &models.TeamNamespace{}
|
return &models.TeamNamespace{}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -192,8 +208,8 @@ func RegisterRoutes(e *echo.Echo) {
|
||||||
a.DELETE("/namespaces/:namespace/teams/:team", namespaceTeamHandler.DeleteWeb)
|
a.DELETE("/namespaces/:namespace/teams/:team", namespaceTeamHandler.DeleteWeb)
|
||||||
a.POST("/namespaces/:namespace/teams/:team", namespaceTeamHandler.UpdateWeb)
|
a.POST("/namespaces/:namespace/teams/:team", namespaceTeamHandler.UpdateWeb)
|
||||||
|
|
||||||
namespaceUserHandler := &crud.WebHandler{
|
namespaceUserHandler := &handler.WebHandler{
|
||||||
EmptyStruct: func() crud.CObject {
|
EmptyStruct: func() handler.CObject {
|
||||||
return &models.NamespaceUser{}
|
return &models.NamespaceUser{}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -202,8 +218,8 @@ func RegisterRoutes(e *echo.Echo) {
|
||||||
a.DELETE("/namespaces/:namespace/users/:user", namespaceUserHandler.DeleteWeb)
|
a.DELETE("/namespaces/:namespace/users/:user", namespaceUserHandler.DeleteWeb)
|
||||||
a.POST("/namespaces/:namespace/users/:user", namespaceUserHandler.UpdateWeb)
|
a.POST("/namespaces/:namespace/users/:user", namespaceUserHandler.UpdateWeb)
|
||||||
|
|
||||||
teamHandler := &crud.WebHandler{
|
teamHandler := &handler.WebHandler{
|
||||||
EmptyStruct: func() crud.CObject {
|
EmptyStruct: func() handler.CObject {
|
||||||
return &models.Team{}
|
return &models.Team{}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -213,8 +229,8 @@ func RegisterRoutes(e *echo.Echo) {
|
||||||
a.POST("/teams/:team", teamHandler.UpdateWeb)
|
a.POST("/teams/:team", teamHandler.UpdateWeb)
|
||||||
a.DELETE("/teams/:team", teamHandler.DeleteWeb)
|
a.DELETE("/teams/:team", teamHandler.DeleteWeb)
|
||||||
|
|
||||||
teamMemberHandler := &crud.WebHandler{
|
teamMemberHandler := &handler.WebHandler{
|
||||||
EmptyStruct: func() crud.CObject {
|
EmptyStruct: func() handler.CObject {
|
||||||
return &models.TeamMember{}
|
return &models.TeamMember{}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
1
vendor/code.vikunja.io/web/.gitignore
generated
vendored
Normal file
1
vendor/code.vikunja.io/web/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
.idea/
|
16
vendor/code.vikunja.io/web/go.mod
generated
vendored
Normal file
16
vendor/code.vikunja.io/web/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
module code.vikunja.io/web
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/labstack/echo v3.3.5+incompatible
|
||||||
|
github.com/labstack/gommon v0.2.8 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.0.9 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.4 // indirect
|
||||||
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/stretchr/testify v1.2.2 // indirect
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
|
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 // indirect
|
||||||
|
)
|
24
vendor/code.vikunja.io/web/go.sum
generated
vendored
Normal file
24
vendor/code.vikunja.io/web/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/labstack/echo v3.3.5+incompatible h1:9PfxPUmasKzeJor9uQTaXLT6WUG/r+vSTmvXxvv3JO4=
|
||||||
|
github.com/labstack/echo v3.3.5+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
|
||||||
|
github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
|
||||||
|
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
|
||||||
|
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||||
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||||
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
||||||
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
|
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8=
|
||||||
|
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw=
|
||||||
|
golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 h1:et7+NAX3lLIk5qUCTA9QelBjGE/NkhzYw/mhnr0s7nI=
|
||||||
|
golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 h1:YAFjXN64LMvktoUZH9zgY4lGc/msGN7HQfoSuKCgaDU=
|
||||||
|
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
17
pkg/routes/crud/create.go → vendor/code.vikunja.io/web/handler/create.go
generated
vendored
17
pkg/routes/crud/create.go → vendor/code.vikunja.io/web/handler/create.go
generated
vendored
|
@ -14,12 +14,12 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package crud
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/web"
|
||||||
"code.vikunja.io/api/pkg/models"
|
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
|
"github.com/op/go-logging"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,21 +39,22 @@ func (c *WebHandler) CreateWeb(ctx echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the user to pass for later checks
|
// Get the user to pass for later checks
|
||||||
currentUser, err := models.GetCurrentUser(ctx)
|
authprovider := ctx.Get("AuthProvider").(*web.Auths)
|
||||||
|
currentAuth, err := authprovider.AuthObject(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
|
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check rights
|
// Check rights
|
||||||
if !currentStruct.CanCreate(¤tUser) {
|
if !currentStruct.CanCreate(currentAuth) {
|
||||||
log.Log.Noticef("%s [ID: %d] tried to create while not having the rights for it", currentUser.Username, currentUser.ID)
|
ctx.Get("LoggingProvider").(*logging.Logger).Noticef("Tried to create while not having the rights for it", currentAuth)
|
||||||
return echo.NewHTTPError(http.StatusForbidden)
|
return echo.NewHTTPError(http.StatusForbidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create
|
// Create
|
||||||
err = currentStruct.Create(¤tUser)
|
err = currentStruct.Create(currentAuth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return HandleHTTPError(err)
|
return HandleHTTPError(err, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.JSON(http.StatusCreated, currentStruct)
|
return ctx.JSON(http.StatusCreated, currentStruct)
|
21
pkg/routes/crud/delete.go → vendor/code.vikunja.io/web/handler/delete.go
generated
vendored
21
pkg/routes/crud/delete.go → vendor/code.vikunja.io/web/handler/delete.go
generated
vendored
|
@ -14,15 +14,19 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package crud
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/web"
|
||||||
"code.vikunja.io/api/pkg/models"
|
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
|
"github.com/op/go-logging"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type message struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteWeb is the web handler to delete something
|
// DeleteWeb is the web handler to delete something
|
||||||
func (c *WebHandler) DeleteWeb(ctx echo.Context) error {
|
func (c *WebHandler) DeleteWeb(ctx echo.Context) error {
|
||||||
|
|
||||||
|
@ -35,19 +39,20 @@ func (c *WebHandler) DeleteWeb(ctx echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the user has the right to delete
|
// Check if the user has the right to delete
|
||||||
currentUser, err := models.GetCurrentUser(ctx)
|
authprovider := ctx.Get("AuthProvider").(*web.Auths)
|
||||||
|
currentAuth, err := authprovider.AuthObject(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
if !currentStruct.CanDelete(¤tUser) {
|
if !currentStruct.CanDelete(currentAuth) {
|
||||||
log.Log.Noticef("%s [ID: %d] tried to delete while not having the rights for it", currentUser.Username, currentUser.ID)
|
ctx.Get("LoggingProvider").(*logging.Logger).Noticef("Tried to delete while not having the rights for it", currentAuth)
|
||||||
return echo.NewHTTPError(http.StatusForbidden)
|
return echo.NewHTTPError(http.StatusForbidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = currentStruct.Delete()
|
err = currentStruct.Delete()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return HandleHTTPError(err)
|
return HandleHTTPError(err, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.JSON(http.StatusOK, models.Message{"Successfully deleted."})
|
return ctx.JSON(http.StatusOK, message{"Successfully deleted."})
|
||||||
}
|
}
|
16
pkg/routes/crud/helper.go → vendor/code.vikunja.io/web/handler/helper.go
generated
vendored
16
pkg/routes/crud/helper.go → vendor/code.vikunja.io/web/handler/helper.go
generated
vendored
|
@ -14,12 +14,12 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package crud
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/web"
|
||||||
"code.vikunja.io/api/pkg/models"
|
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
|
"github.com/op/go-logging"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -31,16 +31,16 @@ type WebHandler struct {
|
||||||
|
|
||||||
// CObject is the definition of our object, holds the structs
|
// CObject is the definition of our object, holds the structs
|
||||||
type CObject interface {
|
type CObject interface {
|
||||||
models.CRUDable
|
web.CRUDable
|
||||||
models.Rights
|
web.Rights
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleHTTPError does what it says
|
// HandleHTTPError does what it says
|
||||||
func HandleHTTPError(err error) *echo.HTTPError {
|
func HandleHTTPError(err error, ctx echo.Context) *echo.HTTPError {
|
||||||
if a, has := err.(models.HTTPErrorProcessor); has {
|
if a, has := err.(web.HTTPErrorProcessor); has {
|
||||||
errDetails := a.HTTPError()
|
errDetails := a.HTTPError()
|
||||||
return echo.NewHTTPError(errDetails.HTTPCode, errDetails)
|
return echo.NewHTTPError(errDetails.HTTPCode, errDetails)
|
||||||
}
|
}
|
||||||
log.Log.Error(err.Error())
|
ctx.Get("LoggingProvider").(*logging.Logger).Error(err.Error())
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||||
}
|
}
|
2
pkg/routes/crud/paramBinder.go → vendor/code.vikunja.io/web/handler/paramBinder.go
generated
vendored
2
pkg/routes/crud/paramBinder.go → vendor/code.vikunja.io/web/handler/paramBinder.go
generated
vendored
|
@ -14,7 +14,7 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package crud
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
15
pkg/routes/crud/read_all.go → vendor/code.vikunja.io/web/handler/read_all.go
generated
vendored
15
pkg/routes/crud/read_all.go → vendor/code.vikunja.io/web/handler/read_all.go
generated
vendored
|
@ -14,12 +14,12 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package crud
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/web"
|
||||||
"code.vikunja.io/api/pkg/models"
|
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
|
"github.com/op/go-logging"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
@ -29,7 +29,8 @@ func (c *WebHandler) ReadAllWeb(ctx echo.Context) error {
|
||||||
// Get our model
|
// Get our model
|
||||||
currentStruct := c.EmptyStruct()
|
currentStruct := c.EmptyStruct()
|
||||||
|
|
||||||
currentUser, err := models.GetCurrentUser(ctx)
|
authprovider := ctx.Get("AuthProvider").(*web.Auths)
|
||||||
|
currentAuth, err := authprovider.AuthObject(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
|
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
|
||||||
}
|
}
|
||||||
|
@ -46,7 +47,7 @@ func (c *WebHandler) ReadAllWeb(ctx echo.Context) error {
|
||||||
}
|
}
|
||||||
pageNumber, err := strconv.Atoi(page)
|
pageNumber, err := strconv.Atoi(page)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error(err.Error())
|
ctx.Get("LoggingProvider").(*logging.Logger).Error(err.Error())
|
||||||
return echo.NewHTTPError(http.StatusBadRequest, "Bad page requested.")
|
return echo.NewHTTPError(http.StatusBadRequest, "Bad page requested.")
|
||||||
}
|
}
|
||||||
if pageNumber < 0 {
|
if pageNumber < 0 {
|
||||||
|
@ -56,9 +57,9 @@ func (c *WebHandler) ReadAllWeb(ctx echo.Context) error {
|
||||||
// Search
|
// Search
|
||||||
search := ctx.QueryParam("s")
|
search := ctx.QueryParam("s")
|
||||||
|
|
||||||
lists, err := currentStruct.ReadAll(search, ¤tUser, pageNumber)
|
lists, err := currentStruct.ReadAll(search, currentAuth, pageNumber)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return HandleHTTPError(err)
|
return HandleHTTPError(err, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.JSON(http.StatusOK, lists)
|
return ctx.JSON(http.StatusOK, lists)
|
15
pkg/routes/crud/read_one.go → vendor/code.vikunja.io/web/handler/read_one.go
generated
vendored
15
pkg/routes/crud/read_one.go → vendor/code.vikunja.io/web/handler/read_one.go
generated
vendored
|
@ -14,12 +14,12 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package crud
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/web"
|
||||||
"code.vikunja.io/api/pkg/models"
|
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
|
"github.com/op/go-logging"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,17 +36,18 @@ func (c *WebHandler) ReadOneWeb(ctx echo.Context) error {
|
||||||
// Get our object
|
// Get our object
|
||||||
err := currentStruct.ReadOne()
|
err := currentStruct.ReadOne()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return HandleHTTPError(err)
|
return HandleHTTPError(err, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check rights
|
// Check rights
|
||||||
// We can only check the rights on a full object, which is why we need to check it afterwards
|
// We can only check the rights on a full object, which is why we need to check it afterwards
|
||||||
currentUser, err := models.GetCurrentUser(ctx)
|
authprovider := ctx.Get("AuthProvider").(*web.Auths)
|
||||||
|
currentAuth, err := authprovider.AuthObject(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
|
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
|
||||||
}
|
}
|
||||||
if !currentStruct.CanRead(¤tUser) {
|
if !currentStruct.CanRead(currentAuth) {
|
||||||
log.Log.Noticef("%s [ID: %d] tried to read while not having the rights for it", currentUser.Username, currentUser.ID)
|
ctx.Get("LoggingProvider").(*logging.Logger).Noticef("Tried to read one while not having the rights for it", currentAuth)
|
||||||
return echo.NewHTTPError(http.StatusForbidden, "You don't have the right to see this")
|
return echo.NewHTTPError(http.StatusForbidden, "You don't have the right to see this")
|
||||||
}
|
}
|
||||||
|
|
15
pkg/routes/crud/update.go → vendor/code.vikunja.io/web/handler/update.go
generated
vendored
15
pkg/routes/crud/update.go → vendor/code.vikunja.io/web/handler/update.go
generated
vendored
|
@ -14,12 +14,12 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package crud
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/web"
|
||||||
"code.vikunja.io/api/pkg/models"
|
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
|
"github.com/op/go-logging"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -40,19 +40,20 @@ func (c *WebHandler) UpdateWeb(ctx echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the user has the right to do that
|
// Check if the user has the right to do that
|
||||||
currentUser, err := models.GetCurrentUser(ctx)
|
authprovider := ctx.Get("AuthProvider").(*web.Auths)
|
||||||
|
currentAuth, err := authprovider.AuthObject(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
|
return echo.NewHTTPError(http.StatusInternalServerError, "Could not determine the current user.")
|
||||||
}
|
}
|
||||||
if !currentStruct.CanUpdate(¤tUser) {
|
if !currentStruct.CanUpdate(currentAuth) {
|
||||||
log.Log.Noticef("%s [ID: %d] tried to update while not having the rights for it", currentUser.Username, currentUser.ID)
|
ctx.Get("LoggingProvider").(*logging.Logger).Noticef("Tried to update while not having the rights for it", currentAuth)
|
||||||
return echo.NewHTTPError(http.StatusForbidden)
|
return echo.NewHTTPError(http.StatusForbidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do the update
|
// Do the update
|
||||||
err = currentStruct.Update()
|
err = currentStruct.Update()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return HandleHTTPError(err)
|
return HandleHTTPError(err, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.JSON(http.StatusOK, currentStruct)
|
return ctx.JSON(http.StatusOK, currentStruct)
|
65
vendor/code.vikunja.io/web/web.go
generated
vendored
Normal file
65
vendor/code.vikunja.io/web/web.go
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
// Vikunja is a todo-list application to facilitate your life.
|
||||||
|
// Copyright 2018 Vikunja and contributors. All rights reserved.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package web
|
||||||
|
|
||||||
|
import "github.com/labstack/echo"
|
||||||
|
|
||||||
|
// Rights defines rights methods
|
||||||
|
type Rights interface {
|
||||||
|
IsAdmin(Auth) bool
|
||||||
|
CanWrite(Auth) bool
|
||||||
|
CanRead(Auth) bool
|
||||||
|
CanDelete(Auth) bool
|
||||||
|
CanUpdate(Auth) bool
|
||||||
|
CanCreate(Auth) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRUDable defines the crud methods
|
||||||
|
type CRUDable interface {
|
||||||
|
Create(Auth) error
|
||||||
|
ReadOne() error
|
||||||
|
ReadAll(string, Auth, int) (interface{}, error)
|
||||||
|
Update() error
|
||||||
|
Delete() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPErrorProcessor is executed when the defined error is thrown, it will make sure the user sees an appropriate error message and http status code
|
||||||
|
type HTTPErrorProcessor interface {
|
||||||
|
HTTPError() HTTPError
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPError holds informations about an http error
|
||||||
|
type HTTPError struct {
|
||||||
|
HTTPCode int `json:"-"`
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auth defines the authentication interface used to get some auth thing
|
||||||
|
type Auth interface {
|
||||||
|
AuthDummy()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authprovider is a holder for the implementation of an authprovider by the application
|
||||||
|
type Authprovider interface {
|
||||||
|
GetAuthObject(echo.Context) (Auth, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auths holds the authobject
|
||||||
|
type Auths struct {
|
||||||
|
AuthObject func(echo.Context) (Auth, error)
|
||||||
|
}
|
1
vendor/github.com/labstack/echo/.travis.yml
generated
vendored
1
vendor/github.com/labstack/echo/.travis.yml
generated
vendored
|
@ -2,7 +2,6 @@ language: go
|
||||||
go:
|
go:
|
||||||
- 1.9.x
|
- 1.9.x
|
||||||
- 1.10.x
|
- 1.10.x
|
||||||
- 1.11.x
|
|
||||||
- tip
|
- tip
|
||||||
install:
|
install:
|
||||||
- make dependency
|
- make dependency
|
||||||
|
|
77
vendor/github.com/labstack/echo/Gopkg.lock
generated
vendored
77
vendor/github.com/labstack/echo/Gopkg.lock
generated
vendored
|
@ -2,113 +2,74 @@
|
||||||
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
|
|
||||||
name = "github.com/davecgh/go-spew"
|
name = "github.com/davecgh/go-spew"
|
||||||
packages = ["spew"]
|
packages = ["spew"]
|
||||||
pruneopts = "UT"
|
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||||
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
|
version = "v1.1.0"
|
||||||
version = "v1.1.1"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:76dc72490af7174349349838f2fe118996381b31ea83243812a97e5a0fd5ed55"
|
|
||||||
name = "github.com/dgrijalva/jwt-go"
|
name = "github.com/dgrijalva/jwt-go"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
|
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
|
||||||
version = "v3.2.0"
|
version = "v3.2.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:568171fc14a3d819b112c3e219d351ea7b05e8dad7935c4168c6b3373244a686"
|
|
||||||
name = "github.com/labstack/gommon"
|
name = "github.com/labstack/gommon"
|
||||||
packages = [
|
packages = ["bytes","color","log","random"]
|
||||||
"bytes",
|
revision = "6fe1405d73ec4bd4cd8a4ac8e2a2b2bf95d03954"
|
||||||
"color",
|
version = "0.2.4"
|
||||||
"log",
|
|
||||||
"random",
|
|
||||||
]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "2a618302b929cc20862dda3aa6f02f64dbe740dd"
|
|
||||||
version = "v0.2.7"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67"
|
|
||||||
name = "github.com/mattn/go-colorable"
|
name = "github.com/mattn/go-colorable"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
|
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
|
||||||
version = "v0.0.9"
|
version = "v0.0.9"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:0981502f9816113c9c8c4ac301583841855c8cf4da8c72f696b3ebedf6d0e4e5"
|
|
||||||
name = "github.com/mattn/go-isatty"
|
name = "github.com/mattn/go-isatty"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
|
||||||
revision = "6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c"
|
version = "v0.0.3"
|
||||||
version = "v0.0.4"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
|
|
||||||
name = "github.com/pmezard/go-difflib"
|
name = "github.com/pmezard/go-difflib"
|
||||||
packages = ["difflib"]
|
packages = ["difflib"]
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||||
version = "v1.0.0"
|
version = "v1.0.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:18752d0b95816a1b777505a97f71c7467a8445b8ffb55631a7bf779f6ba4fa83"
|
|
||||||
name = "github.com/stretchr/testify"
|
name = "github.com/stretchr/testify"
|
||||||
packages = ["assert"]
|
packages = ["assert"]
|
||||||
pruneopts = "UT"
|
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
|
||||||
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
|
version = "v1.2.1"
|
||||||
version = "v1.2.2"
|
|
||||||
|
[[projects]]
|
||||||
[[projects]]
|
branch = "master"
|
||||||
digest = "1:c468422f334a6b46a19448ad59aaffdfc0a36b08fdcc1c749a0b29b6453d7e59"
|
name = "github.com/valyala/bytebufferpool"
|
||||||
name = "github.com/valyala/bytebufferpool"
|
packages = ["."]
|
||||||
packages = ["."]
|
revision = "e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7"
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7"
|
|
||||||
version = "v1.0.0"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:268b8bce0064e8c057d7b913605459f9a26dcab864c0886a56d196540fbf003f"
|
|
||||||
name = "github.com/valyala/fasttemplate"
|
name = "github.com/valyala/fasttemplate"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "dcecefd839c4193db0d35b88ec65b4c12d360ab0"
|
revision = "dcecefd839c4193db0d35b88ec65b4c12d360ab0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:dedf20eb0d3e8d6aa8a4d3d2fae248222b688ed528201995e152cc497899123c"
|
|
||||||
name = "golang.org/x/crypto"
|
name = "golang.org/x/crypto"
|
||||||
packages = [
|
packages = ["acme","acme/autocert"]
|
||||||
"acme",
|
revision = "182114d582623c1caa54f73de9c7224e23a48487"
|
||||||
"acme/autocert",
|
|
||||||
]
|
|
||||||
pruneopts = "UT"
|
|
||||||
revision = "0e37d006457bf46f9e6692014ba72ef82c33022c"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:6eb2645d74b43d9c87b51947df39f7c668a4f422cd512053f7f6f75bfaad0197"
|
|
||||||
name = "golang.org/x/sys"
|
name = "golang.org/x/sys"
|
||||||
packages = ["unix"]
|
packages = ["unix"]
|
||||||
pruneopts = "UT"
|
revision = "c28acc882ebcbfbe8ce9f0f14b9ac26ee138dd51"
|
||||||
revision = "d0be0721c37eeb5299f245a996a483160fc36940"
|
|
||||||
|
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
input-imports = [
|
inputs-digest = "9c7b45e80fe353405800cf01f429b3a203cfb8d4468a04c64a908e11a98ea764"
|
||||||
"github.com/dgrijalva/jwt-go",
|
|
||||||
"github.com/labstack/gommon/bytes",
|
|
||||||
"github.com/labstack/gommon/color",
|
|
||||||
"github.com/labstack/gommon/log",
|
|
||||||
"github.com/labstack/gommon/random",
|
|
||||||
"github.com/stretchr/testify/assert",
|
|
||||||
"github.com/valyala/fasttemplate",
|
|
||||||
"golang.org/x/crypto/acme/autocert",
|
|
||||||
]
|
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|
20
vendor/github.com/labstack/echo/Gopkg.toml
generated
vendored
20
vendor/github.com/labstack/echo/Gopkg.toml
generated
vendored
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
# Gopkg.toml example
|
# Gopkg.toml example
|
||||||
#
|
#
|
||||||
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
|
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||||
# for detailed Gopkg.toml documentation.
|
# for detailed Gopkg.toml documentation.
|
||||||
#
|
#
|
||||||
# required = ["github.com/user/thing/cmd/thing"]
|
# required = ["github.com/user/thing/cmd/thing"]
|
||||||
|
@ -16,13 +17,8 @@
|
||||||
# source = "github.com/myfork/project2"
|
# source = "github.com/myfork/project2"
|
||||||
#
|
#
|
||||||
# [[override]]
|
# [[override]]
|
||||||
# name = "github.com/x/y"
|
# name = "github.com/x/y"
|
||||||
# version = "2.4.0"
|
# version = "2.4.0"
|
||||||
#
|
|
||||||
# [prune]
|
|
||||||
# non-go = false
|
|
||||||
# go-tests = true
|
|
||||||
# unused-packages = true
|
|
||||||
|
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
|
@ -31,11 +27,11 @@
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/labstack/gommon"
|
name = "github.com/labstack/gommon"
|
||||||
version = "0.2.7"
|
version = "0.2.4"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/stretchr/testify"
|
name = "github.com/stretchr/testify"
|
||||||
version = "1.2.2"
|
version = "1.2.1"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
@ -44,7 +40,3 @@
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/crypto"
|
name = "golang.org/x/crypto"
|
||||||
|
|
||||||
[prune]
|
|
||||||
go-tests = true
|
|
||||||
unused-packages = true
|
|
||||||
|
|
2
vendor/github.com/labstack/echo/README.md
generated
vendored
2
vendor/github.com/labstack/echo/README.md
generated
vendored
|
@ -32,7 +32,7 @@ Date: 2018/03/15<br>
|
||||||
Source: https://github.com/vishr/web-framework-benchmark<br>
|
Source: https://github.com/vishr/web-framework-benchmark<br>
|
||||||
Lower is better!
|
Lower is better!
|
||||||
|
|
||||||
<img src="https://i.imgur.com/I32VdMJ.png">
|
<img src="https://api.labstack.com/chart/bar?values=37223,55382,2985,5265|42013,59865,3350,6424&labels=Static,GitHub%20API,Parse%20API,Gplus%20API&titles=Echo,Gin&colors=lightseagreen,goldenrod&x_title=Routes&y_title=ns/op">
|
||||||
|
|
||||||
## [Guide](https://echo.labstack.com/guide)
|
## [Guide](https://echo.labstack.com/guide)
|
||||||
|
|
||||||
|
|
34
vendor/github.com/labstack/echo/bind.go
generated
vendored
34
vendor/github.com/labstack/echo/bind.go
generated
vendored
|
@ -44,11 +44,12 @@ func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) {
|
||||||
case strings.HasPrefix(ctype, MIMEApplicationJSON):
|
case strings.HasPrefix(ctype, MIMEApplicationJSON):
|
||||||
if err = json.NewDecoder(req.Body).Decode(i); err != nil {
|
if err = json.NewDecoder(req.Body).Decode(i); err != nil {
|
||||||
if ute, ok := err.(*json.UnmarshalTypeError); ok {
|
if ute, ok := err.(*json.UnmarshalTypeError); ok {
|
||||||
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unmarshal type error: expected=%v, got=%v, field=%v, offset=%v", ute.Type, ute.Value, ute.Field, ute.Offset))
|
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unmarshal type error: expected=%v, got=%v, offset=%v", ute.Type, ute.Value, ute.Offset))
|
||||||
} else if se, ok := err.(*json.SyntaxError); ok {
|
} else if se, ok := err.(*json.SyntaxError); ok {
|
||||||
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: offset=%v, error=%v", se.Offset, se.Error()))
|
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: offset=%v, error=%v", se.Offset, se.Error()))
|
||||||
|
} else {
|
||||||
|
return NewHTTPError(http.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
return NewHTTPError(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
}
|
||||||
case strings.HasPrefix(ctype, MIMEApplicationXML), strings.HasPrefix(ctype, MIMETextXML):
|
case strings.HasPrefix(ctype, MIMEApplicationXML), strings.HasPrefix(ctype, MIMETextXML):
|
||||||
if err = xml.NewDecoder(req.Body).Decode(i); err != nil {
|
if err = xml.NewDecoder(req.Body).Decode(i); err != nil {
|
||||||
|
@ -56,8 +57,9 @@ func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) {
|
||||||
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unsupported type error: type=%v, error=%v", ute.Type, ute.Error()))
|
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unsupported type error: type=%v, error=%v", ute.Type, ute.Error()))
|
||||||
} else if se, ok := err.(*xml.SyntaxError); ok {
|
} else if se, ok := err.(*xml.SyntaxError); ok {
|
||||||
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: line=%v, error=%v", se.Line, se.Error()))
|
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: line=%v, error=%v", se.Line, se.Error()))
|
||||||
|
} else {
|
||||||
|
return NewHTTPError(http.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
return NewHTTPError(http.StatusBadRequest, err.Error())
|
|
||||||
}
|
}
|
||||||
case strings.HasPrefix(ctype, MIMEApplicationForm), strings.HasPrefix(ctype, MIMEMultipartForm):
|
case strings.HasPrefix(ctype, MIMEApplicationForm), strings.HasPrefix(ctype, MIMEMultipartForm):
|
||||||
params, err := c.FormParams()
|
params, err := c.FormParams()
|
||||||
|
@ -94,29 +96,14 @@ func (b *DefaultBinder) bindData(ptr interface{}, data map[string][]string, tag
|
||||||
inputFieldName = typeField.Name
|
inputFieldName = typeField.Name
|
||||||
// If tag is nil, we inspect if the field is a struct.
|
// If tag is nil, we inspect if the field is a struct.
|
||||||
if _, ok := bindUnmarshaler(structField); !ok && structFieldKind == reflect.Struct {
|
if _, ok := bindUnmarshaler(structField); !ok && structFieldKind == reflect.Struct {
|
||||||
if err := b.bindData(structField.Addr().Interface(), data, tag); err != nil {
|
err := b.bindData(structField.Addr().Interface(), data, tag)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inputValue, exists := data[inputFieldName]
|
inputValue, exists := data[inputFieldName]
|
||||||
if !exists {
|
|
||||||
// Go json.Unmarshal supports case insensitive binding. However the
|
|
||||||
// url params are bound case sensitive which is inconsistent. To
|
|
||||||
// fix this we must check all of the map values in a
|
|
||||||
// case-insensitive search.
|
|
||||||
inputFieldName = strings.ToLower(inputFieldName)
|
|
||||||
for k, v := range data {
|
|
||||||
if strings.ToLower(k) == inputFieldName {
|
|
||||||
inputValue = v
|
|
||||||
exists = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -139,9 +126,10 @@ func (b *DefaultBinder) bindData(ptr interface{}, data map[string][]string, tag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val.Field(i).Set(slice)
|
val.Field(i).Set(slice)
|
||||||
} else if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
|
} else {
|
||||||
return err
|
if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
27
vendor/github.com/labstack/echo/echo.go
generated
vendored
27
vendor/github.com/labstack/echo/echo.go
generated
vendored
|
@ -103,7 +103,7 @@ type (
|
||||||
// MiddlewareFunc defines a function to process middleware.
|
// MiddlewareFunc defines a function to process middleware.
|
||||||
MiddlewareFunc func(HandlerFunc) HandlerFunc
|
MiddlewareFunc func(HandlerFunc) HandlerFunc
|
||||||
|
|
||||||
// HandlerFunc defines a function to serve HTTP requests.
|
// HandlerFunc defines a function to server HTTP requests.
|
||||||
HandlerFunc func(Context) error
|
HandlerFunc func(Context) error
|
||||||
|
|
||||||
// HTTPErrorHandler is a centralized HTTP error handler.
|
// HTTPErrorHandler is a centralized HTTP error handler.
|
||||||
|
@ -217,7 +217,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "3.3.6"
|
Version = "3.3.5"
|
||||||
website = "https://echo.labstack.com"
|
website = "https://echo.labstack.com"
|
||||||
// http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo
|
// http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo
|
||||||
banner = `
|
banner = `
|
||||||
|
@ -326,7 +326,7 @@ func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) {
|
||||||
code = he.Code
|
code = he.Code
|
||||||
msg = he.Message
|
msg = he.Message
|
||||||
if he.Internal != nil {
|
if he.Internal != nil {
|
||||||
err = fmt.Errorf("%v, %v", err, he.Internal)
|
msg = fmt.Sprintf("%v, %v", err, he.Internal)
|
||||||
}
|
}
|
||||||
} else if e.Debug {
|
} else if e.Debug {
|
||||||
msg = err.Error()
|
msg = err.Error()
|
||||||
|
@ -337,6 +337,8 @@ func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) {
|
||||||
msg = Map{"message": msg}
|
msg = Map{"message": msg}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e.Logger.Error(err)
|
||||||
|
|
||||||
// Send response
|
// Send response
|
||||||
if !c.Response().Committed {
|
if !c.Response().Committed {
|
||||||
if c.Request().Method == HEAD { // Issue #608
|
if c.Request().Method == HEAD { // Issue #608
|
||||||
|
@ -460,11 +462,11 @@ func static(i i, prefix, root string) *Route {
|
||||||
return i.GET(prefix+"/*", h)
|
return i.GET(prefix+"/*", h)
|
||||||
}
|
}
|
||||||
|
|
||||||
// File registers a new route with path to serve a static file with optional route-level middleware.
|
// File registers a new route with path to serve a static file.
|
||||||
func (e *Echo) File(path, file string, m ...MiddlewareFunc) *Route {
|
func (e *Echo) File(path, file string) *Route {
|
||||||
return e.GET(path, func(c Context) error {
|
return e.GET(path, func(c Context) error {
|
||||||
return c.File(file)
|
return c.File(file)
|
||||||
}, m...)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add registers a new route for an HTTP method and path with matching handler
|
// Add registers a new route for an HTTP method and path with matching handler
|
||||||
|
@ -557,17 +559,26 @@ func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
c := e.pool.Get().(*context)
|
c := e.pool.Get().(*context)
|
||||||
c.Reset(r, w)
|
c.Reset(r, w)
|
||||||
|
|
||||||
|
m := r.Method
|
||||||
h := NotFoundHandler
|
h := NotFoundHandler
|
||||||
|
|
||||||
if e.premiddleware == nil {
|
if e.premiddleware == nil {
|
||||||
e.router.Find(r.Method, getPath(r), c)
|
path := r.URL.RawPath
|
||||||
|
if path == "" {
|
||||||
|
path = r.URL.Path
|
||||||
|
}
|
||||||
|
e.router.Find(m, getPath(r), c)
|
||||||
h = c.Handler()
|
h = c.Handler()
|
||||||
for i := len(e.middleware) - 1; i >= 0; i-- {
|
for i := len(e.middleware) - 1; i >= 0; i-- {
|
||||||
h = e.middleware[i](h)
|
h = e.middleware[i](h)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
h = func(c Context) error {
|
h = func(c Context) error {
|
||||||
e.router.Find(r.Method, getPath(r), c)
|
path := r.URL.RawPath
|
||||||
|
if path == "" {
|
||||||
|
path = r.URL.Path
|
||||||
|
}
|
||||||
|
e.router.Find(m, getPath(r), c)
|
||||||
h := c.Handler()
|
h := c.Handler()
|
||||||
for i := len(e.middleware) - 1; i >= 0; i-- {
|
for i := len(e.middleware) - 1; i >= 0; i-- {
|
||||||
h = e.middleware[i](h)
|
h = e.middleware[i](h)
|
||||||
|
|
15
vendor/github.com/labstack/echo/go.mod
generated
vendored
15
vendor/github.com/labstack/echo/go.mod
generated
vendored
|
@ -1,15 +0,0 @@
|
||||||
module github.com/labstack/echo
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/davecgh/go-spew v1.1.0 // indirect
|
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
|
||||||
github.com/labstack/gommon v0.0.0-20180312174116-6fe1405d73ec
|
|
||||||
github.com/mattn/go-colorable v0.0.9 // indirect
|
|
||||||
github.com/mattn/go-isatty v0.0.3 // indirect
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
|
||||||
github.com/stretchr/testify v1.2.1
|
|
||||||
github.com/valyala/bytebufferpool v0.0.0-20160817181652-e746df99fe4a // indirect
|
|
||||||
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4
|
|
||||||
golang.org/x/crypto v0.0.0-20180312195533-182114d58262
|
|
||||||
golang.org/x/sys v0.0.0-20180312081825-c28acc882ebc // indirect
|
|
||||||
)
|
|
22
vendor/github.com/labstack/echo/go.sum
generated
vendored
22
vendor/github.com/labstack/echo/go.sum
generated
vendored
|
@ -1,22 +0,0 @@
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
|
||||||
github.com/labstack/gommon v0.0.0-20180312174116-6fe1405d73ec h1:aYKwS4iCpqxskMuvI8+Byq0CxnnWHO/xuLk2pZJ96tY=
|
|
||||||
github.com/labstack/gommon v0.0.0-20180312174116-6fe1405d73ec/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
|
|
||||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
|
||||||
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
|
|
||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/stretchr/testify v1.2.1 h1:52QO5WkIUcHGIR7EnGagH88x1bUzqGXTC5/1bDTUQ7U=
|
|
||||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/valyala/bytebufferpool v0.0.0-20160817181652-e746df99fe4a h1:AOcehBWpFhYPYw0ioDTppQzgI8pAAahVCiMSKTp9rbo=
|
|
||||||
github.com/valyala/bytebufferpool v0.0.0-20160817181652-e746df99fe4a/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
|
||||||
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8=
|
|
||||||
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw=
|
|
||||||
golang.org/x/crypto v0.0.0-20180312195533-182114d58262 h1:1NLVUmR8SQ7cNNA5Vo7ronpXbR+5A+9IwIC/bLE7D8Y=
|
|
||||||
golang.org/x/crypto v0.0.0-20180312195533-182114d58262/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
|
||||||
golang.org/x/sys v0.0.0-20180312081825-c28acc882ebc h1:eCzBKjjvhDDXMoH5l7eA+YK1PEtyJ2QLj3f4IArr+zg=
|
|
||||||
golang.org/x/sys v0.0.0-20180312081825-c28acc882ebc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
4
vendor/github.com/labstack/echo/middleware/cors.go
generated
vendored
4
vendor/github.com/labstack/echo/middleware/cors.go
generated
vendored
|
@ -94,10 +94,6 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
|
||||||
|
|
||||||
// Check allowed origins
|
// Check allowed origins
|
||||||
for _, o := range config.AllowOrigins {
|
for _, o := range config.AllowOrigins {
|
||||||
if o == "*" && config.AllowCredentials {
|
|
||||||
allowOrigin = origin
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if o == "*" || o == origin {
|
if o == "*" || o == origin {
|
||||||
allowOrigin = o
|
allowOrigin = o
|
||||||
break
|
break
|
||||||
|
|
34
vendor/github.com/labstack/echo/middleware/jwt.go
generated
vendored
34
vendor/github.com/labstack/echo/middleware/jwt.go
generated
vendored
|
@ -16,16 +16,6 @@ type (
|
||||||
// Skipper defines a function to skip middleware.
|
// Skipper defines a function to skip middleware.
|
||||||
Skipper Skipper
|
Skipper Skipper
|
||||||
|
|
||||||
// BeforeFunc defines a function which is executed just before the middleware.
|
|
||||||
BeforeFunc BeforeFunc
|
|
||||||
|
|
||||||
// SuccessHandler defines a function which is executed for a valid token.
|
|
||||||
SuccessHandler JWTSuccessHandler
|
|
||||||
|
|
||||||
// ErrorHandler defines a function which is executed for an invalid token.
|
|
||||||
// It may be used to define a custom JWT error.
|
|
||||||
ErrorHandler JWTErrorHandler
|
|
||||||
|
|
||||||
// Signing key to validate token.
|
// Signing key to validate token.
|
||||||
// Required.
|
// Required.
|
||||||
SigningKey interface{}
|
SigningKey interface{}
|
||||||
|
@ -58,12 +48,6 @@ type (
|
||||||
keyFunc jwt.Keyfunc
|
keyFunc jwt.Keyfunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// JWTSuccessHandler defines a function which is executed for a valid token.
|
|
||||||
JWTSuccessHandler func(echo.Context)
|
|
||||||
|
|
||||||
// JWTErrorHandler defines a function which is executed for an invalid token.
|
|
||||||
JWTErrorHandler func(error) error
|
|
||||||
|
|
||||||
jwtExtractor func(echo.Context) (string, error)
|
jwtExtractor func(echo.Context) (string, error)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -75,6 +59,7 @@ const (
|
||||||
// Errors
|
// Errors
|
||||||
var (
|
var (
|
||||||
ErrJWTMissing = echo.NewHTTPError(http.StatusBadRequest, "missing or malformed jwt")
|
ErrJWTMissing = echo.NewHTTPError(http.StatusBadRequest, "missing or malformed jwt")
|
||||||
|
ErrJWTInvalid = echo.NewHTTPError(http.StatusUnauthorized, "invalid or expired jwt")
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -152,15 +137,8 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
|
||||||
return next(c)
|
return next(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.BeforeFunc != nil {
|
|
||||||
config.BeforeFunc(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
auth, err := extractor(c)
|
auth, err := extractor(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if config.ErrorHandler != nil {
|
|
||||||
return config.ErrorHandler(err)
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
token := new(jwt.Token)
|
token := new(jwt.Token)
|
||||||
|
@ -175,17 +153,11 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
|
||||||
if err == nil && token.Valid {
|
if err == nil && token.Valid {
|
||||||
// Store user information from token into context.
|
// Store user information from token into context.
|
||||||
c.Set(config.ContextKey, token)
|
c.Set(config.ContextKey, token)
|
||||||
if config.SuccessHandler != nil {
|
|
||||||
config.SuccessHandler(c)
|
|
||||||
}
|
|
||||||
return next(c)
|
return next(c)
|
||||||
}
|
}
|
||||||
if config.ErrorHandler != nil {
|
|
||||||
return config.ErrorHandler(err)
|
|
||||||
}
|
|
||||||
return &echo.HTTPError{
|
return &echo.HTTPError{
|
||||||
Code: http.StatusUnauthorized,
|
Code: ErrJWTInvalid.Code,
|
||||||
Message: "invalid or expired jwt",
|
Message: ErrJWTInvalid.Message,
|
||||||
Internal: err,
|
Internal: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
10
vendor/github.com/labstack/echo/middleware/logger.go
generated
vendored
10
vendor/github.com/labstack/echo/middleware/logger.go
generated
vendored
|
@ -33,11 +33,9 @@ type (
|
||||||
// - host
|
// - host
|
||||||
// - method
|
// - method
|
||||||
// - path
|
// - path
|
||||||
// - protocol
|
|
||||||
// - referer
|
// - referer
|
||||||
// - user_agent
|
// - user_agent
|
||||||
// - status
|
// - status
|
||||||
// - error
|
|
||||||
// - latency (In nanoseconds)
|
// - latency (In nanoseconds)
|
||||||
// - latency_human (Human readable)
|
// - latency_human (Human readable)
|
||||||
// - bytes_in (Bytes received)
|
// - bytes_in (Bytes received)
|
||||||
|
@ -69,7 +67,7 @@ var (
|
||||||
DefaultLoggerConfig = LoggerConfig{
|
DefaultLoggerConfig = LoggerConfig{
|
||||||
Skipper: DefaultSkipper,
|
Skipper: DefaultSkipper,
|
||||||
Format: `{"time":"${time_rfc3339_nano}","id":"${id}","remote_ip":"${remote_ip}","host":"${host}",` +
|
Format: `{"time":"${time_rfc3339_nano}","id":"${id}","remote_ip":"${remote_ip}","host":"${host}",` +
|
||||||
`"method":"${method}","uri":"${uri}","status":${status},"error":"${error}","latency":${latency},` +
|
`"method":"${method}","uri":"${uri}","status":${status}, "latency":${latency},` +
|
||||||
`"latency_human":"${latency_human}","bytes_in":${bytes_in},` +
|
`"latency_human":"${latency_human}","bytes_in":${bytes_in},` +
|
||||||
`"bytes_out":${bytes_out}}` + "\n",
|
`"bytes_out":${bytes_out}}` + "\n",
|
||||||
CustomTimeFormat: "2006-01-02 15:04:05.00000",
|
CustomTimeFormat: "2006-01-02 15:04:05.00000",
|
||||||
|
@ -155,8 +153,6 @@ func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc {
|
||||||
p = "/"
|
p = "/"
|
||||||
}
|
}
|
||||||
return buf.WriteString(p)
|
return buf.WriteString(p)
|
||||||
case "protocol":
|
|
||||||
return buf.WriteString(req.Proto)
|
|
||||||
case "referer":
|
case "referer":
|
||||||
return buf.WriteString(req.Referer())
|
return buf.WriteString(req.Referer())
|
||||||
case "user_agent":
|
case "user_agent":
|
||||||
|
@ -173,10 +169,6 @@ func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc {
|
||||||
s = config.colorer.Cyan(n)
|
s = config.colorer.Cyan(n)
|
||||||
}
|
}
|
||||||
return buf.WriteString(s)
|
return buf.WriteString(s)
|
||||||
case "error":
|
|
||||||
if err != nil {
|
|
||||||
return buf.WriteString(err.Error())
|
|
||||||
}
|
|
||||||
case "latency":
|
case "latency":
|
||||||
l := stop.Sub(start)
|
l := stop.Sub(start)
|
||||||
return buf.WriteString(strconv.FormatInt(int64(l), 10))
|
return buf.WriteString(strconv.FormatInt(int64(l), 10))
|
||||||
|
|
5
vendor/github.com/labstack/echo/middleware/middleware.go
generated
vendored
5
vendor/github.com/labstack/echo/middleware/middleware.go
generated
vendored
|
@ -11,10 +11,7 @@ import (
|
||||||
type (
|
type (
|
||||||
// Skipper defines a function to skip middleware. Returning true skips processing
|
// Skipper defines a function to skip middleware. Returning true skips processing
|
||||||
// the middleware.
|
// the middleware.
|
||||||
Skipper func(echo.Context) bool
|
Skipper func(c echo.Context) bool
|
||||||
|
|
||||||
// BeforeFunc defines a function which is executed just before the middleware.
|
|
||||||
BeforeFunc func(echo.Context)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func captureTokens(pattern *regexp.Regexp, input string) *strings.Replacer {
|
func captureTokens(pattern *regexp.Regexp, input string) *strings.Replacer {
|
||||||
|
|
5
vendor/github.com/labstack/echo/middleware/rewrite.go
generated
vendored
5
vendor/github.com/labstack/echo/middleware/rewrite.go
generated
vendored
|
@ -57,8 +57,7 @@ func RewriteWithConfig(config RewriteConfig) echo.MiddlewareFunc {
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
for k, v := range config.Rules {
|
for k, v := range config.Rules {
|
||||||
k = strings.Replace(k, "*", "(.*)", -1)
|
k = strings.Replace(k, "*", "(\\S*)", -1)
|
||||||
k = k + "$"
|
|
||||||
config.rulesRegex[regexp.MustCompile(k)] = v
|
config.rulesRegex[regexp.MustCompile(k)] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,9 +74,9 @@ func RewriteWithConfig(config RewriteConfig) echo.MiddlewareFunc {
|
||||||
replacer := captureTokens(k, req.URL.Path)
|
replacer := captureTokens(k, req.URL.Path)
|
||||||
if replacer != nil {
|
if replacer != nil {
|
||||||
req.URL.Path = replacer.Replace(v)
|
req.URL.Path = replacer.Replace(v)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return next(c)
|
return next(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2
vendor/github.com/labstack/gommon/LICENSE
generated
vendored
2
vendor/github.com/labstack/gommon/LICENSE
generated
vendored
|
@ -1,6 +1,6 @@
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2015 labstack
|
Copyright (c) 2018 labstack
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
27
vendor/github.com/labstack/gommon/log/log.go
generated
vendored
27
vendor/github.com/labstack/gommon/log/log.go
generated
vendored
|
@ -22,6 +22,7 @@ type (
|
||||||
Logger struct {
|
Logger struct {
|
||||||
prefix string
|
prefix string
|
||||||
level Lvl
|
level Lvl
|
||||||
|
skip int
|
||||||
output io.Writer
|
output io.Writer
|
||||||
template *fasttemplate.Template
|
template *fasttemplate.Template
|
||||||
levels []string
|
levels []string
|
||||||
|
@ -41,6 +42,8 @@ const (
|
||||||
WARN
|
WARN
|
||||||
ERROR
|
ERROR
|
||||||
OFF
|
OFF
|
||||||
|
panicLevel
|
||||||
|
fatalLevel
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -49,9 +52,14 @@ var (
|
||||||
`"file":"${short_file}","line":"${line}"}`
|
`"file":"${short_file}","line":"${line}"}`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
global.skip = 3
|
||||||
|
}
|
||||||
|
|
||||||
func New(prefix string) (l *Logger) {
|
func New(prefix string) (l *Logger) {
|
||||||
l = &Logger{
|
l = &Logger{
|
||||||
level: INFO,
|
level: INFO,
|
||||||
|
skip: 2,
|
||||||
prefix: prefix,
|
prefix: prefix,
|
||||||
template: l.newTemplate(defaultHeader),
|
template: l.newTemplate(defaultHeader),
|
||||||
color: color.New(),
|
color: color.New(),
|
||||||
|
@ -73,6 +81,9 @@ func (l *Logger) initLevels() {
|
||||||
l.color.Green("INFO"),
|
l.color.Green("INFO"),
|
||||||
l.color.Yellow("WARN"),
|
l.color.Yellow("WARN"),
|
||||||
l.color.Red("ERROR"),
|
l.color.Red("ERROR"),
|
||||||
|
"",
|
||||||
|
l.color.Yellow("PANIC", color.U),
|
||||||
|
l.color.Red("FATAL", color.U),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,32 +198,32 @@ func (l *Logger) Errorj(j JSON) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Logger) Fatal(i ...interface{}) {
|
func (l *Logger) Fatal(i ...interface{}) {
|
||||||
l.Print(i...)
|
l.log(fatalLevel, "", i...)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Logger) Fatalf(format string, args ...interface{}) {
|
func (l *Logger) Fatalf(format string, args ...interface{}) {
|
||||||
l.Printf(format, args...)
|
l.log(fatalLevel, format, args...)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Logger) Fatalj(j JSON) {
|
func (l *Logger) Fatalj(j JSON) {
|
||||||
l.Printj(j)
|
l.log(fatalLevel, "json", j)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Logger) Panic(i ...interface{}) {
|
func (l *Logger) Panic(i ...interface{}) {
|
||||||
l.Print(i...)
|
l.log(panicLevel, "", i...)
|
||||||
panic(fmt.Sprint(i...))
|
panic(fmt.Sprint(i...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Logger) Panicf(format string, args ...interface{}) {
|
func (l *Logger) Panicf(format string, args ...interface{}) {
|
||||||
l.Printf(format, args...)
|
l.log(panicLevel, format, args...)
|
||||||
panic(fmt.Sprintf(format, args))
|
panic(fmt.Sprintf(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Logger) Panicj(j JSON) {
|
func (l *Logger) Panicj(j JSON) {
|
||||||
l.Printj(j)
|
l.log(panicLevel, "json", j)
|
||||||
panic(j)
|
panic(j)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,7 +353,7 @@ func (l *Logger) log(v Lvl, format string, args ...interface{}) {
|
||||||
buf := l.bufferPool.Get().(*bytes.Buffer)
|
buf := l.bufferPool.Get().(*bytes.Buffer)
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
defer l.bufferPool.Put(buf)
|
defer l.bufferPool.Put(buf)
|
||||||
_, file, line, _ := runtime.Caller(2)
|
_, file, line, _ := runtime.Caller(l.skip)
|
||||||
|
|
||||||
if v >= l.level || v == 0 {
|
if v >= l.level || v == 0 {
|
||||||
message := ""
|
message := ""
|
||||||
|
|
4
vendor/github.com/mattn/go-isatty/.travis.yml
generated
vendored
4
vendor/github.com/mattn/go-isatty/.travis.yml
generated
vendored
|
@ -2,6 +2,10 @@ language: go
|
||||||
go:
|
go:
|
||||||
- tip
|
- tip
|
||||||
|
|
||||||
|
os:
|
||||||
|
- linux
|
||||||
|
- osx
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- go get github.com/mattn/goveralls
|
- go get github.com/mattn/goveralls
|
||||||
- go get golang.org/x/tools/cmd/cover
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
|
2
vendor/github.com/mattn/go-isatty/isatty_others.go
generated
vendored
2
vendor/github.com/mattn/go-isatty/isatty_others.go
generated
vendored
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
package isatty
|
package isatty
|
||||||
|
|
||||||
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||||
// terminal. This is also always false on this environment.
|
// terminal. This is also always false on this environment.
|
||||||
func IsCygwinTerminal(fd uintptr) bool {
|
func IsCygwinTerminal(fd uintptr) bool {
|
||||||
return false
|
return false
|
||||||
|
|
19
vendor/github.com/op/go-logging/CHANGELOG.md
generated
vendored
Normal file
19
vendor/github.com/op/go-logging/CHANGELOG.md
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 2.0.0-rc1 (2016-02-11)
|
||||||
|
|
||||||
|
Time flies and it has been three years since this package was first released.
|
||||||
|
There have been a couple of API changes I have wanted to do for some time but
|
||||||
|
I've tried to maintain backwards compatibility. Some inconsistencies in the
|
||||||
|
API have started to show, proper vendor support in Go out of the box and
|
||||||
|
the fact that `go vet` will give warnings -- I have decided to bump the major
|
||||||
|
version.
|
||||||
|
|
||||||
|
* Make eg. `Info` and `Infof` do different things. You want to change all calls
|
||||||
|
to `Info` with a string format go to `Infof` etc. In many cases, `go vet` will
|
||||||
|
guide you.
|
||||||
|
* `Id` in `Record` is now called `ID`
|
||||||
|
|
||||||
|
## 1.0.0 (2013-02-21)
|
||||||
|
|
||||||
|
Initial release
|
6
vendor/github.com/op/go-logging/README.md
generated
vendored
6
vendor/github.com/op/go-logging/README.md
generated
vendored
|
@ -7,6 +7,10 @@ is customizable and supports different logging backends like syslog, file and
|
||||||
memory. Multiple backends can be utilized with different log levels per backend
|
memory. Multiple backends can be utilized with different log levels per backend
|
||||||
and logger.
|
and logger.
|
||||||
|
|
||||||
|
**_NOTE:_** backwards compatibility promise have been dropped for master. Please
|
||||||
|
vendor this package or use `gopkg.in/op/go-logging.v1` for previous version. See
|
||||||
|
[changelog](CHANGELOG.md) for details.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
Let's have a look at an [example](examples/example.go) which demonstrates most
|
Let's have a look at an [example](examples/example.go) which demonstrates most
|
||||||
|
@ -86,4 +90,4 @@ For docs, see http://godoc.org/github.com/op/go-logging or run:
|
||||||
|
|
||||||
## Additional resources
|
## Additional resources
|
||||||
|
|
||||||
* [wslog](https://godoc.org/github.com/cryptix/go/logging/wslog) -- exposes log messages through a WebSocket.
|
* [wslog](https://godoc.org/github.com/cryptix/exp/wslog) -- exposes log messages through a WebSocket.
|
||||||
|
|
38
vendor/github.com/op/go-logging/format.go
generated
vendored
38
vendor/github.com/op/go-logging/format.go
generated
vendored
|
@ -14,6 +14,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -81,7 +82,7 @@ var defaultVerbsLayout = []string{
|
||||||
"s",
|
"s",
|
||||||
"s",
|
"s",
|
||||||
"s",
|
"s",
|
||||||
"s",
|
"0",
|
||||||
"",
|
"",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,7 +163,7 @@ type stringFormatter struct {
|
||||||
// %{message} Message (string)
|
// %{message} Message (string)
|
||||||
// %{longfile} Full file name and line number: /a/b/c/d.go:23
|
// %{longfile} Full file name and line number: /a/b/c/d.go:23
|
||||||
// %{shortfile} Final file name element and line number: d.go:23
|
// %{shortfile} Final file name element and line number: d.go:23
|
||||||
// %{callpath} Callpath like main.a.b.c...c "..." meaning recursive call
|
// %{callpath} Callpath like main.a.b.c...c "..." meaning recursive call ~. meaning truncated path
|
||||||
// %{color} ANSI color based on log level
|
// %{color} ANSI color based on log level
|
||||||
//
|
//
|
||||||
// For normal types, the output can be customized by using the 'verbs' defined
|
// For normal types, the output can be customized by using the 'verbs' defined
|
||||||
|
@ -179,6 +180,9 @@ type stringFormatter struct {
|
||||||
// "%{color:bold}%{time:15:04:05} %{level:-8s}%{color:reset} %{message}" will
|
// "%{color:bold}%{time:15:04:05} %{level:-8s}%{color:reset} %{message}" will
|
||||||
// just colorize the time and level, leaving the message uncolored.
|
// just colorize the time and level, leaving the message uncolored.
|
||||||
//
|
//
|
||||||
|
// For the 'callpath' verb, the output can be adjusted to limit the printing
|
||||||
|
// the stack depth. i.e. '%{callpath:3}' will print '~.a.b.c'
|
||||||
|
//
|
||||||
// Colors on Windows is unfortunately not supported right now and is currently
|
// Colors on Windows is unfortunately not supported right now and is currently
|
||||||
// a no-op.
|
// a no-op.
|
||||||
//
|
//
|
||||||
|
@ -216,12 +220,12 @@ func NewStringFormatter(format string) (Formatter, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle layout customizations or use the default. If this is not for the
|
// Handle layout customizations or use the default. If this is not for the
|
||||||
// time or color formatting, we need to prefix with %.
|
// time, color formatting or callpath, we need to prefix with %.
|
||||||
layout := defaultVerbsLayout[verb]
|
layout := defaultVerbsLayout[verb]
|
||||||
if m[4] != -1 {
|
if m[4] != -1 {
|
||||||
layout = format[m[4]:m[5]]
|
layout = format[m[4]:m[5]]
|
||||||
}
|
}
|
||||||
if verb != fmtVerbTime && verb != fmtVerbLevelColor {
|
if verb != fmtVerbTime && verb != fmtVerbLevelColor && verb != fmtVerbCallpath {
|
||||||
layout = "%" + layout
|
layout = "%" + layout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,12 +242,13 @@ func NewStringFormatter(format string) (Formatter, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
testFmt := "hello %s"
|
||||||
r := &Record{
|
r := &Record{
|
||||||
Id: 12345,
|
ID: 12345,
|
||||||
Time: t,
|
Time: t,
|
||||||
Module: "logger",
|
Module: "logger",
|
||||||
Args: []interface{}{"go"},
|
Args: []interface{}{"go"},
|
||||||
fmt: "hello %s",
|
fmt: &testFmt,
|
||||||
}
|
}
|
||||||
if err := fmter.Format(0, r, &bytes.Buffer{}); err != nil {
|
if err := fmter.Format(0, r, &bytes.Buffer{}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -274,6 +279,12 @@ func (f *stringFormatter) Format(calldepth int, r *Record, output io.Writer) err
|
||||||
output.Write([]byte(r.Time.Format(part.layout)))
|
output.Write([]byte(r.Time.Format(part.layout)))
|
||||||
} else if part.verb == fmtVerbLevelColor {
|
} else if part.verb == fmtVerbLevelColor {
|
||||||
doFmtVerbLevelColor(part.layout, r.Level, output)
|
doFmtVerbLevelColor(part.layout, r.Level, output)
|
||||||
|
} else if part.verb == fmtVerbCallpath {
|
||||||
|
depth, err := strconv.Atoi(part.layout)
|
||||||
|
if err != nil {
|
||||||
|
depth = 0
|
||||||
|
}
|
||||||
|
output.Write([]byte(formatCallpath(calldepth+1, depth)))
|
||||||
} else {
|
} else {
|
||||||
var v interface{}
|
var v interface{}
|
||||||
switch part.verb {
|
switch part.verb {
|
||||||
|
@ -281,7 +292,7 @@ func (f *stringFormatter) Format(calldepth int, r *Record, output io.Writer) err
|
||||||
v = r.Level
|
v = r.Level
|
||||||
break
|
break
|
||||||
case fmtVerbID:
|
case fmtVerbID:
|
||||||
v = r.Id
|
v = r.ID
|
||||||
break
|
break
|
||||||
case fmtVerbPid:
|
case fmtVerbPid:
|
||||||
v = pid
|
v = pid
|
||||||
|
@ -313,8 +324,6 @@ func (f *stringFormatter) Format(calldepth int, r *Record, output io.Writer) err
|
||||||
v = formatFuncName(part.verb, f.Name())
|
v = formatFuncName(part.verb, f.Name())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case fmtVerbCallpath:
|
|
||||||
v = formatCallpath(calldepth + 1)
|
|
||||||
default:
|
default:
|
||||||
panic("unhandled format part")
|
panic("unhandled format part")
|
||||||
}
|
}
|
||||||
|
@ -350,14 +359,19 @@ func formatFuncName(v fmtVerb, f string) string {
|
||||||
panic("unexpected func formatter")
|
panic("unexpected func formatter")
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatCallpath(calldepth int) string {
|
func formatCallpath(calldepth int, depth int) string {
|
||||||
v := ""
|
v := ""
|
||||||
callers := make([]uintptr, 64)
|
callers := make([]uintptr, 64)
|
||||||
n := runtime.Callers(calldepth+2, callers)
|
n := runtime.Callers(calldepth+2, callers)
|
||||||
oldPc := callers[n-1]
|
oldPc := callers[n-1]
|
||||||
|
|
||||||
|
start := n - 3
|
||||||
|
if depth > 0 && start >= depth {
|
||||||
|
start = depth - 1
|
||||||
|
v += "~."
|
||||||
|
}
|
||||||
recursiveCall := false
|
recursiveCall := false
|
||||||
for i := n - 3; i >= 0; i-- {
|
for i := start; i >= 0; i-- {
|
||||||
pc := callers[i]
|
pc := callers[i]
|
||||||
if oldPc == pc {
|
if oldPc == pc {
|
||||||
recursiveCall = true
|
recursiveCall = true
|
||||||
|
@ -368,7 +382,7 @@ func formatCallpath(calldepth int) string {
|
||||||
recursiveCall = false
|
recursiveCall = false
|
||||||
v += ".."
|
v += ".."
|
||||||
}
|
}
|
||||||
if i < n-3 {
|
if i < start {
|
||||||
v += "."
|
v += "."
|
||||||
}
|
}
|
||||||
if f := runtime.FuncForPC(pc); f != nil {
|
if f := runtime.FuncForPC(pc); f != nil {
|
||||||
|
|
72
vendor/github.com/op/go-logging/logger.go
generated
vendored
72
vendor/github.com/op/go-logging/logger.go
generated
vendored
|
@ -41,7 +41,7 @@ var (
|
||||||
// was created, an increasing id, filename and line and finally the actual
|
// was created, an increasing id, filename and line and finally the actual
|
||||||
// formatted log line.
|
// formatted log line.
|
||||||
type Record struct {
|
type Record struct {
|
||||||
Id uint64
|
ID uint64
|
||||||
Time time.Time
|
Time time.Time
|
||||||
Module string
|
Module string
|
||||||
Level Level
|
Level Level
|
||||||
|
@ -50,7 +50,7 @@ type Record struct {
|
||||||
// message is kept as a pointer to have shallow copies update this once
|
// message is kept as a pointer to have shallow copies update this once
|
||||||
// needed.
|
// needed.
|
||||||
message *string
|
message *string
|
||||||
fmt string
|
fmt *string
|
||||||
formatter Formatter
|
formatter Formatter
|
||||||
formatted string
|
formatted string
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,15 @@ func (r *Record) Message() string {
|
||||||
r.Args[i] = redactor.Redacted()
|
r.Args[i] = redactor.Redacted()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
msg := fmt.Sprintf(r.fmt, r.Args...)
|
var buf bytes.Buffer
|
||||||
|
if r.fmt != nil {
|
||||||
|
fmt.Fprintf(&buf, *r.fmt, r.Args...)
|
||||||
|
} else {
|
||||||
|
// use Fprintln to make sure we always get space between arguments
|
||||||
|
fmt.Fprintln(&buf, r.Args...)
|
||||||
|
buf.Truncate(buf.Len() - 1) // strip newline
|
||||||
|
}
|
||||||
|
msg := buf.String()
|
||||||
r.message = &msg
|
r.message = &msg
|
||||||
}
|
}
|
||||||
return *r.message
|
return *r.message
|
||||||
|
@ -132,14 +140,14 @@ func (l *Logger) IsEnabledFor(level Level) bool {
|
||||||
return defaultBackend.IsEnabledFor(level, l.Module)
|
return defaultBackend.IsEnabledFor(level, l.Module)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Logger) log(lvl Level, format string, args ...interface{}) {
|
func (l *Logger) log(lvl Level, format *string, args ...interface{}) {
|
||||||
if !l.IsEnabledFor(lvl) {
|
if !l.IsEnabledFor(lvl) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the logging record and pass it in to the backend
|
// Create the logging record and pass it in to the backend
|
||||||
record := &Record{
|
record := &Record{
|
||||||
Id: atomic.AddUint64(&sequenceNo, 1),
|
ID: atomic.AddUint64(&sequenceNo, 1),
|
||||||
Time: timeNow(),
|
Time: timeNow(),
|
||||||
Module: l.Module,
|
Module: l.Module,
|
||||||
Level: lvl,
|
Level: lvl,
|
||||||
|
@ -164,84 +172,86 @@ func (l *Logger) log(lvl Level, format string, args ...interface{}) {
|
||||||
|
|
||||||
// Fatal is equivalent to l.Critical(fmt.Sprint()) followed by a call to os.Exit(1).
|
// Fatal is equivalent to l.Critical(fmt.Sprint()) followed by a call to os.Exit(1).
|
||||||
func (l *Logger) Fatal(args ...interface{}) {
|
func (l *Logger) Fatal(args ...interface{}) {
|
||||||
s := fmt.Sprint(args...)
|
l.log(CRITICAL, nil, args...)
|
||||||
l.log(CRITICAL, "%s", s)
|
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fatalf is equivalent to l.Critical followed by a call to os.Exit(1).
|
// Fatalf is equivalent to l.Critical followed by a call to os.Exit(1).
|
||||||
func (l *Logger) Fatalf(format string, args ...interface{}) {
|
func (l *Logger) Fatalf(format string, args ...interface{}) {
|
||||||
l.log(CRITICAL, format, args...)
|
l.log(CRITICAL, &format, args...)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Panic is equivalent to l.Critical(fmt.Sprint()) followed by a call to panic().
|
// Panic is equivalent to l.Critical(fmt.Sprint()) followed by a call to panic().
|
||||||
func (l *Logger) Panic(args ...interface{}) {
|
func (l *Logger) Panic(args ...interface{}) {
|
||||||
s := fmt.Sprint(args...)
|
l.log(CRITICAL, nil, args...)
|
||||||
l.log(CRITICAL, "%s", s)
|
panic(fmt.Sprint(args...))
|
||||||
panic(s)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Panicf is equivalent to l.Critical followed by a call to panic().
|
// Panicf is equivalent to l.Critical followed by a call to panic().
|
||||||
func (l *Logger) Panicf(format string, args ...interface{}) {
|
func (l *Logger) Panicf(format string, args ...interface{}) {
|
||||||
s := fmt.Sprintf(format, args...)
|
l.log(CRITICAL, &format, args...)
|
||||||
l.log(CRITICAL, "%s", s)
|
panic(fmt.Sprintf(format, args...))
|
||||||
panic(s)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Critical logs a message using CRITICAL as log level.
|
// Critical logs a message using CRITICAL as log level.
|
||||||
func (l *Logger) Critical(format string, args ...interface{}) {
|
func (l *Logger) Critical(args ...interface{}) {
|
||||||
l.log(CRITICAL, format, args...)
|
l.log(CRITICAL, nil, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Criticalf logs a message using CRITICAL as log level.
|
||||||
|
func (l *Logger) Criticalf(format string, args ...interface{}) {
|
||||||
|
l.log(CRITICAL, &format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error logs a message using ERROR as log level.
|
// Error logs a message using ERROR as log level.
|
||||||
func (l *Logger) Error(format string, args ...interface{}) {
|
func (l *Logger) Error(args ...interface{}) {
|
||||||
l.log(ERROR, format, args...)
|
l.log(ERROR, nil, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Errorf logs a message using ERROR as log level.
|
// Errorf logs a message using ERROR as log level.
|
||||||
func (l *Logger) Errorf(format string, args ...interface{}) {
|
func (l *Logger) Errorf(format string, args ...interface{}) {
|
||||||
l.log(ERROR, format, args...)
|
l.log(ERROR, &format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warning logs a message using WARNING as log level.
|
// Warning logs a message using WARNING as log level.
|
||||||
func (l *Logger) Warning(format string, args ...interface{}) {
|
func (l *Logger) Warning(args ...interface{}) {
|
||||||
l.log(WARNING, format, args...)
|
l.log(WARNING, nil, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warningf logs a message using WARNING as log level.
|
// Warningf logs a message using WARNING as log level.
|
||||||
func (l *Logger) Warningf(format string, args ...interface{}) {
|
func (l *Logger) Warningf(format string, args ...interface{}) {
|
||||||
l.log(WARNING, format, args...)
|
l.log(WARNING, &format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notice logs a message using NOTICE as log level.
|
// Notice logs a message using NOTICE as log level.
|
||||||
func (l *Logger) Notice(format string, args ...interface{}) {
|
func (l *Logger) Notice(args ...interface{}) {
|
||||||
l.log(NOTICE, format, args...)
|
l.log(NOTICE, nil, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Noticef logs a message using NOTICE as log level.
|
// Noticef logs a message using NOTICE as log level.
|
||||||
func (l *Logger) Noticef(format string, args ...interface{}) {
|
func (l *Logger) Noticef(format string, args ...interface{}) {
|
||||||
l.log(NOTICE, format, args...)
|
l.log(NOTICE, &format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info logs a message using INFO as log level.
|
// Info logs a message using INFO as log level.
|
||||||
func (l *Logger) Info(format string, args ...interface{}) {
|
func (l *Logger) Info(args ...interface{}) {
|
||||||
l.log(INFO, format, args...)
|
l.log(INFO, nil, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Infof logs a message using INFO as log level.
|
// Infof logs a message using INFO as log level.
|
||||||
func (l *Logger) Infof(format string, args ...interface{}) {
|
func (l *Logger) Infof(format string, args ...interface{}) {
|
||||||
l.log(INFO, format, args...)
|
l.log(INFO, &format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug logs a message using DEBUG as log level.
|
// Debug logs a message using DEBUG as log level.
|
||||||
func (l *Logger) Debug(format string, args ...interface{}) {
|
func (l *Logger) Debug(args ...interface{}) {
|
||||||
l.log(DEBUG, format, args...)
|
l.log(DEBUG, nil, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debugf logs a message using DEBUG as log level.
|
// Debugf logs a message using DEBUG as log level.
|
||||||
func (l *Logger) Debugf(format string, args ...interface{}) {
|
func (l *Logger) Debugf(format string, args ...interface{}) {
|
||||||
l.log(DEBUG, format, args...)
|
l.log(DEBUG, &format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
441
vendor/golang.org/x/crypto/acme/acme.go
generated
vendored
441
vendor/golang.org/x/crypto/acme/acme.go
generated
vendored
|
@ -14,7 +14,6 @@
|
||||||
package acme
|
package acme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
@ -23,6 +22,8 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/asn1"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -33,14 +34,26 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LetsEncryptURL is the Directory endpoint of Let's Encrypt CA.
|
const (
|
||||||
const LetsEncryptURL = "https://acme-v01.api.letsencrypt.org/directory"
|
// LetsEncryptURL is the Directory endpoint of Let's Encrypt CA.
|
||||||
|
LetsEncryptURL = "https://acme-v01.api.letsencrypt.org/directory"
|
||||||
|
|
||||||
|
// ALPNProto is the ALPN protocol name used by a CA server when validating
|
||||||
|
// tls-alpn-01 challenges.
|
||||||
|
//
|
||||||
|
// Package users must ensure their servers can negotiate the ACME ALPN in
|
||||||
|
// order for tls-alpn-01 challenge verifications to succeed.
|
||||||
|
// See the crypto/tls package's Config.NextProtos field.
|
||||||
|
ALPNProto = "acme-tls/1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// idPeACMEIdentifierV1 is the OID for the ACME extension for the TLS-ALPN challenge.
|
||||||
|
var idPeACMEIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 30, 1}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
maxChainLen = 5 // max depth and breadth of a certificate chain
|
maxChainLen = 5 // max depth and breadth of a certificate chain
|
||||||
|
@ -64,6 +77,10 @@ const (
|
||||||
type Client struct {
|
type Client struct {
|
||||||
// Key is the account key used to register with a CA and sign requests.
|
// Key is the account key used to register with a CA and sign requests.
|
||||||
// Key.Public() must return a *rsa.PublicKey or *ecdsa.PublicKey.
|
// Key.Public() must return a *rsa.PublicKey or *ecdsa.PublicKey.
|
||||||
|
//
|
||||||
|
// The following algorithms are supported:
|
||||||
|
// RS256, ES256, ES384 and ES512.
|
||||||
|
// See RFC7518 for more details about the algorithms.
|
||||||
Key crypto.Signer
|
Key crypto.Signer
|
||||||
|
|
||||||
// HTTPClient optionally specifies an HTTP client to use
|
// HTTPClient optionally specifies an HTTP client to use
|
||||||
|
@ -76,6 +93,22 @@ type Client struct {
|
||||||
// will have no effect.
|
// will have no effect.
|
||||||
DirectoryURL string
|
DirectoryURL string
|
||||||
|
|
||||||
|
// RetryBackoff computes the duration after which the nth retry of a failed request
|
||||||
|
// should occur. The value of n for the first call on failure is 1.
|
||||||
|
// The values of r and resp are the request and response of the last failed attempt.
|
||||||
|
// If the returned value is negative or zero, no more retries are done and an error
|
||||||
|
// is returned to the caller of the original method.
|
||||||
|
//
|
||||||
|
// Requests which result in a 4xx client error are not retried,
|
||||||
|
// except for 400 Bad Request due to "bad nonce" errors and 429 Too Many Requests.
|
||||||
|
//
|
||||||
|
// If RetryBackoff is nil, a truncated exponential backoff algorithm
|
||||||
|
// with the ceiling of 10 seconds is used, where each subsequent retry n
|
||||||
|
// is done after either ("Retry-After" + jitter) or (2^n seconds + jitter),
|
||||||
|
// preferring the former if "Retry-After" header is found in the resp.
|
||||||
|
// The jitter is a random value up to 1 second.
|
||||||
|
RetryBackoff func(n int, r *http.Request, resp *http.Response) time.Duration
|
||||||
|
|
||||||
dirMu sync.Mutex // guards writes to dir
|
dirMu sync.Mutex // guards writes to dir
|
||||||
dir *Directory // cached result of Client's Discover method
|
dir *Directory // cached result of Client's Discover method
|
||||||
|
|
||||||
|
@ -99,15 +132,12 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) {
|
||||||
if dirURL == "" {
|
if dirURL == "" {
|
||||||
dirURL = LetsEncryptURL
|
dirURL = LetsEncryptURL
|
||||||
}
|
}
|
||||||
res, err := c.get(ctx, dirURL)
|
res, err := c.get(ctx, dirURL, wantStatus(http.StatusOK))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Directory{}, err
|
return Directory{}, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
c.addNonce(res.Header)
|
c.addNonce(res.Header)
|
||||||
if res.StatusCode != http.StatusOK {
|
|
||||||
return Directory{}, responseError(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
var v struct {
|
var v struct {
|
||||||
Reg string `json:"new-reg"`
|
Reg string `json:"new-reg"`
|
||||||
|
@ -166,14 +196,11 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration,
|
||||||
req.NotAfter = now.Add(exp).Format(time.RFC3339)
|
req.NotAfter = now.Add(exp).Format(time.RFC3339)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := c.retryPostJWS(ctx, c.Key, c.dir.CertURL, req)
|
res, err := c.post(ctx, c.Key, c.dir.CertURL, req, wantStatus(http.StatusCreated))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusCreated {
|
|
||||||
return nil, "", responseError(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
curl := res.Header.Get("Location") // cert permanent URL
|
curl := res.Header.Get("Location") // cert permanent URL
|
||||||
if res.ContentLength == 0 {
|
if res.ContentLength == 0 {
|
||||||
|
@ -196,26 +223,11 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration,
|
||||||
// Callers are encouraged to parse the returned value to ensure the certificate is valid
|
// Callers are encouraged to parse the returned value to ensure the certificate is valid
|
||||||
// and has expected features.
|
// and has expected features.
|
||||||
func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) {
|
func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) {
|
||||||
for {
|
res, err := c.get(ctx, url, wantStatus(http.StatusOK))
|
||||||
res, err := c.get(ctx, url)
|
if err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer res.Body.Close()
|
|
||||||
if res.StatusCode == http.StatusOK {
|
|
||||||
return c.responseCert(ctx, res, bundle)
|
|
||||||
}
|
|
||||||
if res.StatusCode > 299 {
|
|
||||||
return nil, responseError(res)
|
|
||||||
}
|
|
||||||
d := retryAfter(res.Header.Get("Retry-After"), 3*time.Second)
|
|
||||||
select {
|
|
||||||
case <-time.After(d):
|
|
||||||
// retry
|
|
||||||
case <-ctx.Done():
|
|
||||||
return nil, ctx.Err()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return c.responseCert(ctx, res, bundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RevokeCert revokes a previously issued certificate cert, provided in DER format.
|
// RevokeCert revokes a previously issued certificate cert, provided in DER format.
|
||||||
|
@ -241,14 +253,11 @@ func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte,
|
||||||
if key == nil {
|
if key == nil {
|
||||||
key = c.Key
|
key = c.Key
|
||||||
}
|
}
|
||||||
res, err := c.retryPostJWS(ctx, key, c.dir.RevokeURL, body)
|
res, err := c.post(ctx, key, c.dir.RevokeURL, body, wantStatus(http.StatusOK))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusOK {
|
|
||||||
return responseError(res)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,14 +338,11 @@ func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization,
|
||||||
Resource: "new-authz",
|
Resource: "new-authz",
|
||||||
Identifier: authzID{Type: "dns", Value: domain},
|
Identifier: authzID{Type: "dns", Value: domain},
|
||||||
}
|
}
|
||||||
res, err := c.retryPostJWS(ctx, c.Key, c.dir.AuthzURL, req)
|
res, err := c.post(ctx, c.Key, c.dir.AuthzURL, req, wantStatus(http.StatusCreated))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusCreated {
|
|
||||||
return nil, responseError(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
var v wireAuthz
|
var v wireAuthz
|
||||||
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
|
@ -353,14 +359,11 @@ func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization,
|
||||||
// If a caller needs to poll an authorization until its status is final,
|
// If a caller needs to poll an authorization until its status is final,
|
||||||
// see the WaitAuthorization method.
|
// see the WaitAuthorization method.
|
||||||
func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
||||||
res, err := c.get(ctx, url)
|
res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted {
|
|
||||||
return nil, responseError(res)
|
|
||||||
}
|
|
||||||
var v wireAuthz
|
var v wireAuthz
|
||||||
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
return nil, fmt.Errorf("acme: invalid response: %v", err)
|
return nil, fmt.Errorf("acme: invalid response: %v", err)
|
||||||
|
@ -387,14 +390,11 @@ func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
|
||||||
Status: "deactivated",
|
Status: "deactivated",
|
||||||
Delete: true,
|
Delete: true,
|
||||||
}
|
}
|
||||||
res, err := c.retryPostJWS(ctx, c.Key, url, req)
|
res, err := c.post(ctx, c.Key, url, req, wantStatus(http.StatusOK))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusOK {
|
|
||||||
return responseError(res)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,44 +406,42 @@ func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
|
||||||
// In all other cases WaitAuthorization returns an error.
|
// In all other cases WaitAuthorization returns an error.
|
||||||
// If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
|
// If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
|
||||||
func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
||||||
sleep := sleeper(ctx)
|
|
||||||
for {
|
for {
|
||||||
res, err := c.get(ctx, url)
|
res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if res.StatusCode >= 400 && res.StatusCode <= 499 {
|
|
||||||
// Non-retriable error. For instance, Let's Encrypt may return 404 Not Found
|
|
||||||
// when requesting an expired authorization.
|
|
||||||
defer res.Body.Close()
|
|
||||||
return nil, responseError(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
retry := res.Header.Get("Retry-After")
|
|
||||||
if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted {
|
|
||||||
res.Body.Close()
|
|
||||||
if err := sleep(retry, 1); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var raw wireAuthz
|
var raw wireAuthz
|
||||||
err = json.NewDecoder(res.Body).Decode(&raw)
|
err = json.NewDecoder(res.Body).Decode(&raw)
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
if err != nil {
|
switch {
|
||||||
if err := sleep(retry, 0); err != nil {
|
case err != nil:
|
||||||
return nil, err
|
// Skip and retry.
|
||||||
}
|
case raw.Status == StatusValid:
|
||||||
continue
|
|
||||||
}
|
|
||||||
if raw.Status == StatusValid {
|
|
||||||
return raw.authorization(url), nil
|
return raw.authorization(url), nil
|
||||||
}
|
case raw.Status == StatusInvalid:
|
||||||
if raw.Status == StatusInvalid {
|
|
||||||
return nil, raw.error(url)
|
return nil, raw.error(url)
|
||||||
}
|
}
|
||||||
if err := sleep(retry, 0); err != nil {
|
|
||||||
return nil, err
|
// Exponential backoff is implemented in c.get above.
|
||||||
|
// This is just to prevent continuously hitting the CA
|
||||||
|
// while waiting for a final authorization status.
|
||||||
|
d := retryAfter(res.Header.Get("Retry-After"))
|
||||||
|
if d == 0 {
|
||||||
|
// Given that the fastest challenges TLS-SNI and HTTP-01
|
||||||
|
// require a CA to make at least 1 network round trip
|
||||||
|
// and most likely persist a challenge state,
|
||||||
|
// this default delay seems reasonable.
|
||||||
|
d = time.Second
|
||||||
|
}
|
||||||
|
t := time.NewTimer(d)
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
t.Stop()
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case <-t.C:
|
||||||
|
// Retry.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -452,14 +450,11 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat
|
||||||
//
|
//
|
||||||
// A client typically polls a challenge status using this method.
|
// A client typically polls a challenge status using this method.
|
||||||
func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) {
|
func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) {
|
||||||
res, err := c.get(ctx, url)
|
res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted {
|
|
||||||
return nil, responseError(res)
|
|
||||||
}
|
|
||||||
v := wireChallenge{URI: url}
|
v := wireChallenge{URI: url}
|
||||||
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
return nil, fmt.Errorf("acme: invalid response: %v", err)
|
return nil, fmt.Errorf("acme: invalid response: %v", err)
|
||||||
|
@ -486,16 +481,14 @@ func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error
|
||||||
Type: chal.Type,
|
Type: chal.Type,
|
||||||
Auth: auth,
|
Auth: auth,
|
||||||
}
|
}
|
||||||
res, err := c.retryPostJWS(ctx, c.Key, chal.URI, req)
|
res, err := c.post(ctx, c.Key, chal.URI, req, wantStatus(
|
||||||
|
http.StatusOK, // according to the spec
|
||||||
|
http.StatusAccepted, // Let's Encrypt: see https://goo.gl/WsJ7VT (acme-divergences.md)
|
||||||
|
))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
// Note: the protocol specifies 200 as the expected response code, but
|
|
||||||
// letsencrypt seems to be returning 202.
|
|
||||||
if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted {
|
|
||||||
return nil, responseError(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
var v wireChallenge
|
var v wireChallenge
|
||||||
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
|
@ -552,7 +545,7 @@ func (c *Client) HTTP01ChallengePath(token string) string {
|
||||||
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
|
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
|
||||||
//
|
//
|
||||||
// The returned certificate is valid for the next 24 hours and must be presented only when
|
// The returned certificate is valid for the next 24 hours and must be presented only when
|
||||||
// the server name of the client hello matches exactly the returned name value.
|
// the server name of the TLS ClientHello matches exactly the returned name value.
|
||||||
func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
||||||
ka, err := keyAuth(c.Key.Public(), token)
|
ka, err := keyAuth(c.Key.Public(), token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -579,7 +572,7 @@ func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tl
|
||||||
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
|
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
|
||||||
//
|
//
|
||||||
// The returned certificate is valid for the next 24 hours and must be presented only when
|
// The returned certificate is valid for the next 24 hours and must be presented only when
|
||||||
// the server name in the client hello matches exactly the returned name value.
|
// the server name in the TLS ClientHello matches exactly the returned name value.
|
||||||
func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
||||||
b := sha256.Sum256([]byte(token))
|
b := sha256.Sum256([]byte(token))
|
||||||
h := hex.EncodeToString(b[:])
|
h := hex.EncodeToString(b[:])
|
||||||
|
@ -600,6 +593,52 @@ func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tl
|
||||||
return cert, sanA, nil
|
return cert, sanA, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TLSALPN01ChallengeCert creates a certificate for TLS-ALPN-01 challenge response.
|
||||||
|
// Servers can present the certificate to validate the challenge and prove control
|
||||||
|
// over a domain name. For more details on TLS-ALPN-01 see
|
||||||
|
// https://tools.ietf.org/html/draft-shoemaker-acme-tls-alpn-00#section-3
|
||||||
|
//
|
||||||
|
// The token argument is a Challenge.Token value.
|
||||||
|
// If a WithKey option is provided, its private part signs the returned cert,
|
||||||
|
// and the public part is used to specify the signee.
|
||||||
|
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
|
||||||
|
//
|
||||||
|
// The returned certificate is valid for the next 24 hours and must be presented only when
|
||||||
|
// the server name in the TLS ClientHello matches the domain, and the special acme-tls/1 ALPN protocol
|
||||||
|
// has been specified.
|
||||||
|
func (c *Client) TLSALPN01ChallengeCert(token, domain string, opt ...CertOption) (cert tls.Certificate, err error) {
|
||||||
|
ka, err := keyAuth(c.Key.Public(), token)
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, err
|
||||||
|
}
|
||||||
|
shasum := sha256.Sum256([]byte(ka))
|
||||||
|
extValue, err := asn1.Marshal(shasum[:])
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, err
|
||||||
|
}
|
||||||
|
acmeExtension := pkix.Extension{
|
||||||
|
Id: idPeACMEIdentifierV1,
|
||||||
|
Critical: true,
|
||||||
|
Value: extValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl := defaultTLSChallengeCertTemplate()
|
||||||
|
|
||||||
|
var newOpt []CertOption
|
||||||
|
for _, o := range opt {
|
||||||
|
switch o := o.(type) {
|
||||||
|
case *certOptTemplate:
|
||||||
|
t := *(*x509.Certificate)(o) // shallow copy is ok
|
||||||
|
tmpl = &t
|
||||||
|
default:
|
||||||
|
newOpt = append(newOpt, o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmpl.ExtraExtensions = append(tmpl.ExtraExtensions, acmeExtension)
|
||||||
|
newOpt = append(newOpt, WithTemplate(tmpl))
|
||||||
|
return tlsChallengeCert([]string{domain}, newOpt)
|
||||||
|
}
|
||||||
|
|
||||||
// doReg sends all types of registration requests.
|
// doReg sends all types of registration requests.
|
||||||
// The type of request is identified by typ argument, which is a "resource"
|
// The type of request is identified by typ argument, which is a "resource"
|
||||||
// in the ACME spec terms.
|
// in the ACME spec terms.
|
||||||
|
@ -619,14 +658,15 @@ func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Accoun
|
||||||
req.Contact = acct.Contact
|
req.Contact = acct.Contact
|
||||||
req.Agreement = acct.AgreedTerms
|
req.Agreement = acct.AgreedTerms
|
||||||
}
|
}
|
||||||
res, err := c.retryPostJWS(ctx, c.Key, url, req)
|
res, err := c.post(ctx, c.Key, url, req, wantStatus(
|
||||||
|
http.StatusOK, // updates and deletes
|
||||||
|
http.StatusCreated, // new account creation
|
||||||
|
http.StatusAccepted, // Let's Encrypt divergent implementation
|
||||||
|
))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode < 200 || res.StatusCode > 299 {
|
|
||||||
return nil, responseError(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
var v struct {
|
var v struct {
|
||||||
Contact []string
|
Contact []string
|
||||||
|
@ -656,59 +696,6 @@ func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Accoun
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// retryPostJWS will retry calls to postJWS if there is a badNonce error,
|
|
||||||
// clearing the stored nonces after each error.
|
|
||||||
// If the response was 4XX-5XX, then responseError is called on the body,
|
|
||||||
// the body is closed, and the error returned.
|
|
||||||
func (c *Client) retryPostJWS(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, error) {
|
|
||||||
sleep := sleeper(ctx)
|
|
||||||
for {
|
|
||||||
res, err := c.postJWS(ctx, key, url, body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// handle errors 4XX-5XX with responseError
|
|
||||||
if res.StatusCode >= 400 && res.StatusCode <= 599 {
|
|
||||||
err := responseError(res)
|
|
||||||
res.Body.Close()
|
|
||||||
// according to spec badNonce is urn:ietf:params:acme:error:badNonce
|
|
||||||
// however, acme servers in the wild return their version of the error
|
|
||||||
// https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-5.4
|
|
||||||
if ae, ok := err.(*Error); ok && strings.HasSuffix(strings.ToLower(ae.ProblemType), ":badnonce") {
|
|
||||||
// clear any nonces that we might've stored that might now be
|
|
||||||
// considered bad
|
|
||||||
c.clearNonces()
|
|
||||||
retry := res.Header.Get("Retry-After")
|
|
||||||
if err := sleep(retry, 1); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// postJWS signs the body with the given key and POSTs it to the provided url.
|
|
||||||
// The body argument must be JSON-serializable.
|
|
||||||
func (c *Client) postJWS(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, error) {
|
|
||||||
nonce, err := c.popNonce(ctx, url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
b, err := jwsEncodeJSON(body, key, nonce)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
res, err := c.post(ctx, url, "application/jose+json", bytes.NewReader(b))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
c.addNonce(res.Header)
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// popNonce returns a nonce value previously stored with c.addNonce
|
// popNonce returns a nonce value previously stored with c.addNonce
|
||||||
// or fetches a fresh one from the given URL.
|
// or fetches a fresh one from the given URL.
|
||||||
func (c *Client) popNonce(ctx context.Context, url string) (string, error) {
|
func (c *Client) popNonce(ctx context.Context, url string) (string, error) {
|
||||||
|
@ -749,58 +736,12 @@ func (c *Client) addNonce(h http.Header) {
|
||||||
c.nonces[v] = struct{}{}
|
c.nonces[v] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) httpClient() *http.Client {
|
|
||||||
if c.HTTPClient != nil {
|
|
||||||
return c.HTTPClient
|
|
||||||
}
|
|
||||||
return http.DefaultClient
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) get(ctx context.Context, urlStr string) (*http.Response, error) {
|
|
||||||
req, err := http.NewRequest("GET", urlStr, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return c.do(ctx, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) head(ctx context.Context, urlStr string) (*http.Response, error) {
|
|
||||||
req, err := http.NewRequest("HEAD", urlStr, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return c.do(ctx, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) post(ctx context.Context, urlStr, contentType string, body io.Reader) (*http.Response, error) {
|
|
||||||
req, err := http.NewRequest("POST", urlStr, body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-Type", contentType)
|
|
||||||
return c.do(ctx, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) do(ctx context.Context, req *http.Request) (*http.Response, error) {
|
|
||||||
res, err := c.httpClient().Do(req.WithContext(ctx))
|
|
||||||
if err != nil {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
// Prefer the unadorned context error.
|
|
||||||
// (The acme package had tests assuming this, previously from ctxhttp's
|
|
||||||
// behavior, predating net/http supporting contexts natively)
|
|
||||||
// TODO(bradfitz): reconsider this in the future. But for now this
|
|
||||||
// requires no test updates.
|
|
||||||
return nil, ctx.Err()
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) fetchNonce(ctx context.Context, url string) (string, error) {
|
func (c *Client) fetchNonce(ctx context.Context, url string) (string, error) {
|
||||||
resp, err := c.head(ctx, url)
|
r, err := http.NewRequest("HEAD", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
resp, err := c.doNoRetry(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -852,24 +793,6 @@ func (c *Client) responseCert(ctx context.Context, res *http.Response, bundle bo
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// responseError creates an error of Error type from resp.
|
|
||||||
func responseError(resp *http.Response) error {
|
|
||||||
// don't care if ReadAll returns an error:
|
|
||||||
// json.Unmarshal will fail in that case anyway
|
|
||||||
b, _ := ioutil.ReadAll(resp.Body)
|
|
||||||
e := &wireError{Status: resp.StatusCode}
|
|
||||||
if err := json.Unmarshal(b, e); err != nil {
|
|
||||||
// this is not a regular error response:
|
|
||||||
// populate detail with anything we received,
|
|
||||||
// e.Status will already contain HTTP response code value
|
|
||||||
e.Detail = string(b)
|
|
||||||
if e.Detail == "" {
|
|
||||||
e.Detail = resp.Status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return e.error(resp.Header)
|
|
||||||
}
|
|
||||||
|
|
||||||
// chainCert fetches CA certificate chain recursively by following "up" links.
|
// chainCert fetches CA certificate chain recursively by following "up" links.
|
||||||
// Each recursive call increments the depth by 1, resulting in an error
|
// Each recursive call increments the depth by 1, resulting in an error
|
||||||
// if the recursion level reaches maxChainLen.
|
// if the recursion level reaches maxChainLen.
|
||||||
|
@ -880,14 +803,11 @@ func (c *Client) chainCert(ctx context.Context, url string, depth int) ([][]byte
|
||||||
return nil, errors.New("acme: certificate chain is too deep")
|
return nil, errors.New("acme: certificate chain is too deep")
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := c.get(ctx, url)
|
res, err := c.get(ctx, url, wantStatus(http.StatusOK))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusOK {
|
|
||||||
return nil, responseError(res)
|
|
||||||
}
|
|
||||||
b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1))
|
b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -932,65 +852,6 @@ func linkHeader(h http.Header, rel string) []string {
|
||||||
return links
|
return links
|
||||||
}
|
}
|
||||||
|
|
||||||
// sleeper returns a function that accepts the Retry-After HTTP header value
|
|
||||||
// and an increment that's used with backoff to increasingly sleep on
|
|
||||||
// consecutive calls until the context is done. If the Retry-After header
|
|
||||||
// cannot be parsed, then backoff is used with a maximum sleep time of 10
|
|
||||||
// seconds.
|
|
||||||
func sleeper(ctx context.Context) func(ra string, inc int) error {
|
|
||||||
var count int
|
|
||||||
return func(ra string, inc int) error {
|
|
||||||
count += inc
|
|
||||||
d := backoff(count, 10*time.Second)
|
|
||||||
d = retryAfter(ra, d)
|
|
||||||
wakeup := time.NewTimer(d)
|
|
||||||
defer wakeup.Stop()
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
case <-wakeup.C:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// retryAfter parses a Retry-After HTTP header value,
|
|
||||||
// trying to convert v into an int (seconds) or use http.ParseTime otherwise.
|
|
||||||
// It returns d if v cannot be parsed.
|
|
||||||
func retryAfter(v string, d time.Duration) time.Duration {
|
|
||||||
if i, err := strconv.Atoi(v); err == nil {
|
|
||||||
return time.Duration(i) * time.Second
|
|
||||||
}
|
|
||||||
t, err := http.ParseTime(v)
|
|
||||||
if err != nil {
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
return t.Sub(timeNow())
|
|
||||||
}
|
|
||||||
|
|
||||||
// backoff computes a duration after which an n+1 retry iteration should occur
|
|
||||||
// using truncated exponential backoff algorithm.
|
|
||||||
//
|
|
||||||
// The n argument is always bounded between 0 and 30.
|
|
||||||
// The max argument defines upper bound for the returned value.
|
|
||||||
func backoff(n int, max time.Duration) time.Duration {
|
|
||||||
if n < 0 {
|
|
||||||
n = 0
|
|
||||||
}
|
|
||||||
if n > 30 {
|
|
||||||
n = 30
|
|
||||||
}
|
|
||||||
var d time.Duration
|
|
||||||
if x, err := rand.Int(rand.Reader, big.NewInt(1000)); err == nil {
|
|
||||||
d = time.Duration(x.Int64()) * time.Millisecond
|
|
||||||
}
|
|
||||||
d += time.Duration(1<<uint(n)) * time.Second
|
|
||||||
if d > max {
|
|
||||||
return max
|
|
||||||
}
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
// keyAuth generates a key authorization string for a given token.
|
// keyAuth generates a key authorization string for a given token.
|
||||||
func keyAuth(pub crypto.PublicKey, token string) (string, error) {
|
func keyAuth(pub crypto.PublicKey, token string) (string, error) {
|
||||||
th, err := JWKThumbprint(pub)
|
th, err := JWKThumbprint(pub)
|
||||||
|
@ -1000,15 +861,25 @@ func keyAuth(pub crypto.PublicKey, token string) (string, error) {
|
||||||
return fmt.Sprintf("%s.%s", token, th), nil
|
return fmt.Sprintf("%s.%s", token, th), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// defaultTLSChallengeCertTemplate is a template used to create challenge certs for TLS challenges.
|
||||||
|
func defaultTLSChallengeCertTemplate() *x509.Certificate {
|
||||||
|
return &x509.Certificate{
|
||||||
|
SerialNumber: big.NewInt(1),
|
||||||
|
NotBefore: time.Now(),
|
||||||
|
NotAfter: time.Now().Add(24 * time.Hour),
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// tlsChallengeCert creates a temporary certificate for TLS-SNI challenges
|
// tlsChallengeCert creates a temporary certificate for TLS-SNI challenges
|
||||||
// with the given SANs and auto-generated public/private key pair.
|
// with the given SANs and auto-generated public/private key pair.
|
||||||
// The Subject Common Name is set to the first SAN to aid debugging.
|
// The Subject Common Name is set to the first SAN to aid debugging.
|
||||||
// To create a cert with a custom key pair, specify WithKey option.
|
// To create a cert with a custom key pair, specify WithKey option.
|
||||||
func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
|
func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
|
||||||
var (
|
var key crypto.Signer
|
||||||
key crypto.Signer
|
tmpl := defaultTLSChallengeCertTemplate()
|
||||||
tmpl *x509.Certificate
|
|
||||||
)
|
|
||||||
for _, o := range opt {
|
for _, o := range opt {
|
||||||
switch o := o.(type) {
|
switch o := o.(type) {
|
||||||
case *certOptKey:
|
case *certOptKey:
|
||||||
|
@ -1017,7 +888,7 @@ func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
|
||||||
}
|
}
|
||||||
key = o.key
|
key = o.key
|
||||||
case *certOptTemplate:
|
case *certOptTemplate:
|
||||||
var t = *(*x509.Certificate)(o) // shallow copy is ok
|
t := *(*x509.Certificate)(o) // shallow copy is ok
|
||||||
tmpl = &t
|
tmpl = &t
|
||||||
default:
|
default:
|
||||||
// package's fault, if we let this happen:
|
// package's fault, if we let this happen:
|
||||||
|
@ -1030,16 +901,6 @@ func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
|
||||||
return tls.Certificate{}, err
|
return tls.Certificate{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if tmpl == nil {
|
|
||||||
tmpl = &x509.Certificate{
|
|
||||||
SerialNumber: big.NewInt(1),
|
|
||||||
NotBefore: time.Now(),
|
|
||||||
NotAfter: time.Now().Add(24 * time.Hour),
|
|
||||||
BasicConstraintsValid: true,
|
|
||||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
|
||||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tmpl.DNSNames = san
|
tmpl.DNSNames = san
|
||||||
if len(san) > 0 {
|
if len(san) > 0 {
|
||||||
tmpl.Subject.CommonName = san[0]
|
tmpl.Subject.CommonName = san[0]
|
||||||
|
|
361
vendor/golang.org/x/crypto/acme/autocert/autocert.go
generated
vendored
361
vendor/golang.org/x/crypto/acme/autocert/autocert.go
generated
vendored
|
@ -44,7 +44,7 @@ var createCertRetryAfter = time.Minute
|
||||||
var pseudoRand *lockedMathRand
|
var pseudoRand *lockedMathRand
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
src := mathrand.NewSource(timeNow().UnixNano())
|
src := mathrand.NewSource(time.Now().UnixNano())
|
||||||
pseudoRand = &lockedMathRand{rnd: mathrand.New(src)}
|
pseudoRand = &lockedMathRand{rnd: mathrand.New(src)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ func HostWhitelist(hosts ...string) HostPolicy {
|
||||||
}
|
}
|
||||||
return func(_ context.Context, host string) error {
|
return func(_ context.Context, host string) error {
|
||||||
if !whitelist[host] {
|
if !whitelist[host] {
|
||||||
return errors.New("acme/autocert: host not configured")
|
return fmt.Errorf("acme/autocert: host %q not configured in HostWhitelist", host)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -81,9 +81,9 @@ func defaultHostPolicy(context.Context, string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manager is a stateful certificate manager built on top of acme.Client.
|
// Manager is a stateful certificate manager built on top of acme.Client.
|
||||||
// It obtains and refreshes certificates automatically using "tls-sni-01",
|
// It obtains and refreshes certificates automatically using "tls-alpn-01",
|
||||||
// "tls-sni-02" and "http-01" challenge types, as well as providing them
|
// "tls-sni-01", "tls-sni-02" and "http-01" challenge types,
|
||||||
// to a TLS server via tls.Config.
|
// as well as providing them to a TLS server via tls.Config.
|
||||||
//
|
//
|
||||||
// You must specify a cache implementation, such as DirCache,
|
// You must specify a cache implementation, such as DirCache,
|
||||||
// to reuse obtained certificates across program restarts.
|
// to reuse obtained certificates across program restarts.
|
||||||
|
@ -98,11 +98,11 @@ type Manager struct {
|
||||||
// To always accept the terms, the callers can use AcceptTOS.
|
// To always accept the terms, the callers can use AcceptTOS.
|
||||||
Prompt func(tosURL string) bool
|
Prompt func(tosURL string) bool
|
||||||
|
|
||||||
// Cache optionally stores and retrieves previously-obtained certificates.
|
// Cache optionally stores and retrieves previously-obtained certificates
|
||||||
// If nil, certs will only be cached for the lifetime of the Manager.
|
// and other state. If nil, certs will only be cached for the lifetime of
|
||||||
|
// the Manager. Multiple Managers can share the same Cache.
|
||||||
//
|
//
|
||||||
// Manager passes the Cache certificates data encoded in PEM, with private/public
|
// Using a persistent Cache, such as DirCache, is strongly recommended.
|
||||||
// parts combined in a single Cache.Put call, private key first.
|
|
||||||
Cache Cache
|
Cache Cache
|
||||||
|
|
||||||
// HostPolicy controls which domains the Manager will attempt
|
// HostPolicy controls which domains the Manager will attempt
|
||||||
|
@ -127,8 +127,10 @@ type Manager struct {
|
||||||
|
|
||||||
// Client is used to perform low-level operations, such as account registration
|
// Client is used to perform low-level operations, such as account registration
|
||||||
// and requesting new certificates.
|
// and requesting new certificates.
|
||||||
|
//
|
||||||
// If Client is nil, a zero-value acme.Client is used with acme.LetsEncryptURL
|
// If Client is nil, a zero-value acme.Client is used with acme.LetsEncryptURL
|
||||||
// directory endpoint and a newly-generated ECDSA P-256 key.
|
// as directory endpoint. If the Client.Key is nil, a new ECDSA P-256 key is
|
||||||
|
// generated and, if Cache is not nil, stored in cache.
|
||||||
//
|
//
|
||||||
// Mutating the field after the first call of GetCertificate method will have no effect.
|
// Mutating the field after the first call of GetCertificate method will have no effect.
|
||||||
Client *acme.Client
|
Client *acme.Client
|
||||||
|
@ -140,22 +142,30 @@ type Manager struct {
|
||||||
// If the Client's account key is already registered, Email is not used.
|
// If the Client's account key is already registered, Email is not used.
|
||||||
Email string
|
Email string
|
||||||
|
|
||||||
// ForceRSA makes the Manager generate certificates with 2048-bit RSA keys.
|
// ForceRSA used to make the Manager generate RSA certificates. It is now ignored.
|
||||||
//
|
//
|
||||||
// If false, a default is used. Currently the default
|
// Deprecated: the Manager will request the correct type of certificate based
|
||||||
// is EC-based keys using the P-256 curve.
|
// on what each client supports.
|
||||||
ForceRSA bool
|
ForceRSA bool
|
||||||
|
|
||||||
|
// ExtraExtensions are used when generating a new CSR (Certificate Request),
|
||||||
|
// thus allowing customization of the resulting certificate.
|
||||||
|
// For instance, TLS Feature Extension (RFC 7633) can be used
|
||||||
|
// to prevent an OCSP downgrade attack.
|
||||||
|
//
|
||||||
|
// The field value is passed to crypto/x509.CreateCertificateRequest
|
||||||
|
// in the template's ExtraExtensions field as is.
|
||||||
|
ExtraExtensions []pkix.Extension
|
||||||
|
|
||||||
clientMu sync.Mutex
|
clientMu sync.Mutex
|
||||||
client *acme.Client // initialized by acmeClient method
|
client *acme.Client // initialized by acmeClient method
|
||||||
|
|
||||||
stateMu sync.Mutex
|
stateMu sync.Mutex
|
||||||
state map[string]*certState // keyed by domain name
|
state map[certKey]*certState
|
||||||
|
|
||||||
// renewal tracks the set of domains currently running renewal timers.
|
// renewal tracks the set of domains currently running renewal timers.
|
||||||
// It is keyed by domain name.
|
|
||||||
renewalMu sync.Mutex
|
renewalMu sync.Mutex
|
||||||
renewal map[string]*domainRenewal
|
renewal map[certKey]*domainRenewal
|
||||||
|
|
||||||
// tokensMu guards the rest of the fields: tryHTTP01, certTokens and httpTokens.
|
// tokensMu guards the rest of the fields: tryHTTP01, certTokens and httpTokens.
|
||||||
tokensMu sync.RWMutex
|
tokensMu sync.RWMutex
|
||||||
|
@ -167,21 +177,60 @@ type Manager struct {
|
||||||
// to be provisioned.
|
// to be provisioned.
|
||||||
// The entries are stored for the duration of the authorization flow.
|
// The entries are stored for the duration of the authorization flow.
|
||||||
httpTokens map[string][]byte
|
httpTokens map[string][]byte
|
||||||
// certTokens contains temporary certificates for tls-sni challenges
|
// certTokens contains temporary certificates for tls-sni and tls-alpn challenges
|
||||||
// and is keyed by token domain name, which matches server name of ClientHello.
|
// and is keyed by token domain name, which matches server name of ClientHello.
|
||||||
// Keys always have ".acme.invalid" suffix.
|
// Keys always have ".acme.invalid" suffix for tls-sni. Otherwise, they are domain names
|
||||||
|
// for tls-alpn.
|
||||||
// The entries are stored for the duration of the authorization flow.
|
// The entries are stored for the duration of the authorization flow.
|
||||||
certTokens map[string]*tls.Certificate
|
certTokens map[string]*tls.Certificate
|
||||||
|
// nowFunc, if not nil, returns the current time. This may be set for
|
||||||
|
// testing purposes.
|
||||||
|
nowFunc func() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// certKey is the key by which certificates are tracked in state, renewal and cache.
|
||||||
|
type certKey struct {
|
||||||
|
domain string // without trailing dot
|
||||||
|
isRSA bool // RSA cert for legacy clients (as opposed to default ECDSA)
|
||||||
|
isToken bool // tls-based challenge token cert; key type is undefined regardless of isRSA
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c certKey) String() string {
|
||||||
|
if c.isToken {
|
||||||
|
return c.domain + "+token"
|
||||||
|
}
|
||||||
|
if c.isRSA {
|
||||||
|
return c.domain + "+rsa"
|
||||||
|
}
|
||||||
|
return c.domain
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLSConfig creates a new TLS config suitable for net/http.Server servers,
|
||||||
|
// supporting HTTP/2 and the tls-alpn-01 ACME challenge type.
|
||||||
|
func (m *Manager) TLSConfig() *tls.Config {
|
||||||
|
return &tls.Config{
|
||||||
|
GetCertificate: m.GetCertificate,
|
||||||
|
NextProtos: []string{
|
||||||
|
"h2", "http/1.1", // enable HTTP/2
|
||||||
|
acme.ALPNProto, // enable tls-alpn ACME challenges
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCertificate implements the tls.Config.GetCertificate hook.
|
// GetCertificate implements the tls.Config.GetCertificate hook.
|
||||||
// It provides a TLS certificate for hello.ServerName host, including answering
|
// It provides a TLS certificate for hello.ServerName host, including answering
|
||||||
// *.acme.invalid (TLS-SNI) challenges. All other fields of hello are ignored.
|
// tls-alpn-01 and *.acme.invalid (tls-sni-01 and tls-sni-02) challenges.
|
||||||
|
// All other fields of hello are ignored.
|
||||||
//
|
//
|
||||||
// If m.HostPolicy is non-nil, GetCertificate calls the policy before requesting
|
// If m.HostPolicy is non-nil, GetCertificate calls the policy before requesting
|
||||||
// a new cert. A non-nil error returned from m.HostPolicy halts TLS negotiation.
|
// a new cert. A non-nil error returned from m.HostPolicy halts TLS negotiation.
|
||||||
// The error is propagated back to the caller of GetCertificate and is user-visible.
|
// The error is propagated back to the caller of GetCertificate and is user-visible.
|
||||||
// This does not affect cached certs. See HostPolicy field description for more details.
|
// This does not affect cached certs. See HostPolicy field description for more details.
|
||||||
|
//
|
||||||
|
// If GetCertificate is used directly, instead of via Manager.TLSConfig, package users will
|
||||||
|
// also have to add acme.ALPNProto to NextProtos for tls-alpn-01, or use HTTPHandler
|
||||||
|
// for http-01. (The tls-sni-* challenges have been deprecated by popular ACME providers
|
||||||
|
// due to security issues in the ecosystem.)
|
||||||
func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
if m.Prompt == nil {
|
if m.Prompt == nil {
|
||||||
return nil, errors.New("acme/autocert: Manager.Prompt not set")
|
return nil, errors.New("acme/autocert: Manager.Prompt not set")
|
||||||
|
@ -194,7 +243,7 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
|
||||||
if !strings.Contains(strings.Trim(name, "."), ".") {
|
if !strings.Contains(strings.Trim(name, "."), ".") {
|
||||||
return nil, errors.New("acme/autocert: server name component count invalid")
|
return nil, errors.New("acme/autocert: server name component count invalid")
|
||||||
}
|
}
|
||||||
if strings.ContainsAny(name, `/\`) {
|
if strings.ContainsAny(name, `+/\`) {
|
||||||
return nil, errors.New("acme/autocert: server name contains invalid character")
|
return nil, errors.New("acme/autocert: server name contains invalid character")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,14 +252,17 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// check whether this is a token cert requested for TLS-SNI challenge
|
// Check whether this is a token cert requested for TLS-SNI or TLS-ALPN challenge.
|
||||||
if strings.HasSuffix(name, ".acme.invalid") {
|
if wantsTokenCert(hello) {
|
||||||
m.tokensMu.RLock()
|
m.tokensMu.RLock()
|
||||||
defer m.tokensMu.RUnlock()
|
defer m.tokensMu.RUnlock()
|
||||||
|
// It's ok to use the same token cert key for both tls-sni and tls-alpn
|
||||||
|
// because there's always at most 1 token cert per on-going domain authorization.
|
||||||
|
// See m.verify for details.
|
||||||
if cert := m.certTokens[name]; cert != nil {
|
if cert := m.certTokens[name]; cert != nil {
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
if cert, err := m.cacheGet(ctx, name); err == nil {
|
if cert, err := m.cacheGet(ctx, certKey{domain: name, isToken: true}); err == nil {
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
// TODO: cache error results?
|
// TODO: cache error results?
|
||||||
|
@ -218,8 +270,11 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
|
||||||
}
|
}
|
||||||
|
|
||||||
// regular domain
|
// regular domain
|
||||||
name = strings.TrimSuffix(name, ".") // golang.org/issue/18114
|
ck := certKey{
|
||||||
cert, err := m.cert(ctx, name)
|
domain: strings.TrimSuffix(name, "."), // golang.org/issue/18114
|
||||||
|
isRSA: !supportsECDSA(hello),
|
||||||
|
}
|
||||||
|
cert, err := m.cert(ctx, ck)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
@ -231,14 +286,71 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
|
||||||
if err := m.hostPolicy()(ctx, name); err != nil {
|
if err := m.hostPolicy()(ctx, name); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
cert, err = m.createCert(ctx, name)
|
cert, err = m.createCert(ctx, ck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
m.cachePut(ctx, name, cert)
|
m.cachePut(ctx, ck, cert)
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// wantsTokenCert reports whether a TLS request with SNI is made by a CA server
|
||||||
|
// for a challenge verification.
|
||||||
|
func wantsTokenCert(hello *tls.ClientHelloInfo) bool {
|
||||||
|
// tls-alpn-01
|
||||||
|
if len(hello.SupportedProtos) == 1 && hello.SupportedProtos[0] == acme.ALPNProto {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// tls-sni-xx
|
||||||
|
return strings.HasSuffix(hello.ServerName, ".acme.invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
func supportsECDSA(hello *tls.ClientHelloInfo) bool {
|
||||||
|
// The "signature_algorithms" extension, if present, limits the key exchange
|
||||||
|
// algorithms allowed by the cipher suites. See RFC 5246, section 7.4.1.4.1.
|
||||||
|
if hello.SignatureSchemes != nil {
|
||||||
|
ecdsaOK := false
|
||||||
|
schemeLoop:
|
||||||
|
for _, scheme := range hello.SignatureSchemes {
|
||||||
|
const tlsECDSAWithSHA1 tls.SignatureScheme = 0x0203 // constant added in Go 1.10
|
||||||
|
switch scheme {
|
||||||
|
case tlsECDSAWithSHA1, tls.ECDSAWithP256AndSHA256,
|
||||||
|
tls.ECDSAWithP384AndSHA384, tls.ECDSAWithP521AndSHA512:
|
||||||
|
ecdsaOK = true
|
||||||
|
break schemeLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ecdsaOK {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hello.SupportedCurves != nil {
|
||||||
|
ecdsaOK := false
|
||||||
|
for _, curve := range hello.SupportedCurves {
|
||||||
|
if curve == tls.CurveP256 {
|
||||||
|
ecdsaOK = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ecdsaOK {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, suite := range hello.CipherSuites {
|
||||||
|
switch suite {
|
||||||
|
case tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// HTTPHandler configures the Manager to provision ACME "http-01" challenge responses.
|
// HTTPHandler configures the Manager to provision ACME "http-01" challenge responses.
|
||||||
// It returns an http.Handler that responds to the challenges and must be
|
// It returns an http.Handler that responds to the challenges and must be
|
||||||
// running on port 80. If it receives a request that is not an ACME challenge,
|
// running on port 80. If it receives a request that is not an ACME challenge,
|
||||||
|
@ -252,8 +364,8 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
|
||||||
// Because the fallback handler is run with unencrypted port 80 requests,
|
// Because the fallback handler is run with unencrypted port 80 requests,
|
||||||
// the fallback should not serve TLS-only requests.
|
// the fallback should not serve TLS-only requests.
|
||||||
//
|
//
|
||||||
// If HTTPHandler is never called, the Manager will only use TLS SNI
|
// If HTTPHandler is never called, the Manager will only use the "tls-alpn-01"
|
||||||
// challenges for domain verification.
|
// challenge for domain verification.
|
||||||
func (m *Manager) HTTPHandler(fallback http.Handler) http.Handler {
|
func (m *Manager) HTTPHandler(fallback http.Handler) http.Handler {
|
||||||
m.tokensMu.Lock()
|
m.tokensMu.Lock()
|
||||||
defer m.tokensMu.Unlock()
|
defer m.tokensMu.Unlock()
|
||||||
|
@ -304,16 +416,16 @@ func stripPort(hostport string) string {
|
||||||
// cert returns an existing certificate either from m.state or cache.
|
// cert returns an existing certificate either from m.state or cache.
|
||||||
// If a certificate is found in cache but not in m.state, the latter will be filled
|
// If a certificate is found in cache but not in m.state, the latter will be filled
|
||||||
// with the cached value.
|
// with the cached value.
|
||||||
func (m *Manager) cert(ctx context.Context, name string) (*tls.Certificate, error) {
|
func (m *Manager) cert(ctx context.Context, ck certKey) (*tls.Certificate, error) {
|
||||||
m.stateMu.Lock()
|
m.stateMu.Lock()
|
||||||
if s, ok := m.state[name]; ok {
|
if s, ok := m.state[ck]; ok {
|
||||||
m.stateMu.Unlock()
|
m.stateMu.Unlock()
|
||||||
s.RLock()
|
s.RLock()
|
||||||
defer s.RUnlock()
|
defer s.RUnlock()
|
||||||
return s.tlscert()
|
return s.tlscert()
|
||||||
}
|
}
|
||||||
defer m.stateMu.Unlock()
|
defer m.stateMu.Unlock()
|
||||||
cert, err := m.cacheGet(ctx, name)
|
cert, err := m.cacheGet(ctx, ck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -322,25 +434,25 @@ func (m *Manager) cert(ctx context.Context, name string) (*tls.Certificate, erro
|
||||||
return nil, errors.New("acme/autocert: private key cannot sign")
|
return nil, errors.New("acme/autocert: private key cannot sign")
|
||||||
}
|
}
|
||||||
if m.state == nil {
|
if m.state == nil {
|
||||||
m.state = make(map[string]*certState)
|
m.state = make(map[certKey]*certState)
|
||||||
}
|
}
|
||||||
s := &certState{
|
s := &certState{
|
||||||
key: signer,
|
key: signer,
|
||||||
cert: cert.Certificate,
|
cert: cert.Certificate,
|
||||||
leaf: cert.Leaf,
|
leaf: cert.Leaf,
|
||||||
}
|
}
|
||||||
m.state[name] = s
|
m.state[ck] = s
|
||||||
go m.renew(name, s.key, s.leaf.NotAfter)
|
go m.renew(ck, s.key, s.leaf.NotAfter)
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// cacheGet always returns a valid certificate, or an error otherwise.
|
// cacheGet always returns a valid certificate, or an error otherwise.
|
||||||
// If a cached certficate exists but is not valid, ErrCacheMiss is returned.
|
// If a cached certificate exists but is not valid, ErrCacheMiss is returned.
|
||||||
func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate, error) {
|
func (m *Manager) cacheGet(ctx context.Context, ck certKey) (*tls.Certificate, error) {
|
||||||
if m.Cache == nil {
|
if m.Cache == nil {
|
||||||
return nil, ErrCacheMiss
|
return nil, ErrCacheMiss
|
||||||
}
|
}
|
||||||
data, err := m.Cache.Get(ctx, domain)
|
data, err := m.Cache.Get(ctx, ck.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -371,7 +483,7 @@ func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify and create TLS cert
|
// verify and create TLS cert
|
||||||
leaf, err := validCert(domain, pubDER, privKey)
|
leaf, err := validCert(ck, pubDER, privKey, m.now())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrCacheMiss
|
return nil, ErrCacheMiss
|
||||||
}
|
}
|
||||||
|
@ -383,7 +495,7 @@ func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate
|
||||||
return tlscert, nil
|
return tlscert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) cachePut(ctx context.Context, domain string, tlscert *tls.Certificate) error {
|
func (m *Manager) cachePut(ctx context.Context, ck certKey, tlscert *tls.Certificate) error {
|
||||||
if m.Cache == nil {
|
if m.Cache == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -415,7 +527,7 @@ func (m *Manager) cachePut(ctx context.Context, domain string, tlscert *tls.Cert
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.Cache.Put(ctx, domain, buf.Bytes())
|
return m.Cache.Put(ctx, ck.String(), buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeECDSAKey(w io.Writer, key *ecdsa.PrivateKey) error {
|
func encodeECDSAKey(w io.Writer, key *ecdsa.PrivateKey) error {
|
||||||
|
@ -432,9 +544,9 @@ func encodeECDSAKey(w io.Writer, key *ecdsa.PrivateKey) error {
|
||||||
//
|
//
|
||||||
// If the domain is already being verified, it waits for the existing verification to complete.
|
// If the domain is already being verified, it waits for the existing verification to complete.
|
||||||
// Either way, createCert blocks for the duration of the whole process.
|
// Either way, createCert blocks for the duration of the whole process.
|
||||||
func (m *Manager) createCert(ctx context.Context, domain string) (*tls.Certificate, error) {
|
func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate, error) {
|
||||||
// TODO: maybe rewrite this whole piece using sync.Once
|
// TODO: maybe rewrite this whole piece using sync.Once
|
||||||
state, err := m.certState(domain)
|
state, err := m.certState(ck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -452,44 +564,44 @@ func (m *Manager) createCert(ctx context.Context, domain string) (*tls.Certifica
|
||||||
defer state.Unlock()
|
defer state.Unlock()
|
||||||
state.locked = false
|
state.locked = false
|
||||||
|
|
||||||
der, leaf, err := m.authorizedCert(ctx, state.key, domain)
|
der, leaf, err := m.authorizedCert(ctx, state.key, ck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Remove the failed state after some time,
|
// Remove the failed state after some time,
|
||||||
// making the manager call createCert again on the following TLS hello.
|
// making the manager call createCert again on the following TLS hello.
|
||||||
time.AfterFunc(createCertRetryAfter, func() {
|
time.AfterFunc(createCertRetryAfter, func() {
|
||||||
defer testDidRemoveState(domain)
|
defer testDidRemoveState(ck)
|
||||||
m.stateMu.Lock()
|
m.stateMu.Lock()
|
||||||
defer m.stateMu.Unlock()
|
defer m.stateMu.Unlock()
|
||||||
// Verify the state hasn't changed and it's still invalid
|
// Verify the state hasn't changed and it's still invalid
|
||||||
// before deleting.
|
// before deleting.
|
||||||
s, ok := m.state[domain]
|
s, ok := m.state[ck]
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if _, err := validCert(domain, s.cert, s.key); err == nil {
|
if _, err := validCert(ck, s.cert, s.key, m.now()); err == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
delete(m.state, domain)
|
delete(m.state, ck)
|
||||||
})
|
})
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
state.cert = der
|
state.cert = der
|
||||||
state.leaf = leaf
|
state.leaf = leaf
|
||||||
go m.renew(domain, state.key, state.leaf.NotAfter)
|
go m.renew(ck, state.key, state.leaf.NotAfter)
|
||||||
return state.tlscert()
|
return state.tlscert()
|
||||||
}
|
}
|
||||||
|
|
||||||
// certState returns a new or existing certState.
|
// certState returns a new or existing certState.
|
||||||
// If a new certState is returned, state.exist is false and the state is locked.
|
// If a new certState is returned, state.exist is false and the state is locked.
|
||||||
// The returned error is non-nil only in the case where a new state could not be created.
|
// The returned error is non-nil only in the case where a new state could not be created.
|
||||||
func (m *Manager) certState(domain string) (*certState, error) {
|
func (m *Manager) certState(ck certKey) (*certState, error) {
|
||||||
m.stateMu.Lock()
|
m.stateMu.Lock()
|
||||||
defer m.stateMu.Unlock()
|
defer m.stateMu.Unlock()
|
||||||
if m.state == nil {
|
if m.state == nil {
|
||||||
m.state = make(map[string]*certState)
|
m.state = make(map[certKey]*certState)
|
||||||
}
|
}
|
||||||
// existing state
|
// existing state
|
||||||
if state, ok := m.state[domain]; ok {
|
if state, ok := m.state[ck]; ok {
|
||||||
return state, nil
|
return state, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,7 +610,7 @@ func (m *Manager) certState(domain string) (*certState, error) {
|
||||||
err error
|
err error
|
||||||
key crypto.Signer
|
key crypto.Signer
|
||||||
)
|
)
|
||||||
if m.ForceRSA {
|
if ck.isRSA {
|
||||||
key, err = rsa.GenerateKey(rand.Reader, 2048)
|
key, err = rsa.GenerateKey(rand.Reader, 2048)
|
||||||
} else {
|
} else {
|
||||||
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
@ -512,22 +624,22 @@ func (m *Manager) certState(domain string) (*certState, error) {
|
||||||
locked: true,
|
locked: true,
|
||||||
}
|
}
|
||||||
state.Lock() // will be unlocked by m.certState caller
|
state.Lock() // will be unlocked by m.certState caller
|
||||||
m.state[domain] = state
|
m.state[ck] = state
|
||||||
return state, nil
|
return state, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// authorizedCert starts the domain ownership verification process and requests a new cert upon success.
|
// authorizedCert starts the domain ownership verification process and requests a new cert upon success.
|
||||||
// The key argument is the certificate private key.
|
// The key argument is the certificate private key.
|
||||||
func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain string) (der [][]byte, leaf *x509.Certificate, err error) {
|
func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, ck certKey) (der [][]byte, leaf *x509.Certificate, err error) {
|
||||||
client, err := m.acmeClient(ctx)
|
client, err := m.acmeClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := m.verify(ctx, client, domain); err != nil {
|
if err := m.verify(ctx, client, ck.domain); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
csr, err := certRequest(key, domain)
|
csr, err := certRequest(key, ck.domain, m.ExtraExtensions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -535,25 +647,55 @@ func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
leaf, err = validCert(domain, der, key)
|
leaf, err = validCert(ck, der, key, m.now())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
return der, leaf, nil
|
return der, leaf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// revokePendingAuthz revokes all authorizations idenfied by the elements of uri slice.
|
||||||
|
// It ignores revocation errors.
|
||||||
|
func (m *Manager) revokePendingAuthz(ctx context.Context, uri []string) {
|
||||||
|
client, err := m.acmeClient(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, u := range uri {
|
||||||
|
client.RevokeAuthorization(ctx, u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// verify runs the identifier (domain) authorization flow
|
// verify runs the identifier (domain) authorization flow
|
||||||
// using each applicable ACME challenge type.
|
// using each applicable ACME challenge type.
|
||||||
func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string) error {
|
func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string) error {
|
||||||
// The list of challenge types we'll try to fulfill
|
// The list of challenge types we'll try to fulfill
|
||||||
// in this specific order.
|
// in this specific order.
|
||||||
challengeTypes := []string{"tls-sni-02", "tls-sni-01"}
|
challengeTypes := []string{"tls-alpn-01", "tls-sni-02", "tls-sni-01"}
|
||||||
m.tokensMu.RLock()
|
m.tokensMu.RLock()
|
||||||
if m.tryHTTP01 {
|
if m.tryHTTP01 {
|
||||||
challengeTypes = append(challengeTypes, "http-01")
|
challengeTypes = append(challengeTypes, "http-01")
|
||||||
}
|
}
|
||||||
m.tokensMu.RUnlock()
|
m.tokensMu.RUnlock()
|
||||||
|
|
||||||
|
// Keep track of pending authzs and revoke the ones that did not validate.
|
||||||
|
pendingAuthzs := make(map[string]bool)
|
||||||
|
defer func() {
|
||||||
|
var uri []string
|
||||||
|
for k, pending := range pendingAuthzs {
|
||||||
|
if pending {
|
||||||
|
uri = append(uri, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(uri) > 0 {
|
||||||
|
// Use "detached" background context.
|
||||||
|
// The revocations need not happen in the current verification flow.
|
||||||
|
go m.revokePendingAuthz(context.Background(), uri)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// errs accumulates challenge failure errors, printed if all fail
|
||||||
|
errs := make(map[*acme.Challenge]error)
|
||||||
var nextTyp int // challengeType index of the next challenge type to try
|
var nextTyp int // challengeType index of the next challenge type to try
|
||||||
for {
|
for {
|
||||||
// Start domain authorization and get the challenge.
|
// Start domain authorization and get the challenge.
|
||||||
|
@ -570,6 +712,8 @@ func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string
|
||||||
return fmt.Errorf("acme/autocert: invalid authorization %q", authz.URI)
|
return fmt.Errorf("acme/autocert: invalid authorization %q", authz.URI)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pendingAuthzs[authz.URI] = true
|
||||||
|
|
||||||
// Pick the next preferred challenge.
|
// Pick the next preferred challenge.
|
||||||
var chal *acme.Challenge
|
var chal *acme.Challenge
|
||||||
for chal == nil && nextTyp < len(challengeTypes) {
|
for chal == nil && nextTyp < len(challengeTypes) {
|
||||||
|
@ -577,28 +721,44 @@ func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string
|
||||||
nextTyp++
|
nextTyp++
|
||||||
}
|
}
|
||||||
if chal == nil {
|
if chal == nil {
|
||||||
return fmt.Errorf("acme/autocert: unable to authorize %q; tried %q", domain, challengeTypes)
|
errorMsg := fmt.Sprintf("acme/autocert: unable to authorize %q", domain)
|
||||||
|
for chal, err := range errs {
|
||||||
|
errorMsg += fmt.Sprintf("; challenge %q failed with error: %v", chal.Type, err)
|
||||||
|
}
|
||||||
|
return errors.New(errorMsg)
|
||||||
}
|
}
|
||||||
cleanup, err := m.fulfill(ctx, client, chal)
|
cleanup, err := m.fulfill(ctx, client, chal, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
errs[chal] = err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
if _, err := client.Accept(ctx, chal); err != nil {
|
if _, err := client.Accept(ctx, chal); err != nil {
|
||||||
|
errs[chal] = err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// A challenge is fulfilled and accepted: wait for the CA to validate.
|
// A challenge is fulfilled and accepted: wait for the CA to validate.
|
||||||
if _, err := client.WaitAuthorization(ctx, authz.URI); err == nil {
|
if _, err := client.WaitAuthorization(ctx, authz.URI); err != nil {
|
||||||
return nil
|
errs[chal] = err
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
delete(pendingAuthzs, authz.URI)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fulfill provisions a response to the challenge chal.
|
// fulfill provisions a response to the challenge chal.
|
||||||
// The cleanup is non-nil only if provisioning succeeded.
|
// The cleanup is non-nil only if provisioning succeeded.
|
||||||
func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.Challenge) (cleanup func(), err error) {
|
func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.Challenge, domain string) (cleanup func(), err error) {
|
||||||
switch chal.Type {
|
switch chal.Type {
|
||||||
|
case "tls-alpn-01":
|
||||||
|
cert, err := client.TLSALPN01ChallengeCert(chal.Token, domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m.putCertToken(ctx, domain, &cert)
|
||||||
|
return func() { go m.deleteCertToken(domain) }, nil
|
||||||
case "tls-sni-01":
|
case "tls-sni-01":
|
||||||
cert, name, err := client.TLSSNI01ChallengeCert(chal.Token)
|
cert, name, err := client.TLSSNI01ChallengeCert(chal.Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -634,8 +794,8 @@ func pickChallenge(typ string, chal []*acme.Challenge) *acme.Challenge {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// putCertToken stores the cert under the named key in both m.certTokens map
|
// putCertToken stores the token certificate with the specified name
|
||||||
// and m.Cache.
|
// in both m.certTokens map and m.Cache.
|
||||||
func (m *Manager) putCertToken(ctx context.Context, name string, cert *tls.Certificate) {
|
func (m *Manager) putCertToken(ctx context.Context, name string, cert *tls.Certificate) {
|
||||||
m.tokensMu.Lock()
|
m.tokensMu.Lock()
|
||||||
defer m.tokensMu.Unlock()
|
defer m.tokensMu.Unlock()
|
||||||
|
@ -643,17 +803,18 @@ func (m *Manager) putCertToken(ctx context.Context, name string, cert *tls.Certi
|
||||||
m.certTokens = make(map[string]*tls.Certificate)
|
m.certTokens = make(map[string]*tls.Certificate)
|
||||||
}
|
}
|
||||||
m.certTokens[name] = cert
|
m.certTokens[name] = cert
|
||||||
m.cachePut(ctx, name, cert)
|
m.cachePut(ctx, certKey{domain: name, isToken: true}, cert)
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteCertToken removes the token certificate for the specified domain name
|
// deleteCertToken removes the token certificate with the specified name
|
||||||
// from both m.certTokens map and m.Cache.
|
// from both m.certTokens map and m.Cache.
|
||||||
func (m *Manager) deleteCertToken(name string) {
|
func (m *Manager) deleteCertToken(name string) {
|
||||||
m.tokensMu.Lock()
|
m.tokensMu.Lock()
|
||||||
defer m.tokensMu.Unlock()
|
defer m.tokensMu.Unlock()
|
||||||
delete(m.certTokens, name)
|
delete(m.certTokens, name)
|
||||||
if m.Cache != nil {
|
if m.Cache != nil {
|
||||||
m.Cache.Delete(context.Background(), name)
|
ck := certKey{domain: name, isToken: true}
|
||||||
|
m.Cache.Delete(context.Background(), ck.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -704,7 +865,7 @@ func (m *Manager) deleteHTTPToken(tokenPath string) {
|
||||||
// httpTokenCacheKey returns a key at which an http-01 token value may be stored
|
// httpTokenCacheKey returns a key at which an http-01 token value may be stored
|
||||||
// in the Manager's optional Cache.
|
// in the Manager's optional Cache.
|
||||||
func httpTokenCacheKey(tokenPath string) string {
|
func httpTokenCacheKey(tokenPath string) string {
|
||||||
return "http-01-" + path.Base(tokenPath)
|
return path.Base(tokenPath) + "+http-01"
|
||||||
}
|
}
|
||||||
|
|
||||||
// renew starts a cert renewal timer loop, one per domain.
|
// renew starts a cert renewal timer loop, one per domain.
|
||||||
|
@ -715,18 +876,18 @@ func httpTokenCacheKey(tokenPath string) string {
|
||||||
//
|
//
|
||||||
// The key argument is a certificate private key.
|
// The key argument is a certificate private key.
|
||||||
// The exp argument is the cert expiration time (NotAfter).
|
// The exp argument is the cert expiration time (NotAfter).
|
||||||
func (m *Manager) renew(domain string, key crypto.Signer, exp time.Time) {
|
func (m *Manager) renew(ck certKey, key crypto.Signer, exp time.Time) {
|
||||||
m.renewalMu.Lock()
|
m.renewalMu.Lock()
|
||||||
defer m.renewalMu.Unlock()
|
defer m.renewalMu.Unlock()
|
||||||
if m.renewal[domain] != nil {
|
if m.renewal[ck] != nil {
|
||||||
// another goroutine is already on it
|
// another goroutine is already on it
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if m.renewal == nil {
|
if m.renewal == nil {
|
||||||
m.renewal = make(map[string]*domainRenewal)
|
m.renewal = make(map[certKey]*domainRenewal)
|
||||||
}
|
}
|
||||||
dr := &domainRenewal{m: m, domain: domain, key: key}
|
dr := &domainRenewal{m: m, ck: ck, key: key}
|
||||||
m.renewal[domain] = dr
|
m.renewal[ck] = dr
|
||||||
dr.start(exp)
|
dr.start(exp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -742,7 +903,10 @@ func (m *Manager) stopRenew() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) accountKey(ctx context.Context) (crypto.Signer, error) {
|
func (m *Manager) accountKey(ctx context.Context) (crypto.Signer, error) {
|
||||||
const keyName = "acme_account.key"
|
const keyName = "acme_account+key"
|
||||||
|
|
||||||
|
// Previous versions of autocert stored the value under a different key.
|
||||||
|
const legacyKeyName = "acme_account.key"
|
||||||
|
|
||||||
genKey := func() (*ecdsa.PrivateKey, error) {
|
genKey := func() (*ecdsa.PrivateKey, error) {
|
||||||
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
@ -753,6 +917,9 @@ func (m *Manager) accountKey(ctx context.Context) (crypto.Signer, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := m.Cache.Get(ctx, keyName)
|
data, err := m.Cache.Get(ctx, keyName)
|
||||||
|
if err == ErrCacheMiss {
|
||||||
|
data, err = m.Cache.Get(ctx, legacyKeyName)
|
||||||
|
}
|
||||||
if err == ErrCacheMiss {
|
if err == ErrCacheMiss {
|
||||||
key, err := genKey()
|
key, err := genKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -824,6 +991,13 @@ func (m *Manager) renewBefore() time.Duration {
|
||||||
return 720 * time.Hour // 30 days
|
return 720 * time.Hour // 30 days
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manager) now() time.Time {
|
||||||
|
if m.nowFunc != nil {
|
||||||
|
return m.nowFunc()
|
||||||
|
}
|
||||||
|
return time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
// certState is ready when its mutex is unlocked for reading.
|
// certState is ready when its mutex is unlocked for reading.
|
||||||
type certState struct {
|
type certState struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
|
@ -849,12 +1023,12 @@ func (s *certState) tlscert() (*tls.Certificate, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// certRequest creates a certificate request for the given common name cn
|
// certRequest generates a CSR for the given common name cn and optional SANs.
|
||||||
// and optional SANs.
|
func certRequest(key crypto.Signer, cn string, ext []pkix.Extension, san ...string) ([]byte, error) {
|
||||||
func certRequest(key crypto.Signer, cn string, san ...string) ([]byte, error) {
|
|
||||||
req := &x509.CertificateRequest{
|
req := &x509.CertificateRequest{
|
||||||
Subject: pkix.Name{CommonName: cn},
|
Subject: pkix.Name{CommonName: cn},
|
||||||
DNSNames: san,
|
DNSNames: san,
|
||||||
|
ExtraExtensions: ext,
|
||||||
}
|
}
|
||||||
return x509.CreateCertificateRequest(rand.Reader, req, key)
|
return x509.CreateCertificateRequest(rand.Reader, req, key)
|
||||||
}
|
}
|
||||||
|
@ -885,12 +1059,12 @@ func parsePrivateKey(der []byte) (crypto.Signer, error) {
|
||||||
return nil, errors.New("acme/autocert: failed to parse private key")
|
return nil, errors.New("acme/autocert: failed to parse private key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// validCert parses a cert chain provided as der argument and verifies the leaf, der[0],
|
// validCert parses a cert chain provided as der argument and verifies the leaf and der[0]
|
||||||
// corresponds to the private key, as well as the domain match and expiration dates.
|
// correspond to the private key, the domain and key type match, and expiration dates
|
||||||
// It doesn't do any revocation checking.
|
// are valid. It doesn't do any revocation checking.
|
||||||
//
|
//
|
||||||
// The returned value is the verified leaf cert.
|
// The returned value is the verified leaf cert.
|
||||||
func validCert(domain string, der [][]byte, key crypto.Signer) (leaf *x509.Certificate, err error) {
|
func validCert(ck certKey, der [][]byte, key crypto.Signer, now time.Time) (leaf *x509.Certificate, err error) {
|
||||||
// parse public part(s)
|
// parse public part(s)
|
||||||
var n int
|
var n int
|
||||||
for _, b := range der {
|
for _, b := range der {
|
||||||
|
@ -902,22 +1076,21 @@ func validCert(domain string, der [][]byte, key crypto.Signer) (leaf *x509.Certi
|
||||||
n += copy(pub[n:], b)
|
n += copy(pub[n:], b)
|
||||||
}
|
}
|
||||||
x509Cert, err := x509.ParseCertificates(pub)
|
x509Cert, err := x509.ParseCertificates(pub)
|
||||||
if len(x509Cert) == 0 {
|
if err != nil || len(x509Cert) == 0 {
|
||||||
return nil, errors.New("acme/autocert: no public key found")
|
return nil, errors.New("acme/autocert: no public key found")
|
||||||
}
|
}
|
||||||
// verify the leaf is not expired and matches the domain name
|
// verify the leaf is not expired and matches the domain name
|
||||||
leaf = x509Cert[0]
|
leaf = x509Cert[0]
|
||||||
now := timeNow()
|
|
||||||
if now.Before(leaf.NotBefore) {
|
if now.Before(leaf.NotBefore) {
|
||||||
return nil, errors.New("acme/autocert: certificate is not valid yet")
|
return nil, errors.New("acme/autocert: certificate is not valid yet")
|
||||||
}
|
}
|
||||||
if now.After(leaf.NotAfter) {
|
if now.After(leaf.NotAfter) {
|
||||||
return nil, errors.New("acme/autocert: expired certificate")
|
return nil, errors.New("acme/autocert: expired certificate")
|
||||||
}
|
}
|
||||||
if err := leaf.VerifyHostname(domain); err != nil {
|
if err := leaf.VerifyHostname(ck.domain); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// ensure the leaf corresponds to the private key
|
// ensure the leaf corresponds to the private key and matches the certKey type
|
||||||
switch pub := leaf.PublicKey.(type) {
|
switch pub := leaf.PublicKey.(type) {
|
||||||
case *rsa.PublicKey:
|
case *rsa.PublicKey:
|
||||||
prv, ok := key.(*rsa.PrivateKey)
|
prv, ok := key.(*rsa.PrivateKey)
|
||||||
|
@ -927,6 +1100,9 @@ func validCert(domain string, der [][]byte, key crypto.Signer) (leaf *x509.Certi
|
||||||
if pub.N.Cmp(prv.N) != 0 {
|
if pub.N.Cmp(prv.N) != 0 {
|
||||||
return nil, errors.New("acme/autocert: private key does not match public key")
|
return nil, errors.New("acme/autocert: private key does not match public key")
|
||||||
}
|
}
|
||||||
|
if !ck.isRSA && !ck.isToken {
|
||||||
|
return nil, errors.New("acme/autocert: key type does not match expected value")
|
||||||
|
}
|
||||||
case *ecdsa.PublicKey:
|
case *ecdsa.PublicKey:
|
||||||
prv, ok := key.(*ecdsa.PrivateKey)
|
prv, ok := key.(*ecdsa.PrivateKey)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -935,6 +1111,9 @@ func validCert(domain string, der [][]byte, key crypto.Signer) (leaf *x509.Certi
|
||||||
if pub.X.Cmp(prv.X) != 0 || pub.Y.Cmp(prv.Y) != 0 {
|
if pub.X.Cmp(prv.X) != 0 || pub.Y.Cmp(prv.Y) != 0 {
|
||||||
return nil, errors.New("acme/autocert: private key does not match public key")
|
return nil, errors.New("acme/autocert: private key does not match public key")
|
||||||
}
|
}
|
||||||
|
if ck.isRSA && !ck.isToken {
|
||||||
|
return nil, errors.New("acme/autocert: key type does not match expected value")
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("acme/autocert: unknown public key algorithm")
|
return nil, errors.New("acme/autocert: unknown public key algorithm")
|
||||||
}
|
}
|
||||||
|
@ -955,8 +1134,6 @@ func (r *lockedMathRand) int63n(max int64) int64 {
|
||||||
|
|
||||||
// For easier testing.
|
// For easier testing.
|
||||||
var (
|
var (
|
||||||
timeNow = time.Now
|
|
||||||
|
|
||||||
// Called when a state is removed.
|
// Called when a state is removed.
|
||||||
testDidRemoveState = func(domain string) {}
|
testDidRemoveState = func(certKey) {}
|
||||||
)
|
)
|
||||||
|
|
6
vendor/golang.org/x/crypto/acme/autocert/cache.go
generated
vendored
6
vendor/golang.org/x/crypto/acme/autocert/cache.go
generated
vendored
|
@ -16,10 +16,10 @@ import (
|
||||||
var ErrCacheMiss = errors.New("acme/autocert: certificate cache miss")
|
var ErrCacheMiss = errors.New("acme/autocert: certificate cache miss")
|
||||||
|
|
||||||
// Cache is used by Manager to store and retrieve previously obtained certificates
|
// Cache is used by Manager to store and retrieve previously obtained certificates
|
||||||
// as opaque data.
|
// and other account data as opaque blobs.
|
||||||
//
|
//
|
||||||
// The key argument of the methods refers to a domain name but need not be an FQDN.
|
// Cache implementations should not rely on the key naming pattern. Keys can
|
||||||
// Cache implementations should not rely on the key naming pattern.
|
// include any printable ASCII characters, except the following: \/:*?"<>|
|
||||||
type Cache interface {
|
type Cache interface {
|
||||||
// Get returns a certificate data for the specified key.
|
// Get returns a certificate data for the specified key.
|
||||||
// If there's no such key, Get returns ErrCacheMiss.
|
// If there's no such key, Get returns ErrCacheMiss.
|
||||||
|
|
7
vendor/golang.org/x/crypto/acme/autocert/listener.go
generated
vendored
7
vendor/golang.org/x/crypto/acme/autocert/listener.go
generated
vendored
|
@ -72,11 +72,8 @@ func NewListener(domains ...string) net.Listener {
|
||||||
// the Manager m's Prompt, Cache, HostPolicy, and other desired options.
|
// the Manager m's Prompt, Cache, HostPolicy, and other desired options.
|
||||||
func (m *Manager) Listener() net.Listener {
|
func (m *Manager) Listener() net.Listener {
|
||||||
ln := &listener{
|
ln := &listener{
|
||||||
m: m,
|
m: m,
|
||||||
conf: &tls.Config{
|
conf: m.TLSConfig(),
|
||||||
GetCertificate: m.GetCertificate, // bonus: panic on nil m
|
|
||||||
NextProtos: []string{"h2", "http/1.1"}, // Enable HTTP/2
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443")
|
ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443")
|
||||||
return ln
|
return ln
|
||||||
|
|
43
vendor/golang.org/x/crypto/acme/autocert/renewal.go
generated
vendored
43
vendor/golang.org/x/crypto/acme/autocert/renewal.go
generated
vendored
|
@ -17,9 +17,9 @@ const renewJitter = time.Hour
|
||||||
// domainRenewal tracks the state used by the periodic timers
|
// domainRenewal tracks the state used by the periodic timers
|
||||||
// renewing a single domain's cert.
|
// renewing a single domain's cert.
|
||||||
type domainRenewal struct {
|
type domainRenewal struct {
|
||||||
m *Manager
|
m *Manager
|
||||||
domain string
|
ck certKey
|
||||||
key crypto.Signer
|
key crypto.Signer
|
||||||
|
|
||||||
timerMu sync.Mutex
|
timerMu sync.Mutex
|
||||||
timer *time.Timer
|
timer *time.Timer
|
||||||
|
@ -71,25 +71,43 @@ func (dr *domainRenewal) renew() {
|
||||||
testDidRenewLoop(next, err)
|
testDidRenewLoop(next, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateState locks and replaces the relevant Manager.state item with the given
|
||||||
|
// state. It additionally updates dr.key with the given state's key.
|
||||||
|
func (dr *domainRenewal) updateState(state *certState) {
|
||||||
|
dr.m.stateMu.Lock()
|
||||||
|
defer dr.m.stateMu.Unlock()
|
||||||
|
dr.key = state.key
|
||||||
|
dr.m.state[dr.ck] = state
|
||||||
|
}
|
||||||
|
|
||||||
// do is similar to Manager.createCert but it doesn't lock a Manager.state item.
|
// do is similar to Manager.createCert but it doesn't lock a Manager.state item.
|
||||||
// Instead, it requests a new certificate independently and, upon success,
|
// Instead, it requests a new certificate independently and, upon success,
|
||||||
// replaces dr.m.state item with a new one and updates cache for the given domain.
|
// replaces dr.m.state item with a new one and updates cache for the given domain.
|
||||||
//
|
//
|
||||||
// It may return immediately if the expiration date of the currently cached cert
|
// It may lock and update the Manager.state if the expiration date of the currently
|
||||||
// is far enough in the future.
|
// cached cert is far enough in the future.
|
||||||
//
|
//
|
||||||
// The returned value is a time interval after which the renewal should occur again.
|
// The returned value is a time interval after which the renewal should occur again.
|
||||||
func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
|
func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
|
||||||
// a race is likely unavoidable in a distributed environment
|
// a race is likely unavoidable in a distributed environment
|
||||||
// but we try nonetheless
|
// but we try nonetheless
|
||||||
if tlscert, err := dr.m.cacheGet(ctx, dr.domain); err == nil {
|
if tlscert, err := dr.m.cacheGet(ctx, dr.ck); err == nil {
|
||||||
next := dr.next(tlscert.Leaf.NotAfter)
|
next := dr.next(tlscert.Leaf.NotAfter)
|
||||||
if next > dr.m.renewBefore()+renewJitter {
|
if next > dr.m.renewBefore()+renewJitter {
|
||||||
return next, nil
|
signer, ok := tlscert.PrivateKey.(crypto.Signer)
|
||||||
|
if ok {
|
||||||
|
state := &certState{
|
||||||
|
key: signer,
|
||||||
|
cert: tlscert.Certificate,
|
||||||
|
leaf: tlscert.Leaf,
|
||||||
|
}
|
||||||
|
dr.updateState(state)
|
||||||
|
return next, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
der, leaf, err := dr.m.authorizedCert(ctx, dr.key, dr.domain)
|
der, leaf, err := dr.m.authorizedCert(ctx, dr.key, dr.ck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -102,18 +120,15 @@ func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if err := dr.m.cachePut(ctx, dr.domain, tlscert); err != nil {
|
if err := dr.m.cachePut(ctx, dr.ck, tlscert); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
dr.m.stateMu.Lock()
|
dr.updateState(state)
|
||||||
defer dr.m.stateMu.Unlock()
|
|
||||||
// m.state is guaranteed to be non-nil at this point
|
|
||||||
dr.m.state[dr.domain] = state
|
|
||||||
return dr.next(leaf.NotAfter), nil
|
return dr.next(leaf.NotAfter), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dr *domainRenewal) next(expiry time.Time) time.Duration {
|
func (dr *domainRenewal) next(expiry time.Time) time.Duration {
|
||||||
d := expiry.Sub(timeNow()) - dr.m.renewBefore()
|
d := expiry.Sub(dr.m.now()) - dr.m.renewBefore()
|
||||||
// add a bit of randomness to renew deadline
|
// add a bit of randomness to renew deadline
|
||||||
n := pseudoRand.int63n(int64(renewJitter))
|
n := pseudoRand.int63n(int64(renewJitter))
|
||||||
d -= time.Duration(n)
|
d -= time.Duration(n)
|
||||||
|
|
281
vendor/golang.org/x/crypto/acme/http.go
generated
vendored
Normal file
281
vendor/golang.org/x/crypto/acme/http.go
generated
vendored
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/big"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// retryTimer encapsulates common logic for retrying unsuccessful requests.
|
||||||
|
// It is not safe for concurrent use.
|
||||||
|
type retryTimer struct {
|
||||||
|
// backoffFn provides backoff delay sequence for retries.
|
||||||
|
// See Client.RetryBackoff doc comment.
|
||||||
|
backoffFn func(n int, r *http.Request, res *http.Response) time.Duration
|
||||||
|
// n is the current retry attempt.
|
||||||
|
n int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *retryTimer) inc() {
|
||||||
|
t.n++
|
||||||
|
}
|
||||||
|
|
||||||
|
// backoff pauses the current goroutine as described in Client.RetryBackoff.
|
||||||
|
func (t *retryTimer) backoff(ctx context.Context, r *http.Request, res *http.Response) error {
|
||||||
|
d := t.backoffFn(t.n, r, res)
|
||||||
|
if d <= 0 {
|
||||||
|
return fmt.Errorf("acme: no more retries for %s; tried %d time(s)", r.URL, t.n)
|
||||||
|
}
|
||||||
|
wakeup := time.NewTimer(d)
|
||||||
|
defer wakeup.Stop()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-wakeup.C:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) retryTimer() *retryTimer {
|
||||||
|
f := c.RetryBackoff
|
||||||
|
if f == nil {
|
||||||
|
f = defaultBackoff
|
||||||
|
}
|
||||||
|
return &retryTimer{backoffFn: f}
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultBackoff provides default Client.RetryBackoff implementation
|
||||||
|
// using a truncated exponential backoff algorithm,
|
||||||
|
// as described in Client.RetryBackoff.
|
||||||
|
//
|
||||||
|
// The n argument is always bounded between 1 and 30.
|
||||||
|
// The returned value is always greater than 0.
|
||||||
|
func defaultBackoff(n int, r *http.Request, res *http.Response) time.Duration {
|
||||||
|
const max = 10 * time.Second
|
||||||
|
var jitter time.Duration
|
||||||
|
if x, err := rand.Int(rand.Reader, big.NewInt(1000)); err == nil {
|
||||||
|
// Set the minimum to 1ms to avoid a case where
|
||||||
|
// an invalid Retry-After value is parsed into 0 below,
|
||||||
|
// resulting in the 0 returned value which would unintentionally
|
||||||
|
// stop the retries.
|
||||||
|
jitter = (1 + time.Duration(x.Int64())) * time.Millisecond
|
||||||
|
}
|
||||||
|
if v, ok := res.Header["Retry-After"]; ok {
|
||||||
|
return retryAfter(v[0]) + jitter
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < 1 {
|
||||||
|
n = 1
|
||||||
|
}
|
||||||
|
if n > 30 {
|
||||||
|
n = 30
|
||||||
|
}
|
||||||
|
d := time.Duration(1<<uint(n-1))*time.Second + jitter
|
||||||
|
if d > max {
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// retryAfter parses a Retry-After HTTP header value,
|
||||||
|
// trying to convert v into an int (seconds) or use http.ParseTime otherwise.
|
||||||
|
// It returns zero value if v cannot be parsed.
|
||||||
|
func retryAfter(v string) time.Duration {
|
||||||
|
if i, err := strconv.Atoi(v); err == nil {
|
||||||
|
return time.Duration(i) * time.Second
|
||||||
|
}
|
||||||
|
t, err := http.ParseTime(v)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return t.Sub(timeNow())
|
||||||
|
}
|
||||||
|
|
||||||
|
// resOkay is a function that reports whether the provided response is okay.
|
||||||
|
// It is expected to keep the response body unread.
|
||||||
|
type resOkay func(*http.Response) bool
|
||||||
|
|
||||||
|
// wantStatus returns a function which reports whether the code
|
||||||
|
// matches the status code of a response.
|
||||||
|
func wantStatus(codes ...int) resOkay {
|
||||||
|
return func(res *http.Response) bool {
|
||||||
|
for _, code := range codes {
|
||||||
|
if code == res.StatusCode {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get issues an unsigned GET request to the specified URL.
|
||||||
|
// It returns a non-error value only when ok reports true.
|
||||||
|
//
|
||||||
|
// get retries unsuccessful attempts according to c.RetryBackoff
|
||||||
|
// until the context is done or a non-retriable error is received.
|
||||||
|
func (c *Client) get(ctx context.Context, url string, ok resOkay) (*http.Response, error) {
|
||||||
|
retry := c.retryTimer()
|
||||||
|
for {
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res, err := c.doNoRetry(ctx, req)
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return nil, err
|
||||||
|
case ok(res):
|
||||||
|
return res, nil
|
||||||
|
case isRetriable(res.StatusCode):
|
||||||
|
retry.inc()
|
||||||
|
resErr := responseError(res)
|
||||||
|
res.Body.Close()
|
||||||
|
// Ignore the error value from retry.backoff
|
||||||
|
// and return the one from last retry, as received from the CA.
|
||||||
|
if retry.backoff(ctx, req, res) != nil {
|
||||||
|
return nil, resErr
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
defer res.Body.Close()
|
||||||
|
return nil, responseError(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// post issues a signed POST request in JWS format using the provided key
|
||||||
|
// to the specified URL.
|
||||||
|
// It returns a non-error value only when ok reports true.
|
||||||
|
//
|
||||||
|
// post retries unsuccessful attempts according to c.RetryBackoff
|
||||||
|
// until the context is done or a non-retriable error is received.
|
||||||
|
// It uses postNoRetry to make individual requests.
|
||||||
|
func (c *Client) post(ctx context.Context, key crypto.Signer, url string, body interface{}, ok resOkay) (*http.Response, error) {
|
||||||
|
retry := c.retryTimer()
|
||||||
|
for {
|
||||||
|
res, req, err := c.postNoRetry(ctx, key, url, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ok(res) {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
resErr := responseError(res)
|
||||||
|
res.Body.Close()
|
||||||
|
switch {
|
||||||
|
// Check for bad nonce before isRetriable because it may have been returned
|
||||||
|
// with an unretriable response code such as 400 Bad Request.
|
||||||
|
case isBadNonce(resErr):
|
||||||
|
// Consider any previously stored nonce values to be invalid.
|
||||||
|
c.clearNonces()
|
||||||
|
case !isRetriable(res.StatusCode):
|
||||||
|
return nil, resErr
|
||||||
|
}
|
||||||
|
retry.inc()
|
||||||
|
// Ignore the error value from retry.backoff
|
||||||
|
// and return the one from last retry, as received from the CA.
|
||||||
|
if err := retry.backoff(ctx, req, res); err != nil {
|
||||||
|
return nil, resErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// postNoRetry signs the body with the given key and POSTs it to the provided url.
|
||||||
|
// The body argument must be JSON-serializable.
|
||||||
|
// It is used by c.post to retry unsuccessful attempts.
|
||||||
|
func (c *Client) postNoRetry(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, *http.Request, error) {
|
||||||
|
nonce, err := c.popNonce(ctx, url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
b, err := jwsEncodeJSON(body, key, nonce)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest("POST", url, bytes.NewReader(b))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/jose+json")
|
||||||
|
res, err := c.doNoRetry(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
c.addNonce(res.Header)
|
||||||
|
return res, req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// doNoRetry issues a request req, replacing its context (if any) with ctx.
|
||||||
|
func (c *Client) doNoRetry(ctx context.Context, req *http.Request) (*http.Response, error) {
|
||||||
|
res, err := c.httpClient().Do(req.WithContext(ctx))
|
||||||
|
if err != nil {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
// Prefer the unadorned context error.
|
||||||
|
// (The acme package had tests assuming this, previously from ctxhttp's
|
||||||
|
// behavior, predating net/http supporting contexts natively)
|
||||||
|
// TODO(bradfitz): reconsider this in the future. But for now this
|
||||||
|
// requires no test updates.
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) httpClient() *http.Client {
|
||||||
|
if c.HTTPClient != nil {
|
||||||
|
return c.HTTPClient
|
||||||
|
}
|
||||||
|
return http.DefaultClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// isBadNonce reports whether err is an ACME "badnonce" error.
|
||||||
|
func isBadNonce(err error) bool {
|
||||||
|
// According to the spec badNonce is urn:ietf:params:acme:error:badNonce.
|
||||||
|
// However, ACME servers in the wild return their versions of the error.
|
||||||
|
// See https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-5.4
|
||||||
|
// and https://github.com/letsencrypt/boulder/blob/0e07eacb/docs/acme-divergences.md#section-66.
|
||||||
|
ae, ok := err.(*Error)
|
||||||
|
return ok && strings.HasSuffix(strings.ToLower(ae.ProblemType), ":badnonce")
|
||||||
|
}
|
||||||
|
|
||||||
|
// isRetriable reports whether a request can be retried
|
||||||
|
// based on the response status code.
|
||||||
|
//
|
||||||
|
// Note that a "bad nonce" error is returned with a non-retriable 400 Bad Request code.
|
||||||
|
// Callers should parse the response and check with isBadNonce.
|
||||||
|
func isRetriable(code int) bool {
|
||||||
|
return code <= 399 || code >= 500 || code == http.StatusTooManyRequests
|
||||||
|
}
|
||||||
|
|
||||||
|
// responseError creates an error of Error type from resp.
|
||||||
|
func responseError(resp *http.Response) error {
|
||||||
|
// don't care if ReadAll returns an error:
|
||||||
|
// json.Unmarshal will fail in that case anyway
|
||||||
|
b, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
e := &wireError{Status: resp.StatusCode}
|
||||||
|
if err := json.Unmarshal(b, e); err != nil {
|
||||||
|
// this is not a regular error response:
|
||||||
|
// populate detail with anything we received,
|
||||||
|
// e.Status will already contain HTTP response code value
|
||||||
|
e.Detail = string(b)
|
||||||
|
if e.Detail == "" {
|
||||||
|
e.Detail = resp.Status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e.error(resp.Header)
|
||||||
|
}
|
29
vendor/golang.org/x/crypto/acme/jws.go
generated
vendored
29
vendor/golang.org/x/crypto/acme/jws.go
generated
vendored
|
@ -25,7 +25,7 @@ func jwsEncodeJSON(claimset interface{}, key crypto.Signer, nonce string) ([]byt
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
alg, sha := jwsHasher(key)
|
alg, sha := jwsHasher(key.Public())
|
||||||
if alg == "" || !sha.Available() {
|
if alg == "" || !sha.Available() {
|
||||||
return nil, ErrUnsupportedKey
|
return nil, ErrUnsupportedKey
|
||||||
}
|
}
|
||||||
|
@ -97,13 +97,16 @@ func jwkEncode(pub crypto.PublicKey) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// jwsSign signs the digest using the given key.
|
// jwsSign signs the digest using the given key.
|
||||||
// It returns ErrUnsupportedKey if the key type is unknown.
|
// The hash is unused for ECDSA keys.
|
||||||
// The hash is used only for RSA keys.
|
//
|
||||||
|
// Note: non-stdlib crypto.Signer implementations are expected to return
|
||||||
|
// the signature in the format as specified in RFC7518.
|
||||||
|
// See https://tools.ietf.org/html/rfc7518 for more details.
|
||||||
func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) {
|
func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) {
|
||||||
switch key := key.(type) {
|
if key, ok := key.(*ecdsa.PrivateKey); ok {
|
||||||
case *rsa.PrivateKey:
|
// The key.Sign method of ecdsa returns ASN1-encoded signature.
|
||||||
return key.Sign(rand.Reader, digest, hash)
|
// So, we use the package Sign function instead
|
||||||
case *ecdsa.PrivateKey:
|
// to get R and S values directly and format the result accordingly.
|
||||||
r, s, err := ecdsa.Sign(rand.Reader, key, digest)
|
r, s, err := ecdsa.Sign(rand.Reader, key, digest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -118,18 +121,18 @@ func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error)
|
||||||
copy(sig[size*2-len(sb):], sb)
|
copy(sig[size*2-len(sb):], sb)
|
||||||
return sig, nil
|
return sig, nil
|
||||||
}
|
}
|
||||||
return nil, ErrUnsupportedKey
|
return key.Sign(rand.Reader, digest, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// jwsHasher indicates suitable JWS algorithm name and a hash function
|
// jwsHasher indicates suitable JWS algorithm name and a hash function
|
||||||
// to use for signing a digest with the provided key.
|
// to use for signing a digest with the provided key.
|
||||||
// It returns ("", 0) if the key is not supported.
|
// It returns ("", 0) if the key is not supported.
|
||||||
func jwsHasher(key crypto.Signer) (string, crypto.Hash) {
|
func jwsHasher(pub crypto.PublicKey) (string, crypto.Hash) {
|
||||||
switch key := key.(type) {
|
switch pub := pub.(type) {
|
||||||
case *rsa.PrivateKey:
|
case *rsa.PublicKey:
|
||||||
return "RS256", crypto.SHA256
|
return "RS256", crypto.SHA256
|
||||||
case *ecdsa.PrivateKey:
|
case *ecdsa.PublicKey:
|
||||||
switch key.Params().Name {
|
switch pub.Params().Name {
|
||||||
case "P-256":
|
case "P-256":
|
||||||
return "ES256", crypto.SHA256
|
return "ES256", crypto.SHA256
|
||||||
case "P-384":
|
case "P-384":
|
||||||
|
|
8
vendor/golang.org/x/crypto/acme/types.go
generated
vendored
8
vendor/golang.org/x/crypto/acme/types.go
generated
vendored
|
@ -104,7 +104,7 @@ func RateLimit(err error) (time.Duration, bool) {
|
||||||
if e.Header == nil {
|
if e.Header == nil {
|
||||||
return 0, true
|
return 0, true
|
||||||
}
|
}
|
||||||
return retryAfter(e.Header.Get("Retry-After"), 0), true
|
return retryAfter(e.Header.Get("Retry-After")), true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account is a user account. It is associated with a private key.
|
// Account is a user account. It is associated with a private key.
|
||||||
|
@ -296,8 +296,8 @@ func (e *wireError) error(h http.Header) *Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CertOption is an optional argument type for the TLSSNIxChallengeCert methods for
|
// CertOption is an optional argument type for the TLS ChallengeCert methods for
|
||||||
// customizing a temporary certificate for TLS-SNI challenges.
|
// customizing a temporary certificate for TLS-based challenges.
|
||||||
type CertOption interface {
|
type CertOption interface {
|
||||||
privateCertOpt()
|
privateCertOpt()
|
||||||
}
|
}
|
||||||
|
@ -317,7 +317,7 @@ func (*certOptKey) privateCertOpt() {}
|
||||||
// WithTemplate creates an option for specifying a certificate template.
|
// WithTemplate creates an option for specifying a certificate template.
|
||||||
// See x509.CreateCertificate for template usage details.
|
// See x509.CreateCertificate for template usage details.
|
||||||
//
|
//
|
||||||
// In TLSSNIxChallengeCert methods, the template is also used as parent,
|
// In TLS ChallengeCert methods, the template is also used as parent,
|
||||||
// resulting in a self-signed certificate.
|
// resulting in a self-signed certificate.
|
||||||
// The DNSNames field of t is always overwritten for tls-sni challenge certs.
|
// The DNSNames field of t is always overwritten for tls-sni challenge certs.
|
||||||
func WithTemplate(t *x509.Certificate) CertOption {
|
func WithTemplate(t *x509.Certificate) CertOption {
|
||||||
|
|
17
vendor/golang.org/x/sys/unix/asm_aix_ppc64.s
generated
vendored
Normal file
17
vendor/golang.org/x/sys/unix/asm_aix_ppc64.s
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !gccgo
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System calls for ppc64, AIX are implemented in runtime/syscall_aix.go
|
||||||
|
//
|
||||||
|
|
||||||
|
TEXT ·syscall6(SB),NOSPLIT,$0-88
|
||||||
|
JMP syscall·syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·rawSyscall6(SB),NOSPLIT,$0-88
|
||||||
|
JMP syscall·rawSyscall6(SB)
|
12
vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s
generated
vendored
12
vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s
generated
vendored
|
@ -15,12 +15,6 @@
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
// The runtime may know about them.
|
// The runtime may know about them.
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
|
||||||
BR syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
|
||||||
BR syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||||
BL runtime·entersyscall(SB)
|
BL runtime·entersyscall(SB)
|
||||||
MOVD a1+8(FP), R3
|
MOVD a1+8(FP), R3
|
||||||
|
@ -36,12 +30,6 @@ TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||||
BL runtime·exitsyscall(SB)
|
BL runtime·exitsyscall(SB)
|
||||||
RET
|
RET
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
|
||||||
BR syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
|
||||||
BR syscall·RawSyscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||||
MOVD a1+8(FP), R3
|
MOVD a1+8(FP), R3
|
||||||
MOVD a2+16(FP), R4
|
MOVD a2+16(FP), R4
|
||||||
|
|
12
vendor/golang.org/x/sys/unix/dev_aix_ppc.go
generated
vendored
12
vendor/golang.org/x/sys/unix/dev_aix_ppc.go
generated
vendored
|
@ -6,17 +6,7 @@
|
||||||
// +build ppc
|
// +build ppc
|
||||||
|
|
||||||
// Functions to access/create device major and minor numbers matching the
|
// Functions to access/create device major and minor numbers matching the
|
||||||
// encoding used by the Linux kernel and glibc.
|
// encoding used by AIX.
|
||||||
//
|
|
||||||
// The information below is extracted and adapted from bits/sysmacros.h in the
|
|
||||||
// glibc sources:
|
|
||||||
//
|
|
||||||
// dev_t in glibc is 64-bit, with 32-bit major and minor numbers. glibc's
|
|
||||||
// default encoding is MMMM Mmmm mmmM MMmm, where M is a hex digit of the major
|
|
||||||
// number and m is a hex digit of the minor number. This is backward compatible
|
|
||||||
// with legacy systems where dev_t is 16 bits wide, encoded as MMmm. It is also
|
|
||||||
// backward compatible with the Linux kernel, which for some architectures uses
|
|
||||||
// 32-bit dev_t, encoded as mmmM MMmm.
|
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
|
|
12
vendor/golang.org/x/sys/unix/dev_aix_ppc64.go
generated
vendored
12
vendor/golang.org/x/sys/unix/dev_aix_ppc64.go
generated
vendored
|
@ -6,17 +6,7 @@
|
||||||
// +build ppc64
|
// +build ppc64
|
||||||
|
|
||||||
// Functions to access/create device major and minor numbers matching the
|
// Functions to access/create device major and minor numbers matching the
|
||||||
// encoding used by the Linux kernel and glibc.
|
// encoding used AIX.
|
||||||
//
|
|
||||||
// The information below is extracted and adapted from bits/sysmacros.h in the
|
|
||||||
// glibc sources:
|
|
||||||
//
|
|
||||||
// dev_t in glibc is 64-bit, with 32-bit major and minor numbers. glibc's
|
|
||||||
// default encoding is MMMM Mmmm mmmM MMmm, where M is a hex digit of the major
|
|
||||||
// number and m is a hex digit of the minor number. This is backward compatible
|
|
||||||
// with legacy systems where dev_t is 16 bits wide, encoded as MMmm. It is also
|
|
||||||
// backward compatible with the Linux kernel, which for some architectures uses
|
|
||||||
// 32-bit dev_t, encoded as mmmM MMmm.
|
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
|
|
2
vendor/golang.org/x/sys/unix/ioctl.go
generated
vendored
2
vendor/golang.org/x/sys/unix/ioctl.go
generated
vendored
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
|
|
36
vendor/golang.org/x/sys/unix/mkall.sh
generated
vendored
36
vendor/golang.org/x/sys/unix/mkall.sh
generated
vendored
|
@ -10,7 +10,7 @@
|
||||||
GOOSARCH="${GOOS}_${GOARCH}"
|
GOOSARCH="${GOOS}_${GOARCH}"
|
||||||
|
|
||||||
# defaults
|
# defaults
|
||||||
mksyscall="./mksyscall.pl"
|
mksyscall="go run mksyscall.go"
|
||||||
mkerrors="./mkerrors.sh"
|
mkerrors="./mkerrors.sh"
|
||||||
zerrors="zerrors_$GOOSARCH.go"
|
zerrors="zerrors_$GOOSARCH.go"
|
||||||
mksysctl=""
|
mksysctl=""
|
||||||
|
@ -61,17 +61,17 @@ _* | *_ | _)
|
||||||
;;
|
;;
|
||||||
aix_ppc)
|
aix_ppc)
|
||||||
mkerrors="$mkerrors -maix32"
|
mkerrors="$mkerrors -maix32"
|
||||||
mksyscall="perl mksyscall_aix.pl -aix"
|
mksyscall="./mksyscall_aix_ppc.pl -aix"
|
||||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||||
;;
|
;;
|
||||||
aix_ppc64)
|
aix_ppc64)
|
||||||
mkerrors="$mkerrors -maix64"
|
mkerrors="$mkerrors -maix64"
|
||||||
mksyscall="perl mksyscall_aix.pl -aix"
|
mksyscall="./mksyscall_aix_ppc64.pl -aix"
|
||||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||||
;;
|
;;
|
||||||
darwin_386)
|
darwin_386)
|
||||||
mkerrors="$mkerrors -m32"
|
mkerrors="$mkerrors -m32"
|
||||||
mksyscall="./mksyscall.pl -l32"
|
mksyscall="go run mksyscall.go -l32"
|
||||||
mksysnum="./mksysnum_darwin.pl $(xcrun --show-sdk-path --sdk macosx)/usr/include/sys/syscall.h"
|
mksysnum="./mksysnum_darwin.pl $(xcrun --show-sdk-path --sdk macosx)/usr/include/sys/syscall.h"
|
||||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||||
;;
|
;;
|
||||||
|
@ -92,13 +92,13 @@ darwin_arm64)
|
||||||
;;
|
;;
|
||||||
dragonfly_amd64)
|
dragonfly_amd64)
|
||||||
mkerrors="$mkerrors -m64"
|
mkerrors="$mkerrors -m64"
|
||||||
mksyscall="./mksyscall.pl -dragonfly"
|
mksyscall="go run mksyscall.go -dragonfly"
|
||||||
mksysnum="curl -s 'http://gitweb.dragonflybsd.org/dragonfly.git/blob_plain/HEAD:/sys/kern/syscalls.master' | ./mksysnum_dragonfly.pl"
|
mksysnum="curl -s 'http://gitweb.dragonflybsd.org/dragonfly.git/blob_plain/HEAD:/sys/kern/syscalls.master' | ./mksysnum_dragonfly.pl"
|
||||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||||
;;
|
;;
|
||||||
freebsd_386)
|
freebsd_386)
|
||||||
mkerrors="$mkerrors -m32"
|
mkerrors="$mkerrors -m32"
|
||||||
mksyscall="./mksyscall.pl -l32"
|
mksyscall="go run mksyscall.go -l32"
|
||||||
mksysnum="curl -s 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl"
|
mksysnum="curl -s 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl"
|
||||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||||
;;
|
;;
|
||||||
|
@ -109,7 +109,7 @@ freebsd_amd64)
|
||||||
;;
|
;;
|
||||||
freebsd_arm)
|
freebsd_arm)
|
||||||
mkerrors="$mkerrors"
|
mkerrors="$mkerrors"
|
||||||
mksyscall="./mksyscall.pl -l32 -arm"
|
mksyscall="go run mksyscall.go -l32 -arm"
|
||||||
mksysnum="curl -s 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl"
|
mksysnum="curl -s 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl"
|
||||||
# Let the type of C char be signed for making the bare syscall
|
# Let the type of C char be signed for making the bare syscall
|
||||||
# API consistent across platforms.
|
# API consistent across platforms.
|
||||||
|
@ -124,19 +124,19 @@ linux_sparc64)
|
||||||
;;
|
;;
|
||||||
netbsd_386)
|
netbsd_386)
|
||||||
mkerrors="$mkerrors -m32"
|
mkerrors="$mkerrors -m32"
|
||||||
mksyscall="./mksyscall.pl -l32 -netbsd"
|
mksyscall="go run mksyscall.go -l32 -netbsd"
|
||||||
mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl"
|
mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl"
|
||||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||||
;;
|
;;
|
||||||
netbsd_amd64)
|
netbsd_amd64)
|
||||||
mkerrors="$mkerrors -m64"
|
mkerrors="$mkerrors -m64"
|
||||||
mksyscall="./mksyscall.pl -netbsd"
|
mksyscall="go run mksyscall.go -netbsd"
|
||||||
mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl"
|
mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl"
|
||||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||||
;;
|
;;
|
||||||
netbsd_arm)
|
netbsd_arm)
|
||||||
mkerrors="$mkerrors"
|
mkerrors="$mkerrors"
|
||||||
mksyscall="./mksyscall.pl -l32 -netbsd -arm"
|
mksyscall="go run mksyscall.go -l32 -netbsd -arm"
|
||||||
mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl"
|
mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl"
|
||||||
# Let the type of C char be signed for making the bare syscall
|
# Let the type of C char be signed for making the bare syscall
|
||||||
# API consistent across platforms.
|
# API consistent across platforms.
|
||||||
|
@ -144,21 +144,21 @@ netbsd_arm)
|
||||||
;;
|
;;
|
||||||
openbsd_386)
|
openbsd_386)
|
||||||
mkerrors="$mkerrors -m32"
|
mkerrors="$mkerrors -m32"
|
||||||
mksyscall="./mksyscall.pl -l32 -openbsd"
|
mksyscall="go run mksyscall.go -l32 -openbsd"
|
||||||
mksysctl="./mksysctl_openbsd.pl"
|
mksysctl="./mksysctl_openbsd.pl"
|
||||||
mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl"
|
mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl"
|
||||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||||
;;
|
;;
|
||||||
openbsd_amd64)
|
openbsd_amd64)
|
||||||
mkerrors="$mkerrors -m64"
|
mkerrors="$mkerrors -m64"
|
||||||
mksyscall="./mksyscall.pl -openbsd"
|
mksyscall="go run mksyscall.go -openbsd"
|
||||||
mksysctl="./mksysctl_openbsd.pl"
|
mksysctl="./mksysctl_openbsd.pl"
|
||||||
mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl"
|
mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl"
|
||||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||||
;;
|
;;
|
||||||
openbsd_arm)
|
openbsd_arm)
|
||||||
mkerrors="$mkerrors"
|
mkerrors="$mkerrors"
|
||||||
mksyscall="./mksyscall.pl -l32 -openbsd -arm"
|
mksyscall="go run mksyscall.go -l32 -openbsd -arm"
|
||||||
mksysctl="./mksysctl_openbsd.pl"
|
mksysctl="./mksysctl_openbsd.pl"
|
||||||
mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl"
|
mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl"
|
||||||
# Let the type of C char be signed for making the bare syscall
|
# Let the type of C char be signed for making the bare syscall
|
||||||
|
@ -187,8 +187,14 @@ esac
|
||||||
syscall_goos="syscall_bsd.go $syscall_goos"
|
syscall_goos="syscall_bsd.go $syscall_goos"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
if [ -n "$mksyscall" ]; then echo "$mksyscall -tags $GOOS,$GOARCH $syscall_goos $GOOSARCH_in |gofmt >zsyscall_$GOOSARCH.go"; fi
|
if [ -n "$mksyscall" ]; then
|
||||||
;;
|
if [ "$GOOSARCH" == "aix_ppc64" ]; then
|
||||||
|
# aix/ppc64 script generates files instead of writing to stdin.
|
||||||
|
echo "$mksyscall -tags $GOOS,$GOARCH $syscall_goos $GOOSARCH_in && gofmt -w zsyscall_$GOOSARCH.go && gofmt -w zsyscall_"$GOOSARCH"_gccgo.go && gofmt -w zsyscall_"$GOOSARCH"_gc.go " ;
|
||||||
|
else
|
||||||
|
echo "$mksyscall -tags $GOOS,$GOARCH $syscall_goos $GOOSARCH_in |gofmt >zsyscall_$GOOSARCH.go";
|
||||||
|
fi
|
||||||
|
fi
|
||||||
esac
|
esac
|
||||||
if [ -n "$mksysctl" ]; then echo "$mksysctl |gofmt >$zsysctl"; fi
|
if [ -n "$mksysctl" ]; then echo "$mksysctl |gofmt >$zsysctl"; fi
|
||||||
if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi
|
if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi
|
||||||
|
|
29
vendor/golang.org/x/sys/unix/mkerrors.sh
generated
vendored
29
vendor/golang.org/x/sys/unix/mkerrors.sh
generated
vendored
|
@ -46,6 +46,7 @@ includes_AIX='
|
||||||
#include <sys/stropts.h>
|
#include <sys/stropts.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
|
#include <sys/termio.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
@ -86,6 +87,7 @@ includes_DragonFly='
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <net/bpf.h>
|
#include <net/bpf.h>
|
||||||
|
@ -99,7 +101,7 @@ includes_DragonFly='
|
||||||
'
|
'
|
||||||
|
|
||||||
includes_FreeBSD='
|
includes_FreeBSD='
|
||||||
#include <sys/capability.h>
|
#include <sys/capsicum.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/event.h>
|
#include <sys/event.h>
|
||||||
|
@ -185,14 +187,18 @@ struct ltchars {
|
||||||
#include <linux/if_alg.h>
|
#include <linux/if_alg.h>
|
||||||
#include <linux/if_arp.h>
|
#include <linux/if_arp.h>
|
||||||
#include <linux/if_ether.h>
|
#include <linux/if_ether.h>
|
||||||
|
#include <linux/if_ppp.h>
|
||||||
#include <linux/if_tun.h>
|
#include <linux/if_tun.h>
|
||||||
#include <linux/if_packet.h>
|
#include <linux/if_packet.h>
|
||||||
#include <linux/if_addr.h>
|
#include <linux/if_addr.h>
|
||||||
#include <linux/falloc.h>
|
#include <linux/falloc.h>
|
||||||
#include <linux/filter.h>
|
#include <linux/filter.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
|
#include <linux/kexec.h>
|
||||||
#include <linux/keyctl.h>
|
#include <linux/keyctl.h>
|
||||||
#include <linux/magic.h>
|
#include <linux/magic.h>
|
||||||
|
#include <linux/memfd.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/netfilter/nfnetlink.h>
|
#include <linux/netfilter/nfnetlink.h>
|
||||||
#include <linux/netlink.h>
|
#include <linux/netlink.h>
|
||||||
#include <linux/net_namespace.h>
|
#include <linux/net_namespace.h>
|
||||||
|
@ -214,6 +220,7 @@ struct ltchars {
|
||||||
#include <linux/watchdog.h>
|
#include <linux/watchdog.h>
|
||||||
#include <linux/hdreg.h>
|
#include <linux/hdreg.h>
|
||||||
#include <linux/rtc.h>
|
#include <linux/rtc.h>
|
||||||
|
#include <linux/if_xdp.h>
|
||||||
#include <mtd/ubi-user.h>
|
#include <mtd/ubi-user.h>
|
||||||
#include <net/route.h>
|
#include <net/route.h>
|
||||||
#include <asm/termbits.h>
|
#include <asm/termbits.h>
|
||||||
|
@ -244,6 +251,16 @@ struct ltchars {
|
||||||
#define FS_KEY_DESC_PREFIX "fscrypt:"
|
#define FS_KEY_DESC_PREFIX "fscrypt:"
|
||||||
#define FS_KEY_DESC_PREFIX_SIZE 8
|
#define FS_KEY_DESC_PREFIX_SIZE 8
|
||||||
#define FS_MAX_KEY_SIZE 64
|
#define FS_MAX_KEY_SIZE 64
|
||||||
|
|
||||||
|
// XDP socket constants do not appear to be picked up otherwise.
|
||||||
|
// Copied from samples/bpf/xdpsock_user.c.
|
||||||
|
#ifndef SOL_XDP
|
||||||
|
#define SOL_XDP 283
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef AF_XDP
|
||||||
|
#define AF_XDP 44
|
||||||
|
#endif
|
||||||
'
|
'
|
||||||
|
|
||||||
includes_NetBSD='
|
includes_NetBSD='
|
||||||
|
@ -252,6 +269,7 @@ includes_NetBSD='
|
||||||
#include <sys/event.h>
|
#include <sys/event.h>
|
||||||
#include <sys/extattr.h>
|
#include <sys/extattr.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/sockio.h>
|
#include <sys/sockio.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
|
@ -277,6 +295,7 @@ includes_OpenBSD='
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/event.h>
|
#include <sys/event.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/sockio.h>
|
#include <sys/sockio.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
@ -378,6 +397,7 @@ ccflags="$@"
|
||||||
$2 ~ /^EXTATTR_NAMESPACE_NAMES/ ||
|
$2 ~ /^EXTATTR_NAMESPACE_NAMES/ ||
|
||||||
$2 ~ /^EXTATTR_NAMESPACE_[A-Z]+_STRING/ {next}
|
$2 ~ /^EXTATTR_NAMESPACE_[A-Z]+_STRING/ {next}
|
||||||
|
|
||||||
|
$2 !~ /^ECCAPBITS/ &&
|
||||||
$2 !~ /^ETH_/ &&
|
$2 !~ /^ETH_/ &&
|
||||||
$2 !~ /^EPROC_/ &&
|
$2 !~ /^EPROC_/ &&
|
||||||
$2 !~ /^EQUIV_/ &&
|
$2 !~ /^EQUIV_/ &&
|
||||||
|
@ -413,7 +433,7 @@ ccflags="$@"
|
||||||
$2 ~ /^TC[IO](ON|OFF)$/ ||
|
$2 ~ /^TC[IO](ON|OFF)$/ ||
|
||||||
$2 ~ /^IN_/ ||
|
$2 ~ /^IN_/ ||
|
||||||
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
|
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
|
||||||
$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|ICMP6|TCP|EVFILT|NOTE|EV|SHUT|PROT|MAP|T?PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ ||
|
$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|ICMP6|TCP|EVFILT|NOTE|EV|SHUT|PROT|MAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ ||
|
||||||
$2 ~ /^TP_STATUS_/ ||
|
$2 ~ /^TP_STATUS_/ ||
|
||||||
$2 ~ /^FALLOC_/ ||
|
$2 ~ /^FALLOC_/ ||
|
||||||
$2 == "ICMPV6_FILTER" ||
|
$2 == "ICMPV6_FILTER" ||
|
||||||
|
@ -424,11 +444,14 @@ ccflags="$@"
|
||||||
$2 ~ /^KERN_(HOSTNAME|OS(RELEASE|TYPE)|VERSION)$/ ||
|
$2 ~ /^KERN_(HOSTNAME|OS(RELEASE|TYPE)|VERSION)$/ ||
|
||||||
$2 ~ /^HW_MACHINE$/ ||
|
$2 ~ /^HW_MACHINE$/ ||
|
||||||
$2 ~ /^SYSCTL_VERS/ ||
|
$2 ~ /^SYSCTL_VERS/ ||
|
||||||
|
$2 !~ "MNT_BITS" &&
|
||||||
$2 ~ /^(MS|MNT|UMOUNT)_/ ||
|
$2 ~ /^(MS|MNT|UMOUNT)_/ ||
|
||||||
$2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ ||
|
$2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ ||
|
||||||
$2 ~ /^(O|F|E?FD|NAME|S|PTRACE|PT)_/ ||
|
$2 ~ /^(O|F|E?FD|NAME|S|PTRACE|PT)_/ ||
|
||||||
|
$2 ~ /^KEXEC_/ ||
|
||||||
$2 ~ /^LINUX_REBOOT_CMD_/ ||
|
$2 ~ /^LINUX_REBOOT_CMD_/ ||
|
||||||
$2 ~ /^LINUX_REBOOT_MAGIC[12]$/ ||
|
$2 ~ /^LINUX_REBOOT_MAGIC[12]$/ ||
|
||||||
|
$2 ~ /^MODULE_INIT_/ ||
|
||||||
$2 !~ "NLA_TYPE_MASK" &&
|
$2 !~ "NLA_TYPE_MASK" &&
|
||||||
$2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTC|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P|NETNSA)_/ ||
|
$2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTC|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P|NETNSA)_/ ||
|
||||||
$2 ~ /^SIOC/ ||
|
$2 ~ /^SIOC/ ||
|
||||||
|
@ -474,9 +497,11 @@ ccflags="$@"
|
||||||
$2 ~ /^FSOPT_/ ||
|
$2 ~ /^FSOPT_/ ||
|
||||||
$2 ~ /^WDIOC_/ ||
|
$2 ~ /^WDIOC_/ ||
|
||||||
$2 ~ /^NFN/ ||
|
$2 ~ /^NFN/ ||
|
||||||
|
$2 ~ /^XDP_/ ||
|
||||||
$2 ~ /^(HDIO|WIN|SMART)_/ ||
|
$2 ~ /^(HDIO|WIN|SMART)_/ ||
|
||||||
$2 !~ "WMESGLEN" &&
|
$2 !~ "WMESGLEN" &&
|
||||||
$2 ~ /^W[A-Z0-9]+$/ ||
|
$2 ~ /^W[A-Z0-9]+$/ ||
|
||||||
|
$2 ~/^PPPIOC/ ||
|
||||||
$2 ~ /^BLK[A-Z]*(GET$|SET$|BUF$|PART$|SIZE)/ {printf("\t%s = C.%s\n", $2, $2)}
|
$2 ~ /^BLK[A-Z]*(GET$|SET$|BUF$|PART$|SIZE)/ {printf("\t%s = C.%s\n", $2, $2)}
|
||||||
$2 ~ /^__WCOREFLAG$/ {next}
|
$2 ~ /^__WCOREFLAG$/ {next}
|
||||||
$2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)}
|
$2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)}
|
||||||
|
|
8
vendor/golang.org/x/sys/unix/mkpost.go
generated
vendored
8
vendor/golang.org/x/sys/unix/mkpost.go
generated
vendored
|
@ -46,6 +46,10 @@ func main() {
|
||||||
valRegex := regexp.MustCompile(`type (Fsid|Sigset_t) struct {(\s+)X__val(\s+\S+\s+)}`)
|
valRegex := regexp.MustCompile(`type (Fsid|Sigset_t) struct {(\s+)X__val(\s+\S+\s+)}`)
|
||||||
b = valRegex.ReplaceAll(b, []byte("type $1 struct {${2}Val$3}"))
|
b = valRegex.ReplaceAll(b, []byte("type $1 struct {${2}Val$3}"))
|
||||||
|
|
||||||
|
// Intentionally export __fds_bits field in FdSet
|
||||||
|
fdSetRegex := regexp.MustCompile(`type (FdSet) struct {(\s+)X__fds_bits(\s+\S+\s+)}`)
|
||||||
|
b = fdSetRegex.ReplaceAll(b, []byte("type $1 struct {${2}Bits$3}"))
|
||||||
|
|
||||||
// If we have empty Ptrace structs, we should delete them. Only s390x emits
|
// If we have empty Ptrace structs, we should delete them. Only s390x emits
|
||||||
// nonempty Ptrace structs.
|
// nonempty Ptrace structs.
|
||||||
ptraceRexexp := regexp.MustCompile(`type Ptrace((Psw|Fpregs|Per) struct {\s*})`)
|
ptraceRexexp := regexp.MustCompile(`type Ptrace((Psw|Fpregs|Per) struct {\s*})`)
|
||||||
|
@ -65,6 +69,10 @@ func main() {
|
||||||
convertUtsnameRegex := regexp.MustCompile(`((Sys|Node|Domain)name|Release|Version|Machine)(\s+)\[(\d+)\]u?int8`)
|
convertUtsnameRegex := regexp.MustCompile(`((Sys|Node|Domain)name|Release|Version|Machine)(\s+)\[(\d+)\]u?int8`)
|
||||||
b = convertUtsnameRegex.ReplaceAll(b, []byte("$1$3[$4]byte"))
|
b = convertUtsnameRegex.ReplaceAll(b, []byte("$1$3[$4]byte"))
|
||||||
|
|
||||||
|
// Convert [1024]int8 to [1024]byte in Ptmget members
|
||||||
|
convertPtmget := regexp.MustCompile(`([SC]n)(\s+)\[(\d+)\]u?int8`)
|
||||||
|
b = convertPtmget.ReplaceAll(b, []byte("$1[$3]byte"))
|
||||||
|
|
||||||
// Remove spare fields (e.g. in Statx_t)
|
// Remove spare fields (e.g. in Statx_t)
|
||||||
spareFieldsRegex := regexp.MustCompile(`X__spare\S*`)
|
spareFieldsRegex := regexp.MustCompile(`X__spare\S*`)
|
||||||
b = spareFieldsRegex.ReplaceAll(b, []byte("_"))
|
b = spareFieldsRegex.ReplaceAll(b, []byte("_"))
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue