features/jwt #7
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": {
|
||||
@ -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": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -1059,6 +1307,23 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.UpdateScopesRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"scopes"
|
||||
],
|
||||
"properties": {
|
||||
"scopes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
@ -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": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -1050,6 +1298,23 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"domain.UpdateScopesRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"scopes"
|
||||
],
|
||||
"properties": {
|
||||
"scopes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
|
@ -78,6 +78,24 @@ definitions:
|
||||
$ref: '#/definitions/domain.DiscordWebHookDto'
|
||||
type: array
|
||||
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:
|
||||
properties:
|
||||
enabled:
|
||||
@ -102,6 +120,17 @@ definitions:
|
||||
$ref: '#/definitions/domain.SourceDto'
|
||||
type: array
|
||||
type: object
|
||||
domain.UpdateScopesRequest:
|
||||
properties:
|
||||
scopes:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
username:
|
||||
type: string
|
||||
required:
|
||||
- scopes
|
||||
type: object
|
||||
info:
|
||||
contact: {}
|
||||
title: NewsBot collector
|
||||
@ -670,6 +699,145 @@ paths:
|
||||
summary: Creates a new youtube source to monitor.
|
||||
tags:
|
||||
- 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:
|
||||
Bearer:
|
||||
description: Type "Bearer" followed by a space and JWT token.
|
||||
|
@ -2,5 +2,7 @@ package domain
|
||||
|
||||
const (
|
||||
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"
|
||||
)
|
||||
|
||||
// Register
|
||||
// @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
|
||||
// @Success 200 {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)
|
||||
}
|
||||
|
||||
_, 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 {
|
||||
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 {
|
||||
username := c.FormValue("username")
|
||||
password := c.FormValue("password")
|
||||
|
||||
// Check to see if they are trying to login with the admin token
|
||||
if username == "" {
|
||||
return h.validateAdminToken(c, password)
|
||||
return h.createAdminToken(c, password)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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?
|
||||
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 {
|
||||
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.
|
||||
// this will fail right away and not progress.
|
||||
if h.config.AdminSecret == "" {
|
||||
@ -109,15 +121,30 @@ func (h *Handler) validateAdminToken(c echo.Context, password string) error {
|
||||
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 {
|
||||
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.
|
||||
// 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 {
|
||||
// Check the context for the refresh token
|
||||
var request domain.RefreshTokenRequest
|
||||
@ -131,7 +158,12 @@ func (h *Handler) RefreshJwtToken(c echo.Context) 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 {
|
||||
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 {
|
||||
token, err := h.getJwtToken(c)
|
||||
token, err := h.getJwtTokenFromContext(c)
|
||||
if err != nil {
|
||||
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 {
|
||||
token, err := h.getJwtToken(c)
|
||||
token, err := h.getJwtTokenFromContext(c)
|
||||
if err != nil {
|
||||
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/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
|
||||
return s
|
||||
}
|
||||
@ -136,3 +144,29 @@ func (s *Handler) UnauthorizedResponse(c echo.Context, msg string) error {
|
||||
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"
|
||||
ErrJwtExpired = "auth token has expired"
|
||||
ErrJwtScopeMissing = "required scope is missing"
|
||||
ErrJwtInvalidIssuer = "incorrect server issued the token"
|
||||
)
|
||||
|
||||
type JwtToken struct {
|
||||
@ -32,6 +33,13 @@ func (j JwtToken) IsValid(scope string) error {
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -53,25 +61,27 @@ func (j JwtToken) hasExpired() error {
|
||||
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 {
|
||||
// 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
|
||||
}
|
||||
|
||||
for _, s := range j.Scopes {
|
||||
if strings.Contains(s, scope) {
|
||||
if strings.Contains(userScopes, scope) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New(ErrJwtScopeMissing)
|
||||
}
|
||||
|
||||
func (h *Handler) generateJwt(username, issuer string) (string, error) {
|
||||
return h.generateJwtWithExp(username, issuer, time.Now().Add(10*time.Minute))
|
||||
func (h *Handler) generateJwt(username, scopes, issuer string) (string, error) {
|
||||
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)
|
||||
|
||||
// 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
|
||||
|
||||
var scopes []string
|
||||
if username == "admin" {
|
||||
scopes = append(scopes, domain.ScopeAll)
|
||||
claims["scopes"] = scopes
|
||||
} else {
|
||||
scopes = append(scopes, domain.ScopeRead)
|
||||
claims["scopes"] = scopes
|
||||
}
|
||||
|
||||
tokenString, err := token.SignedString(secret)
|
||||
if err != nil {
|
||||
@ -99,7 +104,7 @@ func (h *Handler) generateJwtWithExp(username, issuer string, expiresAt time.Tim
|
||||
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
|
||||
token, ok := c.Get("user").(*jwt.Token)
|
||||
if !ok {
|
||||
|
@ -67,6 +67,7 @@ func GetEnvConfig() Configs {
|
||||
return Configs{
|
||||
ServerAddress: os.Getenv(ServerAddress),
|
||||
JwtSecret: os.Getenv("JwtSecret"),
|
||||
AdminSecret: os.Getenv("AdminSecret"),
|
||||
|
||||
RedditEnabled: processBoolConfig(os.Getenv(FEATURE_ENABLE_REDDIT_BACKEND)),
|
||||
RedditPullTop: processBoolConfig(os.Getenv(REDDIT_PULL_TOP)),
|
||||
|
Loading…
Reference in New Issue
Block a user