adapter.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. // Copyright 2017 The casbin Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package mycasbin
  15. import (
  16. "errors"
  17. "fmt"
  18. "strings"
  19. "gorm.io/driver/mysql"
  20. "gorm.io/gorm"
  21. "github.com/casbin/casbin/v2/model"
  22. "github.com/casbin/casbin/v2/persist"
  23. )
  24. const (
  25. defaultDatabaseName = "casbin"
  26. defaultTableName = "sys_casbin_rule"
  27. )
  28. type customTableKey struct{}
  29. type CasbinRule struct {
  30. PType string `gorm:"size:100"`
  31. V0 string `gorm:"size:100"`
  32. V1 string `gorm:"size:100"`
  33. V2 string `gorm:"size:100"`
  34. V3 string `gorm:"size:100"`
  35. V4 string `gorm:"size:100"`
  36. V5 string `gorm:"size:100"`
  37. }
  38. func (CasbinRule) TableName() string {
  39. return "sys_casbin_rule"
  40. }
  41. type Filter struct {
  42. PType []string
  43. V0 []string
  44. V1 []string
  45. V2 []string
  46. V3 []string
  47. V4 []string
  48. V5 []string
  49. }
  50. // Adapter represents the Gorm adapter for policy storage.
  51. type Adapter struct {
  52. dataSourceName string
  53. databaseName string
  54. tablePrefix string
  55. tableName string
  56. db *gorm.DB
  57. isFiltered bool
  58. }
  59. // NewAdapterByDBUseTableName creates gorm-adapter by an existing Gorm instance and the specified table prefix and table name
  60. // Example: gormadapter.NewAdapterByDBUseTableName(&db, "cms", "casbin") Automatically generate table name like this "cms_casbin"
  61. func NewAdapterByDBUseTableName(db *gorm.DB, prefix string, tableName string) (*Adapter, error) {
  62. if len(tableName) == 0 {
  63. tableName = defaultTableName
  64. }
  65. a := &Adapter{
  66. tablePrefix: prefix,
  67. tableName: tableName,
  68. }
  69. a.db = db.Scopes(a.casbinRuleTable()).Session(&gorm.Session{Context: db.Statement.Context})
  70. err := a.createTable()
  71. if err != nil {
  72. return nil, err
  73. }
  74. return a, nil
  75. }
  76. // NewAdapterByDB creates gorm-adapter by an existing Gorm instance
  77. func NewAdapterByDB(db *gorm.DB) (*Adapter, error) {
  78. return NewAdapterByDBUseTableName(db, "", defaultTableName)
  79. }
  80. func openDBConnection(dataSourceName string) (*gorm.DB, error) {
  81. return gorm.Open(mysql.Open(dataSourceName), &gorm.Config{})
  82. }
  83. func (a *Adapter) open() error {
  84. var err error
  85. var db *gorm.DB
  86. db, err = openDBConnection(a.dataSourceName)
  87. if err != nil {
  88. return err
  89. }
  90. a.db = db.Scopes(a.casbinRuleTable()).Session(&gorm.Session{})
  91. return a.createTable()
  92. }
  93. func (a *Adapter) close() error {
  94. a.db = nil
  95. return nil
  96. }
  97. // getTableInstance return the dynamic table name
  98. func (a *Adapter) getTableInstance() *CasbinRule {
  99. return &CasbinRule{}
  100. }
  101. func (a *Adapter) getFullTableName() string {
  102. if a.tablePrefix != "" {
  103. return a.tablePrefix + "_" + a.tableName
  104. }
  105. return a.tableName
  106. }
  107. func (a *Adapter) casbinRuleTable() func(db *gorm.DB) *gorm.DB {
  108. return func(db *gorm.DB) *gorm.DB {
  109. tableName := a.getFullTableName()
  110. return db.Table(tableName)
  111. }
  112. }
  113. func (a *Adapter) createTable() error {
  114. t := a.db.Statement.Context.Value(customTableKey{})
  115. if t == nil {
  116. t = a.getTableInstance()
  117. }
  118. if err := a.db.AutoMigrate(t); err != nil {
  119. return err
  120. }
  121. tableName := a.getFullTableName()
  122. index := "idx_" + tableName
  123. hasIndex := a.db.Migrator().HasIndex(t, index)
  124. if !hasIndex {
  125. if err := a.db.Exec(fmt.Sprintf("CREATE UNIQUE INDEX %s ON %s (p_type,v0,v1,v2,v3,v4,v5)", index, tableName)).Error; err != nil {
  126. return err
  127. }
  128. }
  129. return nil
  130. }
  131. func (a *Adapter) dropTable() error {
  132. t := a.db.Statement.Context.Value(customTableKey{})
  133. if t == nil {
  134. return a.db.Migrator().DropTable(a.getTableInstance())
  135. }
  136. return a.db.Migrator().DropTable(t)
  137. }
  138. func loadPolicyLine(line CasbinRule, model model.Model) {
  139. var p = []string{line.PType,
  140. line.V0, line.V1, line.V2, line.V3, line.V4, line.V5}
  141. var lineText string
  142. if line.V5 != "" {
  143. lineText = strings.Join(p, ", ")
  144. } else if line.V4 != "" {
  145. lineText = strings.Join(p[:6], ", ")
  146. } else if line.V3 != "" {
  147. lineText = strings.Join(p[:5], ", ")
  148. } else if line.V2 != "" {
  149. lineText = strings.Join(p[:4], ", ")
  150. } else if line.V1 != "" {
  151. lineText = strings.Join(p[:3], ", ")
  152. } else if line.V0 != "" {
  153. lineText = strings.Join(p[:2], ", ")
  154. }
  155. persist.LoadPolicyLine(lineText, model)
  156. }
  157. // LoadPolicy loads policy from database.
  158. func (a *Adapter) LoadPolicy(model model.Model) error {
  159. var lines []CasbinRule
  160. if err := a.db.Find(&lines).Error; err != nil {
  161. return err
  162. }
  163. for _, line := range lines {
  164. loadPolicyLine(line, model)
  165. }
  166. return nil
  167. }
  168. // LoadFilteredPolicy loads only policy rules that match the filter.
  169. func (a *Adapter) LoadFilteredPolicy(model model.Model, filter interface{}) error {
  170. var lines []CasbinRule
  171. filterValue, ok := filter.(Filter)
  172. if !ok {
  173. return errors.New("invalid filter type")
  174. }
  175. if err := a.db.Scopes(a.filterQuery(a.db, filterValue)).Find(&lines).Error; err != nil {
  176. return err
  177. }
  178. for _, line := range lines {
  179. loadPolicyLine(line, model)
  180. }
  181. a.isFiltered = true
  182. return nil
  183. }
  184. // IsFiltered returns true if the loaded policy has been filtered.
  185. func (a *Adapter) IsFiltered() bool {
  186. return a.isFiltered
  187. }
  188. // filterQuery builds the gorm query to match the rule filter to use within a scope.
  189. func (a *Adapter) filterQuery(db *gorm.DB, filter Filter) func(db *gorm.DB) *gorm.DB {
  190. return func(db *gorm.DB) *gorm.DB {
  191. if len(filter.PType) > 0 {
  192. db = db.Where("p_type in (?)", filter.PType)
  193. }
  194. if len(filter.V0) > 0 {
  195. db = db.Where("v0 in (?)", filter.V0)
  196. }
  197. if len(filter.V1) > 0 {
  198. db = db.Where("v1 in (?)", filter.V1)
  199. }
  200. if len(filter.V2) > 0 {
  201. db = db.Where("v2 in (?)", filter.V2)
  202. }
  203. if len(filter.V3) > 0 {
  204. db = db.Where("v3 in (?)", filter.V3)
  205. }
  206. if len(filter.V4) > 0 {
  207. db = db.Where("v4 in (?)", filter.V4)
  208. }
  209. if len(filter.V5) > 0 {
  210. db = db.Where("v5 in (?)", filter.V5)
  211. }
  212. return db
  213. }
  214. }
  215. func (a *Adapter) savePolicyLine(ptype string, rule []string) CasbinRule {
  216. line := a.getTableInstance()
  217. line.PType = ptype
  218. if len(rule) > 0 {
  219. line.V0 = rule[0]
  220. }
  221. if len(rule) > 1 {
  222. line.V1 = rule[1]
  223. }
  224. if len(rule) > 2 {
  225. line.V2 = rule[2]
  226. }
  227. if len(rule) > 3 {
  228. line.V3 = rule[3]
  229. }
  230. if len(rule) > 4 {
  231. line.V4 = rule[4]
  232. }
  233. if len(rule) > 5 {
  234. line.V5 = rule[5]
  235. }
  236. return *line
  237. }
  238. // SavePolicy saves policy to database.
  239. func (a *Adapter) SavePolicy(model model.Model) error {
  240. err := a.dropTable()
  241. if err != nil {
  242. return err
  243. }
  244. err = a.createTable()
  245. if err != nil {
  246. return err
  247. }
  248. for ptype, ast := range model["p"] {
  249. for _, rule := range ast.Policy {
  250. line := a.savePolicyLine(ptype, rule)
  251. err := a.db.Create(&line).Error
  252. if err != nil {
  253. return err
  254. }
  255. }
  256. }
  257. for ptype, ast := range model["g"] {
  258. for _, rule := range ast.Policy {
  259. line := a.savePolicyLine(ptype, rule)
  260. err := a.db.Create(&line).Error
  261. if err != nil {
  262. return err
  263. }
  264. }
  265. }
  266. return nil
  267. }
  268. // AddPolicy adds a policy rule to the storage.
  269. func (a *Adapter) AddPolicy(sec string, ptype string, rule []string) error {
  270. line := a.savePolicyLine(ptype, rule)
  271. err := a.db.Create(&line).Error
  272. return err
  273. }
  274. // RemovePolicy removes a policy rule from the storage.
  275. func (a *Adapter) RemovePolicy(sec string, ptype string, rule []string) error {
  276. line := a.savePolicyLine(ptype, rule)
  277. err := a.rawDelete(a.db, line) //can't use db.Delete as we're not using primary key http://jinzhu.me/gorm/crud.html#delete
  278. return err
  279. }
  280. // AddPolicies adds multiple policy rules to the storage.
  281. func (a *Adapter) AddPolicies(sec string, ptype string, rules [][]string) error {
  282. return a.db.Transaction(func(tx *gorm.DB) error {
  283. for _, rule := range rules {
  284. line := a.savePolicyLine(ptype, rule)
  285. if err := tx.Create(&line).Error; err != nil {
  286. return err
  287. }
  288. }
  289. return nil
  290. })
  291. }
  292. // RemovePolicies removes multiple policy rules from the storage.
  293. func (a *Adapter) RemovePolicies(sec string, ptype string, rules [][]string) error {
  294. return a.db.Transaction(func(tx *gorm.DB) error {
  295. for _, rule := range rules {
  296. line := a.savePolicyLine(ptype, rule)
  297. if err := a.rawDelete(tx, line); err != nil { //can't use db.Delete as we're not using primary key http://jinzhu.me/gorm/crud.html#delete
  298. return err
  299. }
  300. }
  301. return nil
  302. })
  303. }
  304. // RemoveFilteredPolicy removes policy rules that match the filter from the storage.
  305. func (a *Adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error {
  306. line := a.getTableInstance()
  307. line.PType = ptype
  308. if fieldIndex <= 0 && 0 < fieldIndex+len(fieldValues) {
  309. line.V0 = fieldValues[0-fieldIndex]
  310. }
  311. if fieldIndex <= 1 && 1 < fieldIndex+len(fieldValues) {
  312. line.V1 = fieldValues[1-fieldIndex]
  313. }
  314. if fieldIndex <= 2 && 2 < fieldIndex+len(fieldValues) {
  315. line.V2 = fieldValues[2-fieldIndex]
  316. }
  317. if fieldIndex <= 3 && 3 < fieldIndex+len(fieldValues) {
  318. line.V3 = fieldValues[3-fieldIndex]
  319. }
  320. if fieldIndex <= 4 && 4 < fieldIndex+len(fieldValues) {
  321. line.V4 = fieldValues[4-fieldIndex]
  322. }
  323. if fieldIndex <= 5 && 5 < fieldIndex+len(fieldValues) {
  324. line.V5 = fieldValues[5-fieldIndex]
  325. }
  326. err := a.rawDelete(a.db, *line)
  327. return err
  328. }
  329. func (a *Adapter) rawDelete(db *gorm.DB, line CasbinRule) error {
  330. queryArgs := []interface{}{line.PType}
  331. queryStr := "p_type = ?"
  332. if line.V0 != "" {
  333. queryStr += " and v0 = ?"
  334. queryArgs = append(queryArgs, line.V0)
  335. }
  336. if line.V1 != "" {
  337. queryStr += " and v1 = ?"
  338. queryArgs = append(queryArgs, line.V1)
  339. }
  340. if line.V2 != "" {
  341. queryStr += " and v2 = ?"
  342. queryArgs = append(queryArgs, line.V2)
  343. }
  344. if line.V3 != "" {
  345. queryStr += " and v3 = ?"
  346. queryArgs = append(queryArgs, line.V3)
  347. }
  348. if line.V4 != "" {
  349. queryStr += " and v4 = ?"
  350. queryArgs = append(queryArgs, line.V4)
  351. }
  352. if line.V5 != "" {
  353. queryStr += " and v5 = ?"
  354. queryArgs = append(queryArgs, line.V5)
  355. }
  356. args := append([]interface{}{queryStr}, queryArgs...)
  357. err := db.Delete(a.getTableInstance(), args...).Error
  358. return err
  359. }
  360. func appendWhere(line CasbinRule) (string, []interface{}) {
  361. queryArgs := []interface{}{line.PType}
  362. queryStr := "p_type = ?"
  363. if line.V0 != "" {
  364. queryStr += " and v0 = ?"
  365. queryArgs = append(queryArgs, line.V0)
  366. }
  367. if line.V1 != "" {
  368. queryStr += " and v1 = ?"
  369. queryArgs = append(queryArgs, line.V1)
  370. }
  371. if line.V2 != "" {
  372. queryStr += " and v2 = ?"
  373. queryArgs = append(queryArgs, line.V2)
  374. }
  375. if line.V3 != "" {
  376. queryStr += " and v3 = ?"
  377. queryArgs = append(queryArgs, line.V3)
  378. }
  379. if line.V4 != "" {
  380. queryStr += " and v4 = ?"
  381. queryArgs = append(queryArgs, line.V4)
  382. }
  383. if line.V5 != "" {
  384. queryStr += " and v5 = ?"
  385. queryArgs = append(queryArgs, line.V5)
  386. }
  387. return queryStr, queryArgs
  388. }
  389. // UpdatePolicy updates a new policy rule to DB.
  390. func (a *Adapter) UpdatePolicy(sec string, ptype string, oldRule, newPolicy []string) error {
  391. oldLine := a.savePolicyLine(ptype, oldRule)
  392. queryStr, queryArgs := appendWhere(oldLine)
  393. newLine := a.savePolicyLine(ptype, newPolicy)
  394. err := a.db.Where(queryStr, queryArgs...).Updates(newLine).Error
  395. return err
  396. }