mirror of
https://github.com/1f349/tulip.git
synced 2024-12-22 08:14:13 +00:00
Add support for public oauth client apps
This commit is contained in:
parent
33faf6aa5f
commit
e822172513
@ -105,7 +105,7 @@ func (u *UserPatch) ParseFromForm(v url.Values) (safeErrs []error) {
|
||||
|
||||
type ClientInfoDbOutput struct {
|
||||
Sub, Name, Secret, Domain, Owner string
|
||||
SSO, Active bool
|
||||
Public, SSO, Active bool
|
||||
}
|
||||
|
||||
var _ oauth2.ClientInfo = &ClientInfoDbOutput{}
|
||||
@ -113,7 +113,7 @@ var _ oauth2.ClientInfo = &ClientInfoDbOutput{}
|
||||
func (c *ClientInfoDbOutput) GetID() string { return c.Sub }
|
||||
func (c *ClientInfoDbOutput) GetSecret() string { return c.Secret }
|
||||
func (c *ClientInfoDbOutput) GetDomain() string { return c.Domain }
|
||||
func (c *ClientInfoDbOutput) IsPublic() bool { return false }
|
||||
func (c *ClientInfoDbOutput) IsPublic() bool { return c.Public }
|
||||
func (c *ClientInfoDbOutput) GetUserID() string { return c.Owner }
|
||||
|
||||
// GetName is an extra field for the oauth handler to display the application
|
||||
|
@ -27,6 +27,7 @@ CREATE TABLE IF NOT EXISTS client_store
|
||||
secret TEXT UNIQUE NOT NULL,
|
||||
domain TEXT NOT NULL,
|
||||
owner TEXT NOT NULL,
|
||||
public INTEGER,
|
||||
sso INTEGER,
|
||||
active INTEGER DEFAULT 1,
|
||||
FOREIGN KEY (owner) REFERENCES users (subject)
|
||||
|
@ -198,8 +198,8 @@ func (t *Tx) HasTwoFactor(sub uuid.UUID) (bool, error) {
|
||||
|
||||
func (t *Tx) GetClientInfo(sub string) (oauth2.ClientInfo, error) {
|
||||
var u ClientInfoDbOutput
|
||||
row := t.tx.QueryRow(`SELECT secret, name, domain, sso, active FROM client_store WHERE subject = ? LIMIT 1`, sub)
|
||||
err := row.Scan(&u.Secret, &u.Name, &u.Domain, &u.SSO, &u.Active)
|
||||
row := t.tx.QueryRow(`SELECT secret, name, domain, public, sso, active FROM client_store WHERE subject = ? LIMIT 1`, sub)
|
||||
err := row.Scan(&u.Secret, &u.Name, &u.Domain, &u.Public, &u.SSO, &u.Active)
|
||||
u.Owner = sub
|
||||
if !u.Active {
|
||||
return nil, fmt.Errorf("client is not active")
|
||||
@ -207,16 +207,16 @@ func (t *Tx) GetClientInfo(sub string) (oauth2.ClientInfo, error) {
|
||||
return &u, err
|
||||
}
|
||||
|
||||
func (t *Tx) GetAppList(offset int) ([]ClientInfoDbOutput, error) {
|
||||
func (t *Tx) GetAppList(owner uuid.UUID, admin bool, offset int) ([]ClientInfoDbOutput, error) {
|
||||
var u []ClientInfoDbOutput
|
||||
row, err := t.tx.Query(`SELECT subject, name, domain, owner, sso, active FROM client_store LIMIT 25 OFFSET ?`, offset)
|
||||
row, err := t.tx.Query(`SELECT subject, name, domain, owner, public, sso, active FROM client_store WHERE owner = ? OR ? = 1 LIMIT 25 OFFSET ?`, owner.String(), admin, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer row.Close()
|
||||
for row.Next() {
|
||||
var a ClientInfoDbOutput
|
||||
err := row.Scan(&a.Sub, &a.Name, &a.Domain, &a.Owner, &a.SSO, &a.Active)
|
||||
err := row.Scan(&a.Sub, &a.Name, &a.Domain, &a.Owner, &a.Public, &a.SSO, &a.Active)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -225,18 +225,18 @@ func (t *Tx) GetAppList(offset int) ([]ClientInfoDbOutput, error) {
|
||||
return u, row.Err()
|
||||
}
|
||||
|
||||
func (t *Tx) InsertClientApp(name, domain string, sso, active bool, owner uuid.UUID) error {
|
||||
func (t *Tx) InsertClientApp(name, domain string, public, sso, active bool, owner uuid.UUID) error {
|
||||
u := uuid.New()
|
||||
secret, err := password.GenerateApiSecret(70)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = t.tx.Exec(`INSERT INTO client_store (subject, name, secret, domain, owner, sso, active) VALUES (?, ?, ?, ?, ?, ?, ?)`, u.String(), name, secret, domain, owner.String(), sso, active)
|
||||
_, err = t.tx.Exec(`INSERT INTO client_store (subject, name, secret, domain, owner, public, sso, active) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, u.String(), name, secret, domain, owner.String(), public, sso, active)
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *Tx) UpdateClientApp(subject, owner uuid.UUID, name, domain string, sso, active bool) error {
|
||||
_, err := t.tx.Exec(`UPDATE client_store SET name = ?, domain = ?, sso = ?, active = ? WHERE subject = ? AND owner = ?`, name, domain, sso, active, subject.String(), owner.String())
|
||||
func (t *Tx) UpdateClientApp(subject, owner uuid.UUID, name, domain string, public, sso, active bool) error {
|
||||
_, err := t.tx.Exec(`UPDATE client_store SET name = ?, domain = ?, public = ?, sso = ?, active = ? WHERE subject = ? AND owner = ?`, name, domain, public, sso, active, subject.String(), owner.String())
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -25,11 +25,13 @@
|
||||
<button type="submit">Manage Applications</button>
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
<form method="GET" action="/manage/users">
|
||||
<button type="submit">Manage Users</button>
|
||||
</form>
|
||||
</div>
|
||||
{{if .IsAdmin}}
|
||||
<div>
|
||||
<form method="GET" action="/manage/users">
|
||||
<button type="submit">Manage Users</button>
|
||||
</form>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .OtpEnabled}}
|
||||
<div>
|
||||
<form method="POST" action="/edit/otp">
|
||||
|
@ -58,15 +58,16 @@
|
||||
<label for="field_domain">Domain:</label>
|
||||
<input type="text" name="domain" id="field_domain" value="{{.Edit.Domain}}" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_public">Public: <input type="checkbox" name="public" id="field_public" {{if .Edit.Public}}checked{{end}}/></label>
|
||||
</div>
|
||||
{{if .IsAdmin}}
|
||||
<div>
|
||||
<label for="field_sso">SSO: <input type="checkbox" name="sso" id="field_sso"
|
||||
{{if .Edit.SSO}}checked{{end}}/></label>
|
||||
<label for="field_sso">SSO: <input type="checkbox" name="sso" id="field_sso" {{if .Edit.SSO}}checked{{end}}/></label>
|
||||
</div>
|
||||
{{end}}
|
||||
<div>
|
||||
<label for="field_active">Active: <input type="checkbox" name="active" id="field_active"
|
||||
{{if .Edit.Active}}checked{{end}}/></label>
|
||||
<label for="field_active">Active: <input type="checkbox" name="active" id="field_active" {{if .Edit.Active}}checked{{end}}/></label>
|
||||
</div>
|
||||
<button type="submit">Edit</button>
|
||||
</form>
|
||||
@ -85,6 +86,7 @@
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Domain</th>
|
||||
<th>Public</th>
|
||||
<th>SSO</th>
|
||||
<th>Active</th>
|
||||
<th>Owner</th>
|
||||
@ -97,6 +99,7 @@
|
||||
<td>{{.Sub}}</td>
|
||||
<td>{{.Name}}</td>
|
||||
<td>{{.Domain}}</td>
|
||||
<td>{{.Public}}</td>
|
||||
<td>{{.SSO}}</td>
|
||||
<td>{{.Active}}</td>
|
||||
<td>{{.Owner}}</td>
|
||||
@ -131,6 +134,9 @@
|
||||
<label for="field_domain">Domain:</label>
|
||||
<input type="text" name="domain" id="field_domain" required/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_public">Public: <input type="checkbox" name="public" id="field_public"/></label>
|
||||
</div>
|
||||
{{if .IsAdmin}}
|
||||
<div>
|
||||
<label for="field_sso">SSO: <input type="checkbox" name="sso" id="field_sso"/></label>
|
||||
|
@ -83,9 +83,9 @@ func (h *HttpServer) OptionalAuthentication(flowPart bool, next UserHandler) htt
|
||||
return
|
||||
}
|
||||
if auth.IsGuest() {
|
||||
if loginCookie, err := req.Cookie("login-data"); err == nil {
|
||||
if loginCookie, err := req.Cookie("tulip-login-data"); err == nil {
|
||||
if decryptedBytes, err := base64.RawStdEncoding.DecodeString(loginCookie.Value); err == nil {
|
||||
if decryptedData, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, h.signingKey.PrivateKey(), decryptedBytes, []byte("login-data")); err == nil {
|
||||
if decryptedData, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, h.signingKey.PrivateKey(), decryptedBytes, []byte("tulip-login-data")); err == nil {
|
||||
if len(decryptedData) == 16 {
|
||||
var u uuid.UUID
|
||||
copy(u[:], decryptedData[:])
|
||||
|
@ -150,13 +150,13 @@ func (h *HttpServer) LoginPost(rw http.ResponseWriter, req *http.Request, _ http
|
||||
}
|
||||
|
||||
func (h *HttpServer) setLoginDataCookie(rw http.ResponseWriter, userId uuid.UUID) bool {
|
||||
encryptedData, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, h.signingKey.PublicKey(), userId[:], []byte("login-data"))
|
||||
encryptedData, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, h.signingKey.PublicKey(), userId[:], []byte("tulip-login-data"))
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
encryptedString := base64.RawStdEncoding.EncodeToString(encryptedData)
|
||||
http.SetCookie(rw, &http.Cookie{
|
||||
Name: "login-data",
|
||||
Name: "tulip-login-data",
|
||||
Value: encryptedString,
|
||||
Path: "/",
|
||||
Expires: time.Now().AddDate(0, 3, 0),
|
||||
|
@ -30,7 +30,7 @@ func (h *HttpServer) ManageAppsGet(rw http.ResponseWriter, req *http.Request, _
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
appList, err = tx.GetAppList(offset)
|
||||
appList, err = tx.GetAppList(auth.Data.ID, role == database.RoleAdmin, offset)
|
||||
return
|
||||
}) {
|
||||
return
|
||||
@ -72,6 +72,7 @@ func (h *HttpServer) ManageAppsPost(rw http.ResponseWriter, req *http.Request, _
|
||||
action := req.Form.Get("action")
|
||||
name := req.Form.Get("name")
|
||||
domain := req.Form.Get("domain")
|
||||
public := req.Form.Has("public")
|
||||
sso := req.Form.Has("sso")
|
||||
active := req.Form.Has("active")
|
||||
|
||||
@ -92,7 +93,7 @@ func (h *HttpServer) ManageAppsPost(rw http.ResponseWriter, req *http.Request, _
|
||||
switch action {
|
||||
case "create":
|
||||
if h.DbTx(rw, func(tx *database.Tx) error {
|
||||
return tx.InsertClientApp(name, domain, sso, active, auth.Data.ID)
|
||||
return tx.InsertClientApp(name, domain, public, sso, active, auth.Data.ID)
|
||||
}) {
|
||||
return
|
||||
}
|
||||
@ -102,7 +103,7 @@ func (h *HttpServer) ManageAppsPost(rw http.ResponseWriter, req *http.Request, _
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.UpdateClientApp(sub, auth.Data.ID, name, domain, sso, active)
|
||||
return tx.UpdateClientApp(sub, auth.Data.ID, name, domain, public, sso, active)
|
||||
}) {
|
||||
return
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ func NewHttpServer(conf Conf, db *database.DB, signingKey mjwt.Signer) *http.Ser
|
||||
}
|
||||
|
||||
http.SetCookie(rw, &http.Cookie{
|
||||
Name: "login-data",
|
||||
Name: "tulip-login-data",
|
||||
Path: "/",
|
||||
MaxAge: -1,
|
||||
Secure: true,
|
||||
@ -173,8 +173,8 @@ func NewHttpServer(conf Conf, db *database.DB, signingKey mjwt.Signer) *http.Ser
|
||||
r.POST("/edit/otp", hs.RequireAuthentication(hs.EditOtpPost))
|
||||
|
||||
// management pages
|
||||
r.GET("/manage/apps", hs.RequireAdminAuthentication(hs.ManageAppsGet))
|
||||
r.POST("/manage/apps", hs.RequireAdminAuthentication(hs.ManageAppsPost))
|
||||
r.GET("/manage/apps", hs.RequireAuthentication(hs.ManageAppsGet))
|
||||
r.POST("/manage/apps", hs.RequireAuthentication(hs.ManageAppsPost))
|
||||
r.GET("/manage/users", hs.RequireAdminAuthentication(hs.ManageUsersGet))
|
||||
r.POST("/manage/users", hs.RequireAdminAuthentication(hs.ManageUsersPost))
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user