got the user routes exposed with swagger, added jwt support to swagger and also updated how the scopes are validated
This commit is contained in:
parent
c765227932
commit
c539a20cc7
265
docs/docs.go
265
docs/docs.go
@ -903,6 +903,226 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/v1/users/login": {
|
||||||
|
"post": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Users"
|
||||||
|
],
|
||||||
|
"summary": "Logs into the API and returns a bearer token if successful",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "password",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "username",
|
||||||
|
"in": "formData"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.LoginResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/v1/users/refreshToken": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Users"
|
||||||
|
],
|
||||||
|
"summary": "Generates a new token",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "body",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.RefreshTokenRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.LoginResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/v1/users/register": {
|
||||||
|
"post": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Users"
|
||||||
|
],
|
||||||
|
"summary": "Creates a new user",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "password",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "username",
|
||||||
|
"in": "formData"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/v1/users/scopes/add": {
|
||||||
|
"post": {
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Users"
|
||||||
|
],
|
||||||
|
"summary": "Adds a new scope to a user account",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "body",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.UpdateScopesRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/v1/users/scopes/remove": {
|
||||||
|
"post": {
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Users"
|
||||||
|
],
|
||||||
|
"summary": "Adds a new scope to a user account",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "body",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.UpdateScopesRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
@ -1023,6 +1243,34 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"domain.LoginResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"message": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"refreshToken": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"token": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.RefreshTokenRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"refreshToken": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"domain.SourceDto": {
|
"domain.SourceDto": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -1059,6 +1307,23 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"domain.UpdateScopesRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"scopes"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"scopes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"securityDefinitions": {
|
"securityDefinitions": {
|
||||||
|
@ -894,6 +894,226 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/v1/users/login": {
|
||||||
|
"post": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Users"
|
||||||
|
],
|
||||||
|
"summary": "Logs into the API and returns a bearer token if successful",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "password",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "username",
|
||||||
|
"in": "formData"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.LoginResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/v1/users/refreshToken": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Users"
|
||||||
|
],
|
||||||
|
"summary": "Generates a new token",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "body",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.RefreshTokenRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.LoginResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/v1/users/register": {
|
||||||
|
"post": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Users"
|
||||||
|
],
|
||||||
|
"summary": "Creates a new user",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "password",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "username",
|
||||||
|
"in": "formData"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/v1/users/scopes/add": {
|
||||||
|
"post": {
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Users"
|
||||||
|
],
|
||||||
|
"summary": "Adds a new scope to a user account",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "body",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.UpdateScopesRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/v1/users/scopes/remove": {
|
||||||
|
"post": {
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Users"
|
||||||
|
],
|
||||||
|
"summary": "Adds a new scope to a user account",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "body",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.UpdateScopesRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.BaseResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
@ -1014,6 +1234,34 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"domain.LoginResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"message": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"refreshToken": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"token": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain.RefreshTokenRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"refreshToken": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"domain.SourceDto": {
|
"domain.SourceDto": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -1050,6 +1298,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"domain.UpdateScopesRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"scopes"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"scopes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"securityDefinitions": {
|
"securityDefinitions": {
|
||||||
|
@ -78,6 +78,24 @@ definitions:
|
|||||||
$ref: '#/definitions/domain.DiscordWebHookDto'
|
$ref: '#/definitions/domain.DiscordWebHookDto'
|
||||||
type: array
|
type: array
|
||||||
type: object
|
type: object
|
||||||
|
domain.LoginResponse:
|
||||||
|
properties:
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
refreshToken:
|
||||||
|
type: string
|
||||||
|
token:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
domain.RefreshTokenRequest:
|
||||||
|
properties:
|
||||||
|
refreshToken:
|
||||||
|
type: string
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
domain.SourceDto:
|
domain.SourceDto:
|
||||||
properties:
|
properties:
|
||||||
enabled:
|
enabled:
|
||||||
@ -102,6 +120,17 @@ definitions:
|
|||||||
$ref: '#/definitions/domain.SourceDto'
|
$ref: '#/definitions/domain.SourceDto'
|
||||||
type: array
|
type: array
|
||||||
type: object
|
type: object
|
||||||
|
domain.UpdateScopesRequest:
|
||||||
|
properties:
|
||||||
|
scopes:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- scopes
|
||||||
|
type: object
|
||||||
info:
|
info:
|
||||||
contact: {}
|
contact: {}
|
||||||
title: NewsBot collector
|
title: NewsBot collector
|
||||||
@ -670,6 +699,145 @@ paths:
|
|||||||
summary: Creates a new youtube source to monitor.
|
summary: Creates a new youtube source to monitor.
|
||||||
tags:
|
tags:
|
||||||
- Source
|
- Source
|
||||||
|
/v1/users/login:
|
||||||
|
post:
|
||||||
|
parameters:
|
||||||
|
- in: formData
|
||||||
|
name: password
|
||||||
|
type: string
|
||||||
|
- in: formData
|
||||||
|
name: username
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.LoginResponse'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.BaseResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.BaseResponse'
|
||||||
|
summary: Logs into the API and returns a bearer token if successful
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
|
/v1/users/refreshToken:
|
||||||
|
post:
|
||||||
|
parameters:
|
||||||
|
- description: body
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.RefreshTokenRequest'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.LoginResponse'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.BaseResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.BaseResponse'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Generates a new token
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
|
/v1/users/register:
|
||||||
|
post:
|
||||||
|
parameters:
|
||||||
|
- in: formData
|
||||||
|
name: password
|
||||||
|
type: string
|
||||||
|
- in: formData
|
||||||
|
name: username
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.BaseResponse'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.BaseResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.BaseResponse'
|
||||||
|
summary: Creates a new user
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
|
/v1/users/scopes/add:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- description: body
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.UpdateScopesRequest'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.BaseResponse'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.BaseResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.BaseResponse'
|
||||||
|
summary: Adds a new scope to a user account
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
|
/v1/users/scopes/remove:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- description: body
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.UpdateScopesRequest'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.BaseResponse'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.BaseResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.BaseResponse'
|
||||||
|
summary: Adds a new scope to a user account
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
securityDefinitions:
|
securityDefinitions:
|
||||||
Bearer:
|
Bearer:
|
||||||
description: Type "Bearer" followed by a space and JWT token.
|
description: Type "Bearer" followed by a space and JWT token.
|
||||||
|
@ -2,5 +2,7 @@ package domain
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
ScopeAll = "newsbot:all"
|
ScopeAll = "newsbot:all"
|
||||||
ScopeRead = "newsbot:read"
|
ScopeArticleRead = "newsbot:article:read"
|
||||||
|
ScopeSourceCreate = "newsbot:source:create"
|
||||||
|
ScopeDiscordWebHookCreate = "newsbot:discordwebhook:create"
|
||||||
)
|
)
|
||||||
|
@ -15,8 +15,11 @@ const (
|
|||||||
ErrUsernameAlreadyExists = "the requested username already exists"
|
ErrUsernameAlreadyExists = "the requested username already exists"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Register
|
|
||||||
// @Summary Creates a new user
|
// @Summary Creates a new user
|
||||||
|
// @Router /v1/users/register [post]
|
||||||
|
// @Param request formData domain.LoginFormRequest true "form"
|
||||||
|
// @Accepts x-www-form-urlencoded
|
||||||
|
// @Produce json
|
||||||
// @Tags Users
|
// @Tags Users
|
||||||
// @Success 200 {object} domain.BaseResponse
|
// @Success 200 {object} domain.BaseResponse
|
||||||
// @Failure 400 {object} domain.BaseResponse
|
// @Failure 400 {object} domain.BaseResponse
|
||||||
@ -44,7 +47,7 @@ func (h *Handler) AuthRegister(c echo.Context) error {
|
|||||||
return h.WriteError(c, err, http.StatusInternalServerError)
|
return h.WriteError(c, err, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = h.repo.Users.Create(c.Request().Context(), username, password, domain.ScopeRead)
|
_, err = h.repo.Users.Create(c.Request().Context(), username, password, domain.ScopeArticleRead)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return h.InternalServerErrorResponse(c, err.Error())
|
return h.InternalServerErrorResponse(c, err.Error())
|
||||||
}
|
}
|
||||||
@ -54,17 +57,26 @@ func (h *Handler) AuthRegister(c echo.Context) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Summary Logs into the API and returns a bearer token if successful
|
||||||
|
// @Router /v1/users/login [post]
|
||||||
|
// @Param request formData domain.LoginFormRequest true "form"
|
||||||
|
// @Accepts x-www-form-urlencoded
|
||||||
|
// @Produce json
|
||||||
|
// @Tags Users
|
||||||
|
// @Success 200 {object} domain.LoginResponse
|
||||||
|
// @Failure 400 {object} domain.BaseResponse
|
||||||
|
// @Failure 500 {object} domain.BaseResponse
|
||||||
func (h *Handler) AuthLogin(c echo.Context) error {
|
func (h *Handler) AuthLogin(c echo.Context) error {
|
||||||
username := c.FormValue("username")
|
username := c.FormValue("username")
|
||||||
password := c.FormValue("password")
|
password := c.FormValue("password")
|
||||||
|
|
||||||
// Check to see if they are trying to login with the admin token
|
// Check to see if they are trying to login with the admin token
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return h.validateAdminToken(c, password)
|
return h.createAdminToken(c, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the user exists
|
// check if the user exists
|
||||||
err := h.repo.Users.DoesUserExist(c.Request().Context(), username)
|
user, err := h.repo.Users.GetUser(c.Request().Context(), username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return h.InternalServerErrorResponse(c, err.Error())
|
return h.InternalServerErrorResponse(c, err.Error())
|
||||||
}
|
}
|
||||||
@ -78,7 +90,7 @@ func (h *Handler) AuthLogin(c echo.Context) error {
|
|||||||
// TODO think about moving this down some?
|
// TODO think about moving this down some?
|
||||||
expiresAt := time.Now().Add(time.Hour * 48)
|
expiresAt := time.Now().Add(time.Hour * 48)
|
||||||
|
|
||||||
jwt, err := h.generateJwtWithExp(username, h.config.ServerAddress, expiresAt)
|
jwt, err := h.generateJwtWithExp(username, user.Scopes, h.config.ServerAddress, expiresAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return h.InternalServerErrorResponse(c, err.Error())
|
return h.InternalServerErrorResponse(c, err.Error())
|
||||||
}
|
}
|
||||||
@ -98,7 +110,7 @@ func (h *Handler) AuthLogin(c echo.Context) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) validateAdminToken(c echo.Context, password string) error {
|
func (h *Handler) createAdminToken(c echo.Context, password string) error {
|
||||||
// if the admin token is blank, then the admin wanted this disabled.
|
// if the admin token is blank, then the admin wanted this disabled.
|
||||||
// this will fail right away and not progress.
|
// this will fail right away and not progress.
|
||||||
if h.config.AdminSecret == "" {
|
if h.config.AdminSecret == "" {
|
||||||
@ -109,15 +121,30 @@ func (h *Handler) validateAdminToken(c echo.Context, password string) error {
|
|||||||
return h.UnauthorizedResponse(c, ErrUserNotFound)
|
return h.UnauthorizedResponse(c, ErrUserNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := h.generateJwt("admin", h.config.ServerAddress)
|
token, err := h.generateJwt("admin", domain.ScopeAll, h.config.ServerAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return h.InternalServerErrorResponse(c, err.Error())
|
return h.InternalServerErrorResponse(c, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, token)
|
return c.JSON(http.StatusOK, domain.LoginResponse{
|
||||||
|
BaseResponse: domain.BaseResponse{
|
||||||
|
Message: "OK",
|
||||||
|
},
|
||||||
|
Token: token,
|
||||||
|
Type: "Bearer",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will take collect some information about the requested refresh, validate and then return a new jwt token if approved.
|
// This will take collect some information about the requested refresh, validate and then return a new jwt token if approved.
|
||||||
|
// Register
|
||||||
|
// @Summary Generates a new token
|
||||||
|
// @Router /v1/users/refreshToken [post]
|
||||||
|
// @Param request body domain.RefreshTokenRequest true "body"
|
||||||
|
// @Tags Users
|
||||||
|
// @Success 200 {object} domain.LoginResponse
|
||||||
|
// @Failure 400 {object} domain.BaseResponse
|
||||||
|
// @Failure 500 {object} domain.BaseResponse
|
||||||
|
// @Security Bearer
|
||||||
func (h *Handler) RefreshJwtToken(c echo.Context) error {
|
func (h *Handler) RefreshJwtToken(c echo.Context) error {
|
||||||
// Check the context for the refresh token
|
// Check the context for the refresh token
|
||||||
var request domain.RefreshTokenRequest
|
var request domain.RefreshTokenRequest
|
||||||
@ -131,7 +158,12 @@ func (h *Handler) RefreshJwtToken(c echo.Context) error {
|
|||||||
return h.InternalServerErrorResponse(c, err.Error())
|
return h.InternalServerErrorResponse(c, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
jwt, err := h.generateJwtWithExp(request.Username, h.config.ServerAddress, time.Now().Add(time.Hour*48))
|
user, err := h.repo.Users.GetUser(c.Request().Context(), request.Username)
|
||||||
|
if err != nil {
|
||||||
|
return h.InternalServerErrorResponse(c, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
jwt, err := h.generateJwtWithExp(request.Username, user.Scopes, h.config.ServerAddress, time.Now().Add(time.Hour*48))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return h.InternalServerErrorResponse(c, err.Error())
|
return h.InternalServerErrorResponse(c, err.Error())
|
||||||
}
|
}
|
||||||
@ -151,8 +183,17 @@ func (h *Handler) RefreshJwtToken(c echo.Context) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Summary Adds a new scope to a user account
|
||||||
|
// @Router /v1/users/scopes/add [post]
|
||||||
|
// @Param request body domain.UpdateScopesRequest true "body"
|
||||||
|
// @Tags Users
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} domain.BaseResponse
|
||||||
|
// @Failure 400 {object} domain.BaseResponse
|
||||||
|
// @Failure 500 {object} domain.BaseResponse
|
||||||
func (h *Handler) AddScopes(c echo.Context) error {
|
func (h *Handler) AddScopes(c echo.Context) error {
|
||||||
token, err := h.getJwtToken(c)
|
token, err := h.getJwtTokenFromContext(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return h.UnauthorizedResponse(c, err.Error())
|
return h.UnauthorizedResponse(c, err.Error())
|
||||||
}
|
}
|
||||||
@ -178,8 +219,17 @@ func (h *Handler) AddScopes(c echo.Context) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Summary Adds a new scope to a user account
|
||||||
|
// @Router /v1/users/scopes/remove [post]
|
||||||
|
// @Param request body domain.UpdateScopesRequest true "body"
|
||||||
|
// @Tags Users
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} domain.BaseResponse
|
||||||
|
// @Failure 400 {object} domain.BaseResponse
|
||||||
|
// @Failure 500 {object} domain.BaseResponse
|
||||||
func (h *Handler) RemoveScopes(c echo.Context) error {
|
func (h *Handler) RemoveScopes(c echo.Context) error {
|
||||||
token, err := h.getJwtToken(c)
|
token, err := h.getJwtTokenFromContext(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return h.WriteError(c, err, http.StatusUnauthorized)
|
return h.WriteError(c, err, http.StatusUnauthorized)
|
||||||
}
|
}
|
||||||
|
@ -100,6 +100,14 @@ func NewServer(ctx context.Context, configs services.Configs, conn *sql.DB) *Han
|
|||||||
sources.POST("/:ID/disable", s.disableSource)
|
sources.POST("/:ID/disable", s.disableSource)
|
||||||
sources.POST("/:ID/enable", s.enableSource)
|
sources.POST("/:ID/enable", s.enableSource)
|
||||||
|
|
||||||
|
users := v1.Group("/users")
|
||||||
|
users.POST("/login", s.AuthLogin)
|
||||||
|
users.POST("/register", s.AuthRegister)
|
||||||
|
users.Use(echojwt.WithConfig(jwtConfig))
|
||||||
|
users.POST("/scopes/add", s.AddScopes)
|
||||||
|
users.POST("/scopes/remove", s.RemoveScopes)
|
||||||
|
users.POST("/refreshToken", s.RefreshJwtToken)
|
||||||
|
|
||||||
s.Router = router
|
s.Router = router
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
@ -136,3 +144,29 @@ func (s *Handler) UnauthorizedResponse(c echo.Context, msg string) error {
|
|||||||
Message: msg,
|
Message: msg,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the token is not valid then an json error will be returned.
|
||||||
|
// If the token has the wrong scope, a json error will be returned.
|
||||||
|
// If the token passes all the checks, it is valid and is returned back to the caller.
|
||||||
|
func (s *Handler) ValidateJwtToken(c echo.Context, requiredScope string) JwtToken {
|
||||||
|
token, err := s.getJwtTokenFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
s.WriteMessage(c, ErrJwtMissing, http.StatusUnauthorized)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = token.hasScope(requiredScope)
|
||||||
|
if err != nil {
|
||||||
|
s.WriteMessage(c, ErrJwtScopeMissing, http.StatusUnauthorized)
|
||||||
|
}
|
||||||
|
|
||||||
|
if token.Iss != s.config.ServerAddress {
|
||||||
|
s.WriteMessage(c, ErrJwtInvalidIssuer, http.StatusUnauthorized)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = token.hasExpired()
|
||||||
|
if err != nil {
|
||||||
|
s.WriteMessage(c, ErrJwtExpired, http.StatusUnauthorized)
|
||||||
|
}
|
||||||
|
|
||||||
|
return token
|
||||||
|
}
|
@ -15,6 +15,7 @@ const (
|
|||||||
ErrJwtClaimsMissing = "claims missing on token"
|
ErrJwtClaimsMissing = "claims missing on token"
|
||||||
ErrJwtExpired = "auth token has expired"
|
ErrJwtExpired = "auth token has expired"
|
||||||
ErrJwtScopeMissing = "required scope is missing"
|
ErrJwtScopeMissing = "required scope is missing"
|
||||||
|
ErrJwtInvalidIssuer = "incorrect server issued the token"
|
||||||
)
|
)
|
||||||
|
|
||||||
type JwtToken struct {
|
type JwtToken struct {
|
||||||
@ -32,6 +33,13 @@ func (j JwtToken) IsValid(scope string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check to see if they have the scope to do anything
|
||||||
|
// if they do, let them pass
|
||||||
|
err = j.hasScope(domain.ScopeAll)
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
err = j.hasScope(scope)
|
err = j.hasScope(scope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -53,25 +61,27 @@ func (j JwtToken) hasExpired() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This will check the users token to make sure they have the correct scope to access the handler.
|
||||||
|
// It will evaluate if you have the admin scope or the required scope for the handler.
|
||||||
func (j JwtToken) hasScope(scope string) error {
|
func (j JwtToken) hasScope(scope string) error {
|
||||||
// they have the scope to access everything, so let them pass.
|
// they have the scope to access everything, so let them pass.
|
||||||
if strings.Contains(domain.ScopeAll, scope) {
|
userScopes := strings.Join(j.Scopes, "")
|
||||||
|
if strings.Contains(domain.ScopeAll, userScopes) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range j.Scopes {
|
if strings.Contains(userScopes, scope) {
|
||||||
if strings.Contains(s, scope) {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return errors.New(ErrJwtScopeMissing)
|
return errors.New(ErrJwtScopeMissing)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) generateJwt(username, issuer string) (string, error) {
|
func (h *Handler) generateJwt(username, scopes, issuer string) (string, error) {
|
||||||
return h.generateJwtWithExp(username, issuer, time.Now().Add(10*time.Minute))
|
return h.generateJwtWithExp(username, scopes, issuer, time.Now().Add(10*time.Minute))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) generateJwtWithExp(username, issuer string, expiresAt time.Time) (string, error) {
|
func (h *Handler) generateJwtWithExp(username, userScopes, issuer string, expiresAt time.Time) (string, error) {
|
||||||
secret := []byte(h.config.JwtSecret)
|
secret := []byte(h.config.JwtSecret)
|
||||||
|
|
||||||
// Anyone who wants to decrypt the key needs to use the same method
|
// Anyone who wants to decrypt the key needs to use the same method
|
||||||
@ -83,13 +93,8 @@ func (h *Handler) generateJwtWithExp(username, issuer string, expiresAt time.Tim
|
|||||||
claims["iss"] = issuer
|
claims["iss"] = issuer
|
||||||
|
|
||||||
var scopes []string
|
var scopes []string
|
||||||
if username == "admin" {
|
|
||||||
scopes = append(scopes, domain.ScopeAll)
|
scopes = append(scopes, domain.ScopeAll)
|
||||||
claims["scopes"] = scopes
|
claims["scopes"] = scopes
|
||||||
} else {
|
|
||||||
scopes = append(scopes, domain.ScopeRead)
|
|
||||||
claims["scopes"] = scopes
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenString, err := token.SignedString(secret)
|
tokenString, err := token.SignedString(secret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -99,7 +104,7 @@ func (h *Handler) generateJwtWithExp(username, issuer string, expiresAt time.Tim
|
|||||||
return tokenString, nil
|
return tokenString, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) getJwtToken(c echo.Context) (JwtToken, error) {
|
func (h *Handler) getJwtTokenFromContext(c echo.Context) (JwtToken, error) {
|
||||||
// Make sure that the request came with a jwtToken
|
// Make sure that the request came with a jwtToken
|
||||||
token, ok := c.Get("user").(*jwt.Token)
|
token, ok := c.Get("user").(*jwt.Token)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -67,6 +67,7 @@ func GetEnvConfig() Configs {
|
|||||||
return Configs{
|
return Configs{
|
||||||
ServerAddress: os.Getenv(ServerAddress),
|
ServerAddress: os.Getenv(ServerAddress),
|
||||||
JwtSecret: os.Getenv("JwtSecret"),
|
JwtSecret: os.Getenv("JwtSecret"),
|
||||||
|
AdminSecret: os.Getenv("AdminSecret"),
|
||||||
|
|
||||||
RedditEnabled: processBoolConfig(os.Getenv(FEATURE_ENABLE_REDDIT_BACKEND)),
|
RedditEnabled: processBoolConfig(os.Getenv(FEATURE_ENABLE_REDDIT_BACKEND)),
|
||||||
RedditPullTop: processBoolConfig(os.Getenv(REDDIT_PULL_TOP)),
|
RedditPullTop: processBoolConfig(os.Getenv(REDDIT_PULL_TOP)),
|
||||||
|
Loading…
Reference in New Issue
Block a user