Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
7eeb35b
Updated dependencies to fix twistlock issues
MeganBohland Feb 14, 2024
f3de30c
fixing types for xml sanitizer
adl-trey Jul 31, 2024
ebfcf48
adding test for buffer case
adl-trey Jul 31, 2024
bbadaff
adding init_db.sh to ./player
Sep 18, 2024
df3c618
moved init_db.sh to ./
Sep 18, 2024
a16c7e3
Delete init_db.sh
sammy-sandhu Sep 19, 2024
453e48f
replacing hardcoded local db info with env variables
adl-trey Oct 15, 2024
51fd051
Updated .env.example
MeganBohland Apr 14, 2025
b8b8e1a
Update README.md
MeganBohland Apr 14, 2025
8c646b0
Update README.md
MeganBohland Apr 14, 2025
696d5e5
Fixed typo and knex error
MeganBohland Apr 15, 2025
ba8bdae
Update courses.js
sammy-sandhu Apr 22, 2025
eba23af
Update courses.js
sammy-sandhu Apr 22, 2025
36b6a66
Update courses.js, fixed bug
MeganBohland Apr 23, 2025
e55c02f
Added node_modules for player
Apr 24, 2025
cbac626
Update index.js
sammy-sandhu May 14, 2025
893568e
disabling player basic auth
sammy-sandhu May 15, 2025
ff03884
Uncommented keys in index.js
sammy-sandhu May 15, 2025
9553b80
Disabling basic auth
sammy-sandhu May 16, 2025
c523c7e
disabling basic auth
sammy-sandhu May 16, 2025
a4df08e
Reverting disable of basic auth
sammy-sandhu May 21, 2025
95b6242
Reverting disable of basic auth
sammy-sandhu May 21, 2025
909ad74
Reverting index.js file
sammy-sandhu May 21, 2025
5c57b75
updating player to allow alternate auth header
adl-trey May 22, 2025
e4ec624
Update README.md
vbhayden May 22, 2025
05fbfd5
adding strict header check for player
adl-trey Jun 11, 2025
515126a
Merge branch 'main' of https://github.com/adlnet/catapult
adl-trey Jun 11, 2025
c383ff2
Merge pull request #57 from adlnet/Dependency_fixes
MeganBohland Jul 21, 2025
edb58b9
Testing knex package
Jul 31, 2025
38830a8
Downgraded knex to 2.5.1
Jul 31, 2025
c245bb2
Downgraded Knex to 0.95.*
Jul 31, 2025
11662c6
Regenerated a new node_modules.tar.gz
Jul 31, 2025
da7f72b
Upgraded knex back to 3.1.0
Aug 1, 2025
2bb2886
Created node_modules tar in nodejs image
Aug 1, 2025
f3529a8
Upgraded dependencies for VAT tool
Aug 1, 2025
6713fe1
Merge pull request #58 from adlnet/vat-tool-deps
sammy-sandhu Aug 1, 2025
19df438
Upgraded make-dir/brace-expansion
Aug 1, 2025
548bd9e
Merge pull request #59 from adlnet/cmi5-vat-update
sammy-sandhu Aug 1, 2025
cd4207f
Upgraded dependencies that were reverted
Aug 1, 2025
2dd3579
Merge pull request #60 from adlnet/cmi5-vat-update
sammy-sandhu Aug 1, 2025
06d47b5
disabling blanket cors for cts + player
adl-trey Oct 24, 2025
20a76f4
fixing cts db name
adl-trey Oct 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ cts/var
lts/pkg/dist
.DS_Store
.idea
dumb-init
CATAPULT-player.tar.gz
5 changes: 5 additions & 0 deletions cts/.env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
HOSTNAME=localhost

DB_HOST=rdbms
DB_NAME=catapult_cts
DB_USERNAME=catapult
DB_PASSWORD=quartz

PLAYER_ROOT_PATH=/player
PLAYER_BASE_URL=http://player:3398/player
PLAYER_CONTENT_URL=http://localhost/player/content
Expand Down
10 changes: 7 additions & 3 deletions cts/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,13 @@ services:
- ./seeds:/usr/src/app/seeds:ro
environment:
- HOSTNAME
- DATABASE_USER=catapult
- DATABASE_USER_PASSWORD=quartz
- DATABASE_NAME=catapult_cts
- DB_HOST
- DB_NAME
- DB_USERNAME
- DB_PASSWORD
# - DATABASE_USER=catapult
# - DATABASE_USER_PASSWORD=quartz
# - DATABASE_NAME=catapult_cts
- PLAYER_BASE_URL
- PLAYER_KEY
- PLAYER_SECRET
Expand Down
7 changes: 3 additions & 4 deletions cts/service/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ const provision = async () => {
host: process.argv[3],
port: process.argv[2] || 3399,
routes: {
cors: {
credentials: true
},
cors: false,
response: {
emptyStatusCode: 204
}
Expand All @@ -65,7 +63,8 @@ const provision = async () => {
}
};

await waitPort({host: "rdbms", port: 3306});
const DB_HOST = (process.env.DB_HOST || "rdbms");
await waitPort({host: DB_HOST, port: 3306});

const db = await require("./lib/db")();

Expand Down
25 changes: 17 additions & 8 deletions cts/service/knexfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,33 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
const DB_HOST = (process.env.DB_HOST || "rdbms");
const DB_NAME = (process.env.DB_NAME || "catapult_cts");
const DB_USERNAME = (process.env.DB_USERNAME || "catapult");
const DB_PASSWORD = (process.env.DB_PASSWORD || "quartz");

const Hoek = require("@hapi/hoek"),
waitPort = require("wait-port"),
{
MYSQL_HOST: HOST = "rdbms",
// MYSQL_HOST: HOST = "rdbms",
MYSQL_HOST_FILE: HOST_FILE,
DATABASE_USER: USER = "catapult",
// DATABASE_USER: USER = "catapult",
DATABASE_USER_FILE: USER_FILE,
DATABASE_USER_PASSWORD: PASSWORD = "quartz",
// DATABASE_USER_PASSWORD: DB_PASSWORD,
DATABASE_USER_PASSWORD_FILE: PASSWORD_FILE,
DATABASE_NAME: DB = "catapult_player",
// DATABASE_NAME: DB = "catapult_player",
DATABASE_NAME_FILE: DB_FILE,
} = process.env;

module.exports = async () => {
const host = HOST_FILE ? fs.readFileSync(HOST_FILE) : HOST,
user = USER_FILE ? fs.readFileSync(USER_FILE) : USER,
password = PASSWORD_FILE ? fs.readFileSync(PASSWORD_FILE) : PASSWORD,
database = DB_FILE ? fs.readFileSync(DB_FILE) : DB;
// const host = HOST_FILE ? fs.readFileSync(HOST_FILE) : HOST,
// user = USER_FILE ? fs.readFileSync(USER_FILE) : USER,
// password = PASSWORD_FILE ? fs.readFileSync(PASSWORD_FILE) : PASSWORD,
// database = DB_FILE ? fs.readFileSync(DB_FILE) : DB;
const host = HOST_FILE ? fs.readFileSync(HOST_FILE) : DB_HOST,
user = USER_FILE ? fs.readFileSync(USER_FILE) : DB_USERNAME,
password = PASSWORD_FILE ? fs.readFileSync(PASSWORD_FILE) : DB_PASSWORD,
database = DB_FILE ? fs.readFileSync(DB_FILE) : DB_NAME;

await waitPort({host, port: 3306});

Expand Down
2 changes: 1 addition & 1 deletion cts/service/lib/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
limitations under the License.
*/
const Knex = require("knex"),
KnexStringcase = require('knex-stringcase'),
KnexStringcase = require('knex-stringcase').default,
KnexCfg = require("../knexfile");

module.exports = async () => {
Expand Down
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 12 additions & 4 deletions player/.env.example
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
HOSTNAME=localhost
HOST_PORT=63398

DB_HOST=rdbms
DB_NAME=catapult_player
DB_USERNAME=catapult
DB_PASSWORD=quartz

CONTENT_URL=http://localhost:63398/content

PLAYER_ALTERNATE_AUTH_HEADER=
PLAYER_REQUIRE_STRICT_HEADERS=

API_KEY="some API access key"
API_SECRET="an API access secret"
TOKEN_SECRET="some random string"

LRS_ENDPOINT="http://host.docker.internal:8081/20.1.x/lrs/default/"
LRS_USERNAME="dev-tools-xapi"
LRS_PASSWORD="dev-tools-xapi-password"
LRS_XAPI_VERSION=1.0.3
LRS_ENDPOINT="The LRS endpoint"
LRS_USERNAME="LRS username"
LRS_PASSWORD="LRS password"
LRS_XAPI_VERSION="version if needed

## Default is empty, add if necessary for nginx config
PLAYER_API_ROOT=
Expand Down
46 changes: 35 additions & 11 deletions player/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,44 @@ After cloning the repository, create a `.env` file in the `player/` directory. T
* The host port where the service can be accessed

For example:

```
HOST_PORT=63398
CONTENT_URL=http://localhost:63398/content
API_KEY="some API access key"
API_SECRET="an API access secret"
TOKEN_SECRET="some random string"
LRS_ENDPOINT="http://host.docker.internal:8081/20.1.x/lrs/default/"
LRS_USERNAME="dev-tools-xapi"
LRS_PASSWORD="dev-tools-xapi-password"
```
## Tables

| Variable | Purpose | Suggested Value | Required? |
|:---------:|:-------:|:---------------:|:---------:|
| HOSTNAME | The base URL or host address the player can be reached at, such as "localhost" | No recommendation - but example is "localhost" | Yes|
| HOST_PORT | The port the player can be reached at on the home address | No recommendation - but example is "3398" | Yes |
| DB_HOST | The name of the database the player uses | rdbms (This is the default database the player has in it's repo) | Yes|
| DB_NAME | The name of the database being used by the player | catapult_player | Yes |
| DB_USERNAME | The default username for the database | catapult | Yes |
| DB_PASSWORD | The default password for the database | quartz | Yes |
| CONTENT_URL | The content URL is where the player serves up content. It is either the "PLAYER_STANDALONE_LAUNCH_URL_BASE/content" or "HOSTNAME/HOSTPORT/content") | Yes |
| API_KEY | This is the players access key or 'username' | No recommendation - but example is "BasicKey" | Yes |
| API_SECRET | This is the players password | No recommendation - but example is "BasicSecret" | Yes |
| TOKEN_SECRET | This is a secret token for the player when using JWT authentication | No recommendation - but example is "BasicTokenSecret" | Yes |
| LRS_ENDPOINT | The endpoint to reach an LRS if using in conjuction with player | No recommendation | No - only used if using an LRS |
| LRS_USERNAME | The username to login to the LRS if used | No recommendation | No - only used if using an LRS |
| LRS_PASSWORD | The password to login to the LRS if used | No recommendation | No - only used if using an LRS |
| LRS_XAPI_VERSION | The LRS xAPI version ( if needed using an LRS) | No recommendation | No - only used if using an LRS |
| PLAYER_API_ROOT | The player API root, or location of the player to be used in networking with nginx | /cmi5/player | No - only used when using nginx to proxy |
| PLAYER_STANDALONE_LAUNCH_URL_BASE | The 'public friendly' location of the player. This is not the internal docker address, but the address that the player can be reached at from outside the network. | No recommendation - but example is "http://localhost/cmi5/player" | No - only used when using nginx to proxy |
| PLAYER_ALTERNATE_AUTH_HEADER | An optional header to check when processing auth from a client / browser request. This is a rare configuration value and should only be used when when a deployment environment overrides the expected `Authorization` header. The default auth header will still be used if the specified header value was not found in the request. | Empty, but for Platform One we use `cmi5Authorization` | No. |

Then run,

docker-compose up --build -d

To build and run the player service. Once run it will be available at the `HOST_PORT` mapped in the `.env` as above.

## Running in a Docker container/network with NGINX server

When running the player as a service behind a docker network or NGINX server there are a few extra steps to take. The variables PLAYER_API_ROOT and PLAYER_STANDALONE_LAUNCH_URL_BASE need to be used.

Also, be sure to add this to the nginx.conf:
```
location /cmi5/player {
#
proxy_pass https://"docker container name": :docker port"/cmi5/player;
}
```
This is the INTERNAL network address.

11 changes: 8 additions & 3 deletions player/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,17 @@ services:
- LRS_USERNAME
- LRS_PASSWORD
- LRS_XAPI_VERSIONs
- DATABASE_USER=catapult
- DATABASE_USER_PASSWORD=quartz
- DATABASE_NAME=catapult_player
- DB_HOST
- DB_NAME
- DB_USERNAME
- DB_PASSWOD
# - DATABASE_USER=catapult
# - DATABASE_USER_PASSWORD=quartz
# - DATABASE_NAME=catapult_player
- FIRST_TENANT_NAME
- PLAYER_API_ROOT=${PLAYER_ROOT_PATH}
- PLAYER_STANDALONE_LAUNCH_URL_BASE
- PLAYER_REQUIRE_STRICT_HEADERS
- HOST_PORT
rdbms:
image: mysql:8.0.31
Expand Down
102 changes: 102 additions & 0 deletions player/service/auth/basic.modded.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
'use strict';

const Boom = require('@hapi/boom');
const Hoek = require('@hapi/hoek');


const internals = {};


exports.plugin = {
pkg: require('../package.json'),
requirements: {
hapi: '>=20.0.0'
},

register(server, options) {

server.auth.scheme('basic', internals.implementation);
}
};

const alternateAuthHeader = (process.env.PLAYER_ALTERNATE_AUTH_HEADER || "").toLowerCase();
const alternateAuthHeaderInUse = alternateAuthHeader !== "";

internals.implementation = function (server, options) {

Hoek.assert(options, 'Missing basic auth strategy options');
Hoek.assert(typeof options.validate === 'function', 'options.validate must be a valid function in basic scheme');

const settings = Hoek.clone(options);

const scheme = {
authenticate: async function (request, h) {

let authorization = request.headers.authorization;

// Certain deployment environments will replace the client's Authorization
// header prior to its arrival through the proxy layers.
//
// We allow the client to submit that info through a different header to
// by pass those filters, but we'll want to assign it back into the expected
// location for when this request object is passed around later.
//
if (alternateAuthHeaderInUse) {
let alternateAuth = request.headers[alternateAuthHeader];
if (alternateAuth != undefined) {
authorization = alternateAuth;
request.headers["authorization"] = authorization;
}
}

if (!authorization) {
throw Boom.unauthorized(null, 'Basic', settings.unauthorizedAttributes);
}

const parts = authorization.split(/\s+/);

if (parts[0].toLowerCase() !== 'basic') {
throw Boom.unauthorized(null, 'Basic', settings.unauthorizedAttributes);
}

if (parts.length !== 2) {
throw Boom.badRequest('Bad HTTP authentication header format', 'Basic');
}

const credentialsPart = Buffer.from(parts[1], 'base64').toString();
const sep = credentialsPart.indexOf(':');
if (sep === -1) {
throw Boom.badRequest('Bad header internal syntax', 'Basic');
}

const username = credentialsPart.slice(0, sep);
const password = credentialsPart.slice(sep + 1);

if (!username &&
!settings.allowEmptyUsername) {

throw Boom.unauthorized('HTTP authentication header missing username', 'Basic', settings.unauthorizedAttributes);
}

const { isValid, credentials, response } = await settings.validate(request, username, password, h);

if (response !== undefined) {
return h.response(response).takeover();
}

if (!isValid) {
return h.unauthenticated(Boom.unauthorized('Bad username or password', 'Basic', settings.unauthorizedAttributes), credentials ? { credentials } : null);
}

if (!credentials ||
typeof credentials !== 'object') {

throw Boom.badImplementation('Bad credentials object received for Basic auth validation');
}

return h.authenticated({ credentials });
}
};

return scheme;
};
14 changes: 11 additions & 3 deletions player/service/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ const Hapi = require("@hapi/hapi"),
H2o2 = require("@hapi/h2o2"),
Inert = require("@hapi/inert"),
Vision = require("@hapi/vision"),
AuthBasic = require("@hapi/basic"),
// AuthBasic = require("@hapi/basic"),
AuthBasic = require("./auth/basic.modded"),
AuthJwt = require("@hapi/jwt"),
waitPort = require("wait-port"),
{
Expand All @@ -34,17 +35,23 @@ const Hapi = require("@hapi/hapi"),
} = process.env;

const rootPath = (process.env.PLAYER_API_ROOT || "");
const requireStrictHeaders = (process.env.PLAYER_REQUIRE_STRICT_HEADERS || "true") == "true";

console.log("Using Strict Headers: ", requireStrictHeaders);

const provision = async () => {
const server = Hapi.server(
{
host: process.argv[3],
port: process.argv[2] || 3398,
routes: {
cors: true,
cors: false,
response: {
emptyStatusCode: 204
}
},
state: {
strictHeader: requireStrictHeaders
}
}
),
Expand All @@ -65,7 +72,8 @@ const provision = async () => {
}
};

await waitPort({host: "rdbms", port: 3306});
const DB_HOST = (process.env.DB_HOST || "rdbms");
await waitPort({host: DB_HOST, port: 3306});

const db = await require("./lib/db")();

Expand Down
Loading
Loading