Skip to content

Commit

Permalink
Merge pull request #49 from PierreBougon/develop
Browse files Browse the repository at this point in the history
Release v1.0
  • Loading branch information
PierreBougon authored Dec 2, 2019
2 parents 074d144 + 486d598 commit b240757
Show file tree
Hide file tree
Showing 23 changed files with 1,116 additions and 126 deletions.
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

75 changes: 75 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<p align="center">
<img src="documentation/images/bym.png">
</p>


# Bym API


Bym is an Epitech EIP project that aim to make the playlists sharable and collaborative



## Project state

**Master :** [![Build Status](https://travis-ci.com/PierreBougon/Bym-BackEnd.svg?token=9ToS6xY1MC9b9a6bTDWE&branch=master)](https://travis-ci.com/PierreBougon/Bym-BackEnd) - **Develop :** [![Build Status](https://travis-ci.com/PierreBougon/Bym-BackEnd.svg?token=9ToS6xY1MC9b9a6bTDWE&branch=develop)](https://travis-ci.com/PierreBougon/Bym-BackEnd)

## Getting Started


### Prerequisites

To start this server you need some dependencies use
```shell script
govendor install +local
```

### Installing

To try out Bym you should install Bym mobile application from the Play Store or AppStore.


## Running the tests

Use `go test` to run the tests, please if you want to contribute be sure to write tests as well and make sure the are passing

### Break down into end to end tests

Actually only the models are tested to guarantee a 100% sure solution when it comes to data


### And coding style

Coding style is pretty free on this project I review every Pull Requests to be sure the code has enough quality to be merged, you should follow basic guidelines about clean code

## Deployment

Every deployments are automatized with heroku, when a Pull Request is merge on Develop or Master and if the Travis CI assure 100% of tests passing

___

* **Production URL :** https://bym-backend-prod.herokuapp.com/

* **Development URL :** https://bym-back-end.herokuapp.com/



## Contributing

Please feel free to open a Pull Request if you think you have a good piece of code that could be useful on production. You can also open issues to asks or discuss about anything in this project.


## Authors

* **Pierre Bougon** - *Owner developer*
* **Mathis Guilbon** - *Developer*

See also the list of [contributors](https://github.com/PierreBougon/Bym-BackEnd/contributors) who participated in this project.

## License

This project is licensed under the GNU General Public License v3.0 - see the [LICENSE.md](LICENSE.md) file for details

## Acknowledgments

* Bym Team to made this project a successful EIP
12 changes: 11 additions & 1 deletion app/auth.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package app

import (
"fmt"
"github.com/PierreBougon/Bym-BackEnd/models"
u "github.com/PierreBougon/Bym-BackEnd/utils"

Expand Down Expand Up @@ -37,7 +38,16 @@ var JwtAuthentication = func(next http.Handler) http.Handler {
}

tokenHeader := r.Header.Get("Authorization") //Grab the token from the header
if tokenHeader == "" { //Token is missing, returns with error code 403 Unauthorized
//special case to try if auth header is contained into javascript websocket header
if tokenHeader == "" {
fmt.Println(r.Header.Get("Sec-WebSocket-Protocol"))
tokenHeader = r.Header.Get("Sec-WebSocket-Protocol") //Grab the token from the header
if strings.Contains(tokenHeader, "bearer") && !strings.Contains(tokenHeader, " ") {
tokenHeader = tokenHeader[:6] + " " + tokenHeader[6:]
}
//r.Header.Set("Sec-WebSocket-Protocol", "Protocol_BYM")
}
if tokenHeader == "" { //Token is missing, returns with error code 403 Unauthorized
sendErrorJson(w, "Missing auth token", http.StatusForbidden)
return
}
Expand Down
2 changes: 1 addition & 1 deletion controllers/authController.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var Authenticate = func(w http.ResponseWriter, r *http.Request) {

resp := models.Login(account.Email, account.Password)
if resp["status"] == false {
u.RespondUnhautorized(w)
u.RespondUnauthorized(w)
} else {
u.Respond(w, resp)
}
Expand Down
70 changes: 66 additions & 4 deletions controllers/playlistController.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package controllers

import (
"fmt"
"github.com/PierreBougon/Bym-BackEnd/models"
u "github.com/PierreBougon/Bym-BackEnd/utils"
"github.com/PierreBougon/Bym-BackEnd/websocket"

"encoding/json"
"github.com/gorilla/mux"
Expand All @@ -25,6 +27,7 @@ var CreatePlaylist = func(w http.ResponseWriter, r *http.Request) {
}

var GetPlaylists = func(w http.ResponseWriter, r *http.Request) {
fmt.Println("get playlists")
vals := r.URL.Query() // Returns a url.Values, which is a map[string][]string
user_id, ok := vals["user_id"] // Note type, not ID. ID wasn't specified anywhere.

Expand All @@ -47,6 +50,38 @@ var GetPlaylists = func(w http.ResponseWriter, r *http.Request) {
}

var GetPlaylist = func(w http.ResponseWriter, r *http.Request) {
filter := models.PlaylistFilter{
ShowSongs: true,
ShowFollower: false,
ShowAcl: false,
}
getBoolVal := func(val []string, b *bool) bool {
if len(val) >= 1 {
ret, err := strconv.ParseBool(val[0])
if err == nil {
*b = ret
return true
}
}
return false
}
vals := r.URL.Query()
for name, val := range vals {
goodParam := false
switch name {
case "showSongs":
goodParam = getBoolVal(val, &filter.ShowSongs)
case "showFollower":
goodParam = getBoolVal(val, &filter.ShowFollower)
case "showAcl":
goodParam = getBoolVal(val, &filter.ShowAcl)
}
if !goodParam {
w.WriteHeader(http.StatusBadRequest)
u.Respond(w, u.Message(false, "Invalid request, wrong value given to "+name))
return
}
}
params := mux.Vars(r)
id, err := strconv.Atoi(params["id"])

Expand All @@ -55,7 +90,7 @@ var GetPlaylist = func(w http.ResponseWriter, r *http.Request) {
return
}

data := models.GetPlaylistById(uint(id))
data := models.GetPlaylistById(uint(id), &filter)
if data == nil {
u.RespondBadRequest(w)
return
Expand All @@ -82,6 +117,8 @@ var UpdatePlaylist = func(w http.ResponseWriter, r *http.Request) {
resp := playlist.UpdatePlaylist(user, uint(id), playlist)
if resp["status"] == false {
w.WriteHeader(http.StatusBadRequest)
} else {
websocket.NotifyPlaylistSubscribers(user, uint(id), websocket.PlaylistNeedRefresh(uint(id), user))
}
u.Respond(w, resp)
}
Expand All @@ -97,6 +134,8 @@ var LeavePlaylist = func(w http.ResponseWriter, r *http.Request) {
resp := (&models.Playlist{}).LeavePlaylist(user, uint(id))
if resp["status"] == false {
w.WriteHeader(http.StatusBadRequest)
} else {
websocket.NotifyPlaylistSubscribers(user, uint(id), websocket.PlaylistNeedRefresh(uint(id), user))
}
u.Respond(w, resp)
}
Expand All @@ -109,7 +148,8 @@ var DeletePlaylist = func(w http.ResponseWriter, r *http.Request) {
return
}
user := r.Context().Value("user").(uint)
resp := (&models.Playlist{}).DeletePlaylist(user, uint(id))
messageOnDelete := websocket.PlaylistDeleted(uint(id), user)
resp := (&models.Playlist{}).DeletePlaylist(user, uint(id), websocket.NotifyPlaylistSubscribers, messageOnDelete)
if resp["status"] == false {
w.WriteHeader(http.StatusBadRequest)
}
Expand All @@ -127,11 +167,12 @@ var JoinPlaylist = func(w http.ResponseWriter, r *http.Request) {
resp := (&models.Playlist{}).Join(user, uint(id))
if resp["status"] == false {
w.WriteHeader(http.StatusBadRequest)
} else {
websocket.NotifyPlaylistSubscribers(user, uint(id), websocket.PlaylistNeedRefresh(uint(id), user))
}
u.Respond(w, resp)
}


var ChangeAclOnPlaylist = func(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id, err := strconv.ParseUint(params["id"], 10, 32)
Expand All @@ -154,6 +195,27 @@ var ChangeAclOnPlaylist = func(w http.ResponseWriter, r *http.Request) {
resp := models.ChangeAclOnPlaylist(user, *userAcl.User, uint(id), *userAcl.Role)
if resp["status"] == false {
w.WriteHeader(http.StatusBadRequest)
} else {
websocket.NotifyPlaylistSubscribers(user, uint(id), websocket.PlaylistNeedRefresh(uint(id), user))
}
u.Respond(w, resp)
}
}

var GetPlaylistRole = func(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id, err := strconv.ParseUint(params["id"], 10, 32)
if err != nil {
u.RespondBadRequest(w)
return
}
user := r.Context().Value("user").(uint)
data, errMsg := models.GetRole(user, uint(id))
var resp map[string]interface{}
if errMsg != "" {
resp = u.Message(false, errMsg)
} else {
resp = u.Message(true, "success")
}
resp["role"] = data
u.Respond(w, resp)
}
15 changes: 8 additions & 7 deletions controllers/songController.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package controllers
import (
"github.com/PierreBougon/Bym-BackEnd/models"
u "github.com/PierreBougon/Bym-BackEnd/utils"
"github.com/PierreBougon/Bym-BackEnd/websocket"
"github.com/gorilla/mux"

"encoding/json"
Expand All @@ -19,7 +20,7 @@ var CreateSong = func(w http.ResponseWriter, r *http.Request) {
return
}

resp := song.Create(user)
resp := song.Create(user, websocket.NotifyPlaylistSubscribers, websocket.PlaylistNeedRefresh)
if resp["status"] == false {
w.WriteHeader(http.StatusBadRequest)
}
Expand All @@ -29,11 +30,11 @@ var CreateSong = func(w http.ResponseWriter, r *http.Request) {
var GetSongs = func(w http.ResponseWriter, r *http.Request) {
var param string

vals := r.URL.Query() // Returns a url.Values, which is a map[string][]string
playlist_id, ok := vals["playlist_id"] // Note type, not ID. ID wasn't specified anywhere.
vals := r.URL.Query() // Returns a url.Values, which is a map[string][]string
playlistId, ok := vals["playlist_id"] // Note type, not ID. ID wasn't specified anywhere.

if ok && len(playlist_id) >= 1 {
param = playlist_id[0] // The first `?type=model`
if ok && len(playlistId) >= 1 {
param = playlistId[0] // The first `?type=model`
} else {
w.WriteHeader(http.StatusBadRequest)
u.Respond(w, u.Message(false, "Invalid request need playlist id"))
Expand Down Expand Up @@ -72,7 +73,7 @@ var UpdateSong = func(w http.ResponseWriter, r *http.Request) {
u.RespondBadRequest(w)
return
}
resp := song.UpdateSong(user, uint(id), song)
resp := song.UpdateSong(user, uint(id), song, websocket.NotifyPlaylistSubscribers, websocket.PlaylistNeedRefresh)
if resp["status"] == false {
w.WriteHeader(http.StatusBadRequest)
}
Expand All @@ -87,7 +88,7 @@ var DeleteSong = func(w http.ResponseWriter, r *http.Request) {
return
}
user := r.Context().Value("user").(uint)
resp := (&models.Song{}).DeleteSong(user, uint(id))
resp := (&models.Song{}).DeleteSong(user, uint(id), websocket.NotifyPlaylistSubscribers, websocket.PlaylistNeedRefresh)
if resp["status"] == false {
w.WriteHeader(http.StatusBadRequest)
}
Expand Down
11 changes: 7 additions & 4 deletions controllers/websocketController.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package controllers

import (
"github.com/PierreBougon/Bym-BackEnd/websocket_communication"
"fmt"
"github.com/PierreBougon/Bym-BackEnd/websocket"
"net/http"
)

var ConnectWebSocket = func(w http.ResponseWriter, r *http.Request) {
print("Trying to establish a connection")
var wsPool *websocket_communication.WSPool
wsPool = websocket_communication.GetWSPool()
fmt.Println("Trying to establish a connection")
fmt.Println(r.Header.Get("Sec-WebSocket-Protocol")) //Grab the token from the header

var wsPool *websocket.WSPool
wsPool = websocket.GetWSPool()
wsPool.CreateWebSocket(w, r)
}
Binary file added documentation/images/bym.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 4 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ func main() {
router.HandleFunc("/", u.RespondBasicSuccess).Methods("GET")
api.HandleFunc("", u.RespondBasicSuccess).Methods("GET")

//Connect Websocket
api.HandleFunc("/ws", controllers.ConnectWebSocket).Methods("GET")
// Connect Websocket
router.HandleFunc("/ws", controllers.ConnectWebSocket).Methods("GET")

// Auth / Account
auth := api.PathPrefix("/user").Subrouter()
Expand All @@ -57,6 +57,7 @@ func main() {
playlist.HandleFunc("/join/{id}", controllers.JoinPlaylist).Methods("POST")
playlist.HandleFunc("/leave/{id}", controllers.LeavePlaylist).Methods("DELETE")
playlist.HandleFunc("/change_user_acl/{id}", controllers.ChangeAclOnPlaylist).Methods("POST")
playlist.HandleFunc("/get_role/{id}", controllers.GetPlaylistRole).Methods("GET")

// Songs
song := api.PathPrefix("/song").Subrouter()
Expand All @@ -78,6 +79,7 @@ func main() {

//Launch the app, visit localhost:443/api
err := http.ListenAndServe(":"+port, router)

if err != nil {
fmt.Print(err)
}
Expand Down
16 changes: 12 additions & 4 deletions models/PlaylistAccessControl.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package models

import "time"
import (
"fmt"
"strconv"
"time"
)

type PlaylistAccessControl struct {
CreatedAt time.Time
Expand All @@ -13,17 +17,21 @@ type PlaylistAccessControl struct {
func checkRight(user uint, playlist uint, neededRole uint) bool {
acl := &PlaylistAccessControl{}
p := &Playlist{}
fmt.Println("Check right on playlist " + fmt.Sprintf("%d role needed is %d", playlist, neededRole))
db.Table("playlists").Where("id = ?", playlist).First(p)
if user == p.UserId {
fmt.Println("User is authorized to do the action because he is the author.")
return true
}

notFound := db.Table("playlist_access_control").Where(PlaylistAccessControl{
notFound := db.Table("playlist_access_controls").Where(PlaylistAccessControl{
UserId: user,
PlaylistId: playlist,
}).First(acl).RecordNotFound()
if notFound || acl.RoleId > neededRole {
if notFound {
fmt.Println("User does not have any right on this playlist.")
return false
}
return true
fmt.Println("Current role on this playlist : " + string(acl.RoleId) + " Result : " + strconv.FormatBool(acl.RoleId <= neededRole))
return acl.RoleId <= neededRole
}
Loading

0 comments on commit b240757

Please sign in to comment.