earaujoassis / space
1
package api
2

3
import (
4
    "fmt"
5
    "net/http"
6
    "strings"
7

8
    "github.com/gin-gonic/contrib/sessions"
9
    "github.com/gin-gonic/gin"
10

11
    "github.com/earaujoassis/space/datastore"
12
    "github.com/earaujoassis/space/models"
13
    "github.com/earaujoassis/space/oauth"
14
    "github.com/earaujoassis/space/security"
15
    "github.com/earaujoassis/space/services"
16
    "github.com/earaujoassis/space/utils"
17
)
18

19
// exposeClientsRoutes defines and exposes HTTP routes for a given gin.RouterGroup
20
//      in the REST API escope, for the clients resource
21 0
func exposeClientsRoutes(router *gin.RouterGroup) {
22 0
    clientsRoutes := router.Group("/clients")
23
    {
24
        // Requires X-Requested-By and Origin (same-origin policy)
25
        // Authorization type: action token / Bearer (for web use)
26 0
        clientsRoutes.GET("/", requiresConformance, actionTokenBearerAuthorization, func(c *gin.Context) {
27 0
            action := c.MustGet("Action").(models.Action)
28 0
            session := sessions.Default(c)
29 0
            userPublicID := session.Get("userPublicID")
30 0
            user := services.FindUserByPublicID(userPublicID.(string))
31 0
            if userPublicID == nil || user.ID == 0 || user.ID != action.UserID || user.Admin != true {
32 0
                c.Header("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"%s\"", c.Request.RequestURI))
33 0
                c.JSON(http.StatusUnauthorized, utils.H{
34 0
                    "_status":  "error",
35 0
                    "_message": "Clients are not available",
36 0
                    "error": oauth.AccessDenied,
37 0
                })
38 0
                return
39
            }
40

41 0
            c.JSON(http.StatusOK, utils.H{
42 0
                "_status":  "success",
43 0
                "_message": "Clients are available",
44 0
                "clients": services.ActiveClients(),
45 0
            })
46
        })
47

48
        // Requires X-Requested-By and Origin (same-origin policy)
49
        // Authorization type: action token / Bearer (for web use)
50 0
        clientsRoutes.POST("/create", requiresConformance, actionTokenBearerAuthorization, func(c *gin.Context) {
51 0
            session := sessions.Default(c)
52 0
            action := c.MustGet("Action").(models.Action)
53 0
            userPublicID := session.Get("userPublicID")
54 0
            user := services.FindUserByPublicID(userPublicID.(string))
55 0
            if userPublicID == nil || user.ID == 0 || user.ID != action.UserID || user.Admin != true {
56 0
                c.Header("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"%s\"", c.Request.RequestURI))
57 0
                c.JSON(http.StatusUnauthorized, utils.H{
58 0
                    "_status":  "error",
59 0
                    "_message": "Client was not created",
60 0
                    "error": oauth.AccessDenied,
61 0
                })
62 0
                return
63
            }
64

65 0
            clientName := c.PostForm("name")
66 0
            clientDescription := c.PostForm("description")
67 0
            clientSecret := models.GenerateRandomString(64)
68 0
            clientScope := models.PublicScope
69 0
            canonicalURI := c.PostForm("canonical_uri")
70 0
            redirectURI := c.PostForm("redirect_uri")
71

72 0
            client := services.CreateNewClient(clientName,
73 0
                clientDescription,
74 0
                clientSecret,
75 0
                clientScope,
76 0
                canonicalURI,
77 0
                redirectURI)
78

79 0
            if client.ID == 0 {
80 0
                c.JSON(http.StatusBadRequest, utils.H{
81 0
                    "_status":  "error",
82 0
                    "_message": "Client was not created",
83 0
                    "error":    "cannot create Client",
84 0
                    "client":   client,
85 0
                })
86 0
            } else {
87 0
                c.JSON(http.StatusNoContent, nil)
88
            }
89
        })
90

91
        // In order to avoid an overhead in this endpoint, it relies only on the cookies session data to guarantee security
92
        // TODO Improve security for this endpoint avoiding any overhead
93 0
        clientsRoutes.PATCH("/:client_id/profile", requiresConformance, actionTokenBearerAuthorization, func(c *gin.Context) {
94 0
            var clientUUID = c.Param("client_id")
95

96 0
            session := sessions.Default(c)
97 0
            action := c.MustGet("Action").(models.Action)
98 0
            userPublicID := session.Get("userPublicID")
99 0
            user := services.FindUserByPublicID(userPublicID.(string))
100 0
            if userPublicID == nil || user.ID == 0 || user.ID != action.UserID || user.Admin != true {
101 0
                c.Header("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"%s\"", c.Request.RequestURI))
102 0
                c.JSON(http.StatusUnauthorized, utils.H{
103 0
                    "_status":  "error",
104 0
                    "_message": "Client was not updated",
105 0
                    "error": oauth.AccessDenied,
106 0
                })
107 0
                return
108
            }
109

110 0
            if !security.ValidUUID(clientUUID) {
111 0
                c.JSON(http.StatusBadRequest, utils.H{
112 0
                    "_status":  "error",
113 0
                    "_message": "Client was not updated",
114 0
                    "error": "must use valid UUID for identification",
115 0
                })
116 0
                return
117
            }
118

119 0
            var newScopes = c.PostForm("scopes")
120
            // Clients can only have read or public scopes
121 0
            if (newScopes != models.PublicScope && newScopes != models.ReadScope) {
122 0
                newScopes = ""
123
            }
124

125 0
            client := services.FindClientByUUID(clientUUID)
126 0
            client.CanonicalURI = c.PostForm("canonical_uri")
127 0
            client.RedirectURI = c.PostForm("redirect_uri")
128 0
            if (newScopes != "") {
129 0
                client.Scopes = newScopes
130
            }
131 0
            dataStore := datastore.GetDataStoreConnection()
132 0
            dataStore.Save(&client)
133 0
            c.JSON(http.StatusNoContent, nil)
134
        })
135

136
        // In order to avoid an overhead in this endpoint, it relies only on the cookies session data to guarantee security
137
        // TODO Improve security for this endpoint avoiding any overhead
138 0
        clientsRoutes.GET("/:client_id/credentials", func(c *gin.Context) {
139 0
            var clientUUID = c.Param("client_id")
140

141 0
            session := sessions.Default(c)
142 0
            userPublicID := session.Get("userPublicID")
143 0
            user := services.FindUserByPublicID(userPublicID.(string))
144 0
            if userPublicID == nil || user.ID == 0 || user.Admin != true {
145 0
                c.Header("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"%s\"", c.Request.RequestURI))
146 0
                c.JSON(http.StatusUnauthorized, utils.H{
147 0
                    "_status":  "error",
148 0
                    "_message": "Client credentials are not available",
149 0
                    "error": oauth.AccessDenied,
150 0
                })
151 0
                return
152
            }
153

154 0
            if !security.ValidUUID(clientUUID) {
155 0
                c.JSON(http.StatusBadRequest, utils.H{
156 0
                    "_status":  "error",
157 0
                    "_message": "Client credentials are not available",
158 0
                    "error": "must use valid UUID for identification",
159 0
                })
160 0
                return
161
            }
162

163 0
            client := services.FindClientByUUID(clientUUID)
164
            // For security reasons, the client's secret is regenerated
165 0
            clientSecret := models.GenerateRandomString(64)
166 0
            client.UpdateSecret(clientSecret)
167 0
            dataStore := datastore.GetDataStoreConnection()
168 0
            dataStore.Save(&client)
169

170 0
            contentString := fmt.Sprintf("name,client_key,client_secret\n%s,%s,%s\n", client.Name, client.Key, clientSecret)
171 0
            content := strings.NewReader(contentString)
172 0
            contentLength := int64(len(contentString))
173 0
            contentType := "text/csv"
174

175 0
            extraHeaders := map[string]string{
176 0
                "Content-Disposition": `attachment; filename="credentials.csv"`,
177
            }
178

179 0
            c.DataFromReader(http.StatusOK, contentLength, contentType, content, extraHeaders)
180
        })
181
    }
182
}

Read our documentation on viewing source code .

Loading