mirror of
				https://github.com/owncast/owncast.git
				synced 2025-11-01 02:44:31 +08:00 
			
		
		
		
	Disconnect stream Admin API + HTTP Basic Auth (#204)
* Create http auth middleware * Add support for ending the inbound stream. Closes #191 * Add a simple success response to API requests
This commit is contained in:
		
							
								
								
									
										19
									
								
								controllers/admin.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								controllers/admin.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| package controllers | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/gabek/owncast/core" | ||||
| 	"github.com/gabek/owncast/core/rtmp" | ||||
| ) | ||||
|  | ||||
| // DisconnectInboundConnection will force-disconnect an inbound stream | ||||
| func DisconnectInboundConnection(w http.ResponseWriter, r *http.Request) { | ||||
| 	if !core.GetStatus().Online { | ||||
| 		writeSimpleResponse(w, false, "no inbound stream connected") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	rtmp.Disconnect() | ||||
| 	writeSimpleResponse(w, true, "inbound stream disconnected") | ||||
| } | ||||
| @ -3,6 +3,8 @@ package controllers | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/gabek/owncast/models" | ||||
| ) | ||||
|  | ||||
| type j map[string]interface{} | ||||
| @ -24,3 +26,12 @@ func badRequestHandler(w http.ResponseWriter, err error) { | ||||
| 	w.WriteHeader(http.StatusBadRequest) | ||||
| 	json.NewEncoder(w).Encode(j{"error": err.Error()}) | ||||
| } | ||||
|  | ||||
| func writeSimpleResponse(w http.ResponseWriter, success bool, message string) { | ||||
| 	response := models.BaseAPIResponse{ | ||||
| 		Success: success, | ||||
| 		Message: message, | ||||
| 	} | ||||
| 	w.WriteHeader(http.StatusOK) | ||||
| 	json.NewEncoder(w).Encode(response) | ||||
| } | ||||
|  | ||||
| @ -28,6 +28,7 @@ var ( | ||||
|  | ||||
| var _transcoder ffmpeg.Transcoder | ||||
| var _pipe *os.File | ||||
| var _rtmpConnection net.Conn | ||||
|  | ||||
| //Start starts the rtmp service, listening on port 1935 | ||||
| func Start() { | ||||
| @ -92,6 +93,7 @@ func HandleConn(c *rtmp.Conn, nc net.Conn) { | ||||
|  | ||||
| 	_isConnected = true | ||||
| 	core.SetStreamAsConnected() | ||||
| 	_rtmpConnection = nc | ||||
|  | ||||
| 	f, err := os.OpenFile(pipePath, os.O_RDWR, os.ModeNamedPipe) | ||||
| 	_pipe = f | ||||
| @ -121,9 +123,20 @@ func handleDisconnect(conn net.Conn) { | ||||
| 	_pipe.Close() | ||||
| 	_isConnected = false | ||||
| 	_transcoder.Stop() | ||||
| 	_rtmpConnection = nil | ||||
| 	core.SetStreamAsDisconnected() | ||||
| } | ||||
|  | ||||
| // Disconnect will force disconnect the current inbound RTMP connection. | ||||
| func Disconnect() { | ||||
| 	if _rtmpConnection == nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	log.Infoln("Inbound stream disconnect requested.") | ||||
| 	handleDisconnect(_rtmpConnection) | ||||
| } | ||||
|  | ||||
| //IsConnected gets whether there is an rtmp connection or not | ||||
| //this is only a getter since it is controlled by the rtmp handler | ||||
| func IsConnected() bool { | ||||
|  | ||||
							
								
								
									
										7
									
								
								models/baseAPIResponse.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								models/baseAPIResponse.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| package models | ||||
|  | ||||
| // BaseAPIResponse is a simple response to API requests. | ||||
| type BaseAPIResponse struct { | ||||
| 	Success bool   `json:"success"` | ||||
| 	Message string `json:"message"` | ||||
| } | ||||
							
								
								
									
										34
									
								
								router/middleware/auth.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								router/middleware/auth.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| package middleware | ||||
|  | ||||
| import ( | ||||
| 	"crypto/subtle" | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/gabek/owncast/config" | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| // RequireAdminAuth wraps a handler requiring HTTP basic auth for it using the given | ||||
| // the stream key as the password and and a hardcoded "admin" for username. | ||||
| func RequireAdminAuth(handler http.HandlerFunc) http.HandlerFunc { | ||||
| 	username := "admin" | ||||
| 	password := config.Config.VideoSettings.StreamingKey | ||||
|  | ||||
| 	return func(w http.ResponseWriter, r *http.Request) { | ||||
|  | ||||
| 		user, pass, ok := r.BasicAuth() | ||||
| 		realm := "Owncast Authenticated Request" | ||||
|  | ||||
| 		// Failed | ||||
| 		if !ok || subtle.ConstantTimeCompare([]byte(user), []byte(username)) != 1 || subtle.ConstantTimeCompare([]byte(pass), []byte(password)) != 1 { | ||||
| 			w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`"`) | ||||
| 			http.Error(w, "Unauthorized", http.StatusUnauthorized) | ||||
| 			log.Warnln("Failed authentication for", r.URL.Path, "from", r.RemoteAddr, r.UserAgent()) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		// Success | ||||
| 		log.Traceln("Authenticated request OK for", r.URL.Path, "from", r.RemoteAddr, r.UserAgent()) | ||||
| 		handler(w, r) | ||||
| 	} | ||||
| } | ||||
| @ -10,6 +10,7 @@ import ( | ||||
| 	"github.com/gabek/owncast/controllers" | ||||
| 	"github.com/gabek/owncast/core/chat" | ||||
| 	"github.com/gabek/owncast/core/rtmp" | ||||
| 	"github.com/gabek/owncast/router/middleware" | ||||
| ) | ||||
|  | ||||
| //Start starts the router for the http, ws, and rtmp | ||||
| @ -43,6 +44,11 @@ func Start() error { | ||||
| 		http.HandleFunc("/embed/video", controllers.GetVideoEmbed) | ||||
| 	} | ||||
|  | ||||
| 	// Authenticated admin requests | ||||
|  | ||||
| 	// Disconnect inbound stream | ||||
| 	http.HandleFunc("/api/admin/disconnect", middleware.RequireAdminAuth(controllers.DisconnectInboundConnection)) | ||||
|  | ||||
| 	port := config.Config.GetPublicWebServerPort() | ||||
|  | ||||
| 	log.Infof("Web server running on port: %d", port) | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Gabe Kangas
					Gabe Kangas