Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a new feature to handle authentication using query path. #215

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Binary file added .gck.sh.swp
Binary file not shown.
27 changes: 27 additions & 0 deletions auth/entity/client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package client

import "encoding/json"

type Client struct {
Email string `json:"email,omitempty"`
Code string `json:"code,omitempty"`
Key string `json:"key,omitempty"`
Active bool `json:"active,omitempty"`
LastSessionKey string `json:"lsk,omitempty"`
}

type Clients = []Client

func New() *Client {
return &Client{}
}

func (client *Client) ToJson() ([]byte, error) {
return json.Marshal(client)
}

func FromJson(data []byte) (client *Client, err error) {
client = New()
err = json.Unmarshal(data, client)
return
}
43 changes: 43 additions & 0 deletions auth/entity/session/session.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package connection

import (
"encoding/json"
"fmt"
"time"

"github.com/gwuhaolin/livego/auth/utils"
)

type Session struct {
_Key string
TimeStamp string `json:"time,omitempty"`
Code string `json:"code,omitempty"`
IpAddress string `json:"ip,omitempty"`
Active bool `json:"active,omitempty"`
}

type Connections = []Session

func New(code, IpAddress string) *Session {
return &Session{
_Key: fmt.Sprintf("session-[%s-%s]", utils.RandomNumberString(4), utils.RandomNumberString(5)),
TimeStamp: time.Now().UTC().Format(time.RFC3339Nano),
Code: code,
IpAddress: IpAddress,
Active: true,
}
}

func (session *Session) Key() string {
return session._Key
}

func (client *Session) ToJson() ([]byte, error) {
return json.Marshal(client)
}

func FromJson(data []byte) (conn *Session, err error) {
conn = &Session{}
err = json.Unmarshal(data, conn)
return
}
141 changes: 141 additions & 0 deletions auth/security.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package auth

import (
"fmt"
"net/url"
"strings"

"github.com/go-redis/redis/v7"
log "github.com/sirupsen/logrus"

cli "github.com/gwuhaolin/livego/auth/entity/client"
sess "github.com/gwuhaolin/livego/auth/entity/session"
"github.com/gwuhaolin/livego/configure"
)

var Auth *Security

type Security struct {
redisCli *redis.Client
Allow func(string, string) (bool, error)
}

func init() {

protected := configure.Config.GetBool("is_protected")
log.Infof("security.init(): %v", protected)

// Identify if Authentication is enabled:
Auth = New(protected)
// =====================================================

log.Infof("redis_addr: %s | redis_pwd: %s | redis_db: %d",
configure.Config.GetString("redis_addr"),
configure.Config.GetString("redis_pwd"),
configure.Config.GetInt("redis_db"))
if len(configure.Config.GetString("redis_addr")) != 0 {

Auth.redisCli = redis.NewClient(&redis.Options{
Addr: configure.Config.GetString("redis_addr"),
Password: configure.Config.GetString("redis_pwd"),
DB: configure.Config.GetInt("redis_db"),
})
_, err := Auth.redisCli.Ping().Result()
if err != nil {
log.Panic("Redis: ", err)
}
log.Info("Redis connected")

}

}

func New(protected bool) (security *Security) {

security = &Security{}
if protected {
security.Allow = security.allow
} else {
security.Allow = func(string, string) (bool, error) {
log.Debugf("security.Allow(): default true")
return true, nil // Default true
}
}
return

}

func (security *Security) allow(ipAddr, value string) (allowed bool, err error) {

allowed = false
if url, err := url.Parse(value); err == nil {
user := url.Query().Get("u")
key := url.Query().Get("k")
log.Debugf("security.Allow(): [%s]-[%s]", user, len(key) > 0)

if client, err := security.findClient(user); err == nil {
if client != nil && client.Active {

lsk := client.LastSessionKey

session, err := security.findSession(lsk)
if err == redis.Nil || session == nil {
session = sess.New(user, ipAddr)
if err = security.saveSession(session); err == nil {
client.LastSessionKey = session.Key()
if err = security.saveClient(client); err == nil {
log.Debugf("security.Allow(): %+v", session)
allowed = true
}
}
} else {
// Validate the IP Address from source to identify if it is another session to the same user:
allowed = (strings.Split(ipAddr, ":")[0] == strings.Split(session.IpAddress, ":")[0])
}
}
}
}
if err != nil {
log.Errorf("security.Allow(): %s", err.Error())
}
return

}

func (s *Security) findClient(code string) (*cli.Client, error) {

if value, err := s.redisCli.Get(fmt.Sprintf("client-%s", code)).Result(); err == nil {
return cli.FromJson([]byte(value))
} else {
return nil, err
}

}

func (s *Security) saveClient(client *cli.Client) (err error) {

content, _ := client.ToJson()
cmd_val, err := s.redisCli.Set(fmt.Sprintf("client-%s", client.Code), string(content), 0).Result()
log.Debugf("security.saveClient(): %s", cmd_val)
return

}

func (s *Security) findSession(key string) (*sess.Session, error) {

if value, err := s.redisCli.Get(key).Result(); err == nil {
return sess.FromJson([]byte(value))
} else {
return nil, err
}

}

func (s *Security) saveSession(session *sess.Session) (err error) {

content, _ := session.ToJson()
cmd_val, err := s.redisCli.Set(session.Key(), string(content), 0).Result()
log.Debugf("security.saveSession(): %s", cmd_val)
return

}
22 changes: 22 additions & 0 deletions auth/utils/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package utils

import (
"math/rand"
"strconv"
"strings"
"time"
)

func init() {
rand.Seed(time.Now().Unix())
}

func RandomNumberString(length int) string {

var result strings.Builder
for n := 0; n < length; n++ {
result.WriteString(strconv.Itoa(rand.Intn(9)))
}
return result.String()

}
8 changes: 8 additions & 0 deletions configure/liveconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type JWT struct {
}
type ServerCfg struct {
Level string `mapstructure:"level"`
IsProtected bool `mapstructure:"is_protected"`
ConfigFile string `mapstructure:"config_file"`
FLVArchive bool `mapstructure:"flv_archive"`
FLVDir string `mapstructure:"flv_dir"`
Expand All @@ -52,6 +53,7 @@ type ServerCfg struct {
APIAddr string `mapstructure:"api_addr"`
RedisAddr string `mapstructure:"redis_addr"`
RedisPwd string `mapstructure:"redis_pwd"`
RedisDb int `mapstructure:"redis_db"`
ReadTimeout int `mapstructure:"read_timeout"`
WriteTimeout int `mapstructure:"write_timeout"`
EnableTLSVerify bool `mapstructure:"enable_tls_verify"`
Expand Down Expand Up @@ -184,3 +186,9 @@ func GetStaticPushUrlList(appname string) ([]string, bool) {
}
return nil, false
}

func IsProtected() bool {
c := ServerCfg{}
Config.Unmarshal(&c)
return c.IsProtected
}
12 changes: 12 additions & 0 deletions gck.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#/bin/bash

if [ $# -ne 1 ]
then
CHANNEL="${RANDOM}-${RANDOM}-${RANDOM}"
else
CHANNEL="${1}"
fi

echo ${CHANNEL}
curl "http://localhost:8090/control/get?room=${CHANNEL}"
echo
3 changes: 3 additions & 0 deletions livego.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@

# # API Options
# api_addr: ":8090"
is_protected: true
redis_addr: "localhost:6379"
# redis_db: 10
server:
- appname: live
live: true
Expand Down
1 change: 1 addition & 0 deletions protocol/rtmp/core/conn_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ func (connServer *ConnServer) GetInfo() (app string, name string, url string) {
app = connServer.ConnInfo.App
name = connServer.PublishInfo.Name
url = connServer.ConnInfo.TcUrl + "/" + connServer.PublishInfo.Name
log.Debugf("connServer.GetInfo: [%s]-[%s]-[%s]", app, name, url)
return
}

Expand Down
16 changes: 13 additions & 3 deletions protocol/rtmp/rtmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/gwuhaolin/livego/utils/uid"

"github.com/gwuhaolin/livego/auth"
"github.com/gwuhaolin/livego/av"
"github.com/gwuhaolin/livego/configure"
"github.com/gwuhaolin/livego/container/flv"
Expand Down Expand Up @@ -91,8 +92,8 @@ func (s *Server) Serve(listener net.Listener) (err error) {
return
}
conn := core.NewConn(netconn, 4*1024)
log.Debug("new client, connect remote: ", conn.RemoteAddr().String(),
"local:", conn.LocalAddr().String())
log.Debugf("new client, connect remote: %s local: %s", conn.RemoteAddr().String(),
conn.LocalAddr().String())
go s.handleConn(conn)
}
}
Expand All @@ -111,7 +112,16 @@ func (s *Server) handleConn(conn *core.Conn) error {
return err
}

appname, name, _ := connServer.GetInfo()
appname, name, uri := connServer.GetInfo()

// If is not a publisher we need to validate the credential here......
if !connServer.IsPublisher() && configure.IsProtected() {
if allow, err := auth.Auth.Allow(conn.RemoteAddr().String(), uri); err != nil || !allow {
conn.Close()
log.Error("handleConn auth err: ", allow, err)
return err
}
}

if ret := configure.CheckAppName(appname); !ret {
err := fmt.Errorf("application name=%s is not configured", appname)
Expand Down