Commit 54428826 authored by Lukas Matt's avatar Lukas Matt

Restrict API route access with revel intercept methods

Defining "revel.InterceptMethod(ApiOAuth.checkUser, revel.BEFORE)"
will require authentication for all routes in ApiOAuth struct.

For single routes we can overwrite this behavior with
a custom ApiOAuth.checkUser function
parent bd8aa8c7
......@@ -88,13 +88,7 @@ func (a ApiAspect) ShowPerson() revel.Result {
)
a.Params.Bind(&personID, "id")
user, err := models.CurrentUser(a.Controller)
if err != nil {
a.Log.Error(TAG, "db", err, "api", ERR_UNAUTHORIZED)
return a.ApiError(http.StatusUnauthorized, ERR_UNAUTHORIZED)
}
err = aspects.FindByUserPersonID(user.ID, personID)
err := aspects.FindByUserPersonID(a.CurrentUser.ID, personID)
if err != nil {
a.Log.Error(TAG, "db", err, "api", ERR_NOT_FOUND)
return a.ApiError(http.StatusNotFound, ERR_NOT_FOUND)
......@@ -132,6 +126,12 @@ func (a ApiAspect) ShowPerson() revel.Result {
*
* @apiError (Errors) {String} error Contains the recent error message
*
* @apiErrorExample {json} Unauthorized
* HTTP/1.1 401 Unauthorized
* {
* "error": "[...]"
* }
*
* @apiErrorExample {json} ServerError
* HTTP/1.1 500 Internal Server Error
* {
......@@ -188,6 +188,12 @@ func (a ApiAspect) CreatePerson() revel.Result {
*
* @apiError (Errors) {String} error Contains the recent error message
*
* @apiErrorExample {json} Unauthorized
* HTTP/1.1 401 Unauthorized
* {
* "error": "[...]"
* }
*
* @apiErrorExample {json} ServerError
* HTTP/1.1 500 Internal Server Error
* {
......@@ -216,14 +222,9 @@ func (a ApiAspect) DeletePerson() revel.Result {
}
func (a ApiAspect) Index(fields string) revel.Result {
user, err := models.CurrentUser(a.Controller)
if err != nil {
a.Log.Error(TAG, "db", err, "api", ERR_UNAUTHORIZED)
return a.ApiError(http.StatusUnauthorized, ERR_UNAUTHORIZED)
}
return a.RenderJSON(
helpers.SelectStructFields(user.Aspects, fields))
helpers.SelectStructFields(
a.CurrentUser.Aspects, fields))
}
/**
......@@ -283,18 +284,12 @@ func (a ApiAspect) Create() revel.Result {
return a.ApiError(http.StatusBadRequest, ERR_EMPTY_STRING)
}
user, err := models.CurrentUser(a.Controller)
if err != nil {
a.Log.Error(TAG, "db", err, "api", ERR_UNAUTHORIZED)
return a.ApiError(http.StatusUnauthorized, ERR_UNAUTHORIZED)
}
aspect := models.Aspect{
Name: name,
UserID: user.ID,
UserID: a.CurrentUser.ID,
}
err = aspect.Create()
err := aspect.Create()
if err != nil {
a.Log.Error(TAG, "db", err, "api", ERR_SERVER)
return a.ApiError(http.StatusInternalServerError, ERR_SERVER)
......
......@@ -20,6 +20,7 @@ package controllers
import (
"net/http"
"github.com/revel/revel"
"gopkg.in/ganggo/ganggo.v0/app/models"
)
const (
......@@ -36,6 +37,8 @@ const (
type ApiHelper struct {
*revel.Controller
CurrentUser models.User
}
type ApiError struct {
......@@ -50,3 +53,13 @@ func (c ApiHelper) ApiError(status int, msg string) revel.Result {
c.Response.Status = status
return c.RenderJSON(ApiError{msg})
}
func (c ApiHelper) checkUser() revel.Result {
user, err := models.CurrentUser(c.Controller)
if err != nil {
c.Log.Error(TAG, "db", err, "api", ERR_UNAUTHORIZED)
return c.ApiError(http.StatusUnauthorized, ERR_UNAUTHORIZED)
}
c.CurrentUser = user
return nil
}
......@@ -75,6 +75,12 @@ type ApiComment struct {
*
* @apiError (Errors) {String} error Contains the recent error message
*
* @apiErrorExample {json} Unauthorized
* HTTP/1.1 401 Unauthorized
* {
* "error": "[...]"
* }
*
* @apiErrorExample {json} NotFound
* HTTP/1.1 404 Not Found
* {
......@@ -144,20 +150,14 @@ func (c ApiComment) Create() revel.Result {
return c.ApiError(http.StatusBadRequest, ERR_EMPTY_STRING)
}
user, err := models.CurrentUser(c.Controller)
if err != nil {
c.Log.Error(TAG, "db", err, "api", ERR_UNAUTHORIZED)
return c.ApiError(http.StatusUnauthorized, ERR_UNAUTHORIZED)
}
err = post.FindByID(postID)
err := post.FindByID(postID)
if err != nil {
c.Log.Error(TAG, "db", err, "api", ERR_NOT_FOUND)
return c.ApiError(http.StatusNotFound, ERR_NOT_FOUND)
}
dispatcher := jobs.Dispatcher{
User: user,
User: c.CurrentUser,
ParentPerson: &post.Person,
Message: federation.EntityComment{
ParentGuid: post.Guid,
......
package controllers
//
// GangGo API Library
// Copyright (C) 2017 Lukas Matt <lukas@zauberstuhl.de>
//
// 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
// 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 <http://www.gnu.org/licenses/>.
//
import "github.com/revel/revel"
func init() {
revel.InterceptMethod(ApiComment.checkUser, revel.BEFORE)
revel.InterceptMethod(ApiLike.checkUser, revel.BEFORE)
revel.InterceptMethod(ApiPost.checkUser, revel.BEFORE)
revel.InterceptMethod(ApiPeople.checkUser, revel.BEFORE)
revel.InterceptMethod(ApiProfile.checkUser, revel.BEFORE)
revel.InterceptMethod(ApiAspect.checkUser, revel.BEFORE)
revel.InterceptMethod(ApiNotification.checkUser, revel.BEFORE)
revel.InterceptMethod(ApiOAuth.checkUser, revel.BEFORE)
}
......@@ -74,6 +74,12 @@ type ApiLike struct {
*
* @apiError (Errors) {String} error Contains the recent error message
*
* @apiErrorExample {json} Unauthorized
* HTTP/1.1 401 Unauthorized
* {
* "error": "[...]"
* }
*
* @apiErrorExample {json} NotFound
* HTTP/1.1 404 Not Found
* {
......@@ -169,12 +175,6 @@ func (l ApiLike) Create() revel.Result {
return l.ApiError(http.StatusNotFound, ERR_NOT_FOUND)
}
user, err := models.CurrentUser(l.Controller)
if err != nil {
l.Log.Error(TAG, "db", err, "api", ERR_UNAUTHORIZED)
return l.ApiError(http.StatusUnauthorized, ERR_UNAUTHORIZED)
}
guid, err := helpers.Uuid()
if err != nil {
l.Log.Error(TAG, "db", err, "api", ERR_SERVER)
......@@ -184,7 +184,7 @@ func (l ApiLike) Create() revel.Result {
entityLike := federation.EntityLike{
Positive: positive,
Guid: guid,
Author: user.Person.Author,
Author: l.CurrentUser.Person.Author,
TargetType: models.ShareablePost,
ParentGuid: post.Guid,
}
......@@ -196,7 +196,7 @@ func (l ApiLike) Create() revel.Result {
}
dispatcher := jobs.Dispatcher{
User: user,
User: l.CurrentUser,
Message: entityLike,
}
......
......@@ -84,14 +84,8 @@ type ApiNotification struct {
*
*/
func (n ApiNotification) Index(fields string, offset int) revel.Result {
user, err := models.CurrentUser(n.Controller)
if err != nil {
n.Log.Error(TAG, "db", err, "api", ERR_UNAUTHORIZED)
return n.ApiError(http.StatusUnauthorized, ERR_UNAUTHORIZED)
}
var notify models.Notifications
err = notify.FindByUserID(user.ID, offset)
err := notify.FindByUserID(n.CurrentUser.ID, offset)
if err != nil {
n.Log.Error(TAG, "db", err, "api", ERR_SERVER)
return n.ApiError(http.StatusInternalServerError, ERR_SERVER)
......@@ -155,18 +149,12 @@ func (n ApiNotification) Show() revel.Result {
*
*/
func (n ApiNotification) Update(id uint) revel.Result {
user, err := models.CurrentUser(n.Controller)
if err != nil {
n.Log.Error(TAG, "db", err, "api", ERR_UNAUTHORIZED)
return n.ApiError(http.StatusUnauthorized, ERR_UNAUTHORIZED)
}
notify := models.Notification{
ID: id,
UserID: user.ID,
UserID: n.CurrentUser.ID,
Unread: false,
}
err = notify.Update()
err := notify.Update()
if err != nil {
n.Log.Error(TAG, "db", err, "api", ERR_SERVER)
return n.ApiError(http.StatusInternalServerError, ERR_SERVER)
......
......@@ -38,6 +38,21 @@ type TokenString string
* @apiDefine Oauth Oauth endpoint
*
* Create and delete oauth tokens
*
* ## Authentication Process Example
*
* If you want to access API features you will have to register your application first:
*
* ```
* curl -X POST -d "grant_type=password&username=ganggo&password=s3cr3t&client_id=MyAwesomeAppID" https://<your-server>/api/v0/oauth/tokens
* ```
*
* This will return an access token which you should include in your HTTP header (access_token=[...]) for future API calls:
*
* ```
* curl -H "access_token: 0987654312" -X GET https://<your-server>/api/v0/posts
* ```
*
*/
type ApiOAuth struct {
*revel.Controller
......@@ -174,15 +189,21 @@ func (o ApiOAuth) Create() revel.Result {
*/
func (o ApiOAuth) Delete(id uint) revel.Result {
token := models.OAuthToken{ID: id}
user, err := models.CurrentUser(o.Controller)
if err != nil {
o.Log.Error(TAG, "err", err, "api", ERR_UNAUTHORIZED)
return o.ApiError(http.StatusUnauthorized, ERR_UNAUTHORIZED)
}
err = token.Delete(user)
err := token.Delete(o.CurrentUser)
if err != nil {
o.Log.Error(TAG, "err", err, "api", ERR_SERVER)
return o.ApiError(http.StatusInternalServerError, ERR_SERVER)
}
return o.RenderJSON(TokenResult{})
}
// This will overwrite APIHelper checkUser and
// skip authentication on ApiOAuth.Create
func (o ApiOAuth) checkUser() revel.Result {
args := make(map[string]string)
route := revel.MainRouter.Reverse("ApiOAuth.Create", args)
if route != nil && route.URL == o.Request.URL.Path {
return nil
}
return o.ApiHelper.checkUser()
}
......@@ -99,19 +99,15 @@ func (p ApiPost) Index() revel.Result {
p.Params.Bind(&fields, "fields")
p.Params.Bind(&visibility, "visibility")
user, err := models.CurrentUser(p.Controller)
if err != nil {
p.Log.Error(TAG, "db", err, "api", ERR_UNAUTHORIZED)
return p.ApiError(http.StatusUnauthorized, ERR_UNAUTHORIZED)
}
var err error
if visibility == 0 {
err = posts.FindAll(user.ID, offset)
err = posts.FindAll(p.CurrentUser.ID, offset)
} else if visibility == 1 {
err = posts.FindAllPublic(offset)
} else if visibility == 2 {
err = posts.FindAllPrivate(user.ID, offset)
err = posts.FindAllPrivate(p.CurrentUser.ID, offset)
}
if err != nil {
p.Log.Error(TAG, "db", err, "api", ERR_SERVER)
return p.ApiError(http.StatusInternalServerError, ERR_SERVER)
......@@ -188,12 +184,6 @@ func (p ApiPost) Create() revel.Result {
return p.ApiError(http.StatusBadRequest, ERR_EMPTY_STRING)
}
user, err := models.CurrentUser(p.Controller)
if err != nil {
p.Log.Error(TAG, "db", err, "api", ERR_UNAUTHORIZED)
return p.ApiError(http.StatusUnauthorized, ERR_UNAUTHORIZED)
}
// create post
guid, err := helpers.Uuid()
if err != nil {
......@@ -203,7 +193,7 @@ func (p ApiPost) Create() revel.Result {
entity := federation.EntityStatusMessage{
Text: postText,
Author: user.Person.Author,
Author: p.CurrentUser.Person.Author,
Guid: guid,
CreatedAt: time.Now().UTC(),
ProviderName: "GangGo",
......@@ -226,7 +216,7 @@ func (p ApiPost) Create() revel.Result {
// posts to the author
shareable := models.Shareable{
ShareableID: post.ID,
UserID: user.ID,
UserID: p.CurrentUser.ID,
ShareableType: models.ShareablePost,
}; if err := shareable.Create(); err != nil {
p.Log.Error(TAG, "db", err, "api", ERR_SERVER)
......@@ -247,7 +237,7 @@ func (p ApiPost) Create() revel.Result {
}
dispatcher := jobs.Dispatcher{
User: user,
User: p.CurrentUser,
AspectID: aspectID,
Message: entity,
}
......@@ -309,17 +299,11 @@ func (p ApiPost) Show(guid string) revel.Result {
p.Params.Bind(&fields, "fields")
user, err := models.CurrentUser(p.Controller)
if err != nil {
p.Log.Error(TAG, "db", err, "api", ERR_UNAUTHORIZED)
return p.ApiError(http.StatusUnauthorized, ERR_UNAUTHORIZED)
}
postID, err := strconv.ParseUint(guid, 10, 32);
if err == nil {
err = post.FindByIDAndUser(uint(postID), user)
err = post.FindByIDAndUser(uint(postID), p.CurrentUser)
} else {
err = post.FindByGuidAndUser(guid, user)
err = post.FindByGuidAndUser(guid, p.CurrentUser)
}
if err != nil {
p.Log.Error(TAG, "db", err, "api", ERR_SERVER)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment